From 04dc563825958f82c56afc13dd9d540b41baaa4e Mon Sep 17 00:00:00 2001 From: bthomee Date: Mon, 16 Feb 2026 11:34:23 -0800 Subject: [PATCH] deploy: 958d8f375453d80bb1aa4c293b5102c045a3e4b4 --- AMMBid_8cpp_source.html | 4 +- AMMCalc__test_8cpp_source.html | 2 +- AMMClawback__test_8cpp_source.html | 2 +- AMMCore_8cpp_source.html | 2 +- AMMCreate_8cpp_source.html | 2 +- AMMDeposit_8cpp_source.html | 2 +- AMMExtended__test_8cpp_source.html | 2 +- AMMHelpers_8cpp_source.html | 2 +- AMMHelpers_8h_source.html | 2 +- AMMInfo_8cpp_source.html | 4 +- AMMInfo__test_8cpp_source.html | 2 +- AMMTest_8cpp_source.html | 4 +- AMMUtils_8cpp_source.html | 4 +- AMMUtils_8h_source.html | 2 +- AMMVote_8cpp_source.html | 2 +- AMMWithdraw_8cpp_source.html | 2 +- AMM_8cpp_source.html | 8 +- AMM_8h_source.html | 2 +- AMM__test_8cpp_source.html | 10727 ++++++++-------- AcceptedLedgerTx_8cpp_source.html | 2 +- AccountChannels_8cpp_source.html | 2 +- AccountCurrenciesHandler_8cpp_source.html | 2 +- AccountCurrencies__test_8cpp_source.html | 2 +- AccountDelete__test_8cpp_source.html | 4 +- AccountInfo_8cpp_source.html | 2 +- AccountInfo__test_8cpp_source.html | 6 +- AccountLines_8cpp_source.html | 2 +- AccountLines__test_8cpp_source.html | 2 +- AccountObjects_8cpp_source.html | 4 +- AccountObjects__test_8cpp_source.html | 1107 +- AccountOffers__test_8cpp_source.html | 8 +- AccountTx_8cpp_source.html | 4 +- AccountTx__test_8cpp_source.html | 4 +- AmendmentTable_8cpp_source.html | 6 +- AmountConversions_8h_source.html | 6 +- ApiVersion_8h_source.html | 4 +- Application_8cpp_source.html | 3732 +++--- Application_8h_source.html | 4 +- ApplyContext_8cpp_source.html | 12 +- ApplyContext_8h_source.html | 77 +- ApplyStateTable_8cpp_source.html | 4 +- ApplyView_8cpp_source.html | 10 +- Asset_8cpp_source.html | 2 +- BaseHTTPPeer_8h_source.html | 4 +- BaseWSPeer_8h_source.html | 611 +- Batch_8cpp_source.html | 2 +- Batch__test_8cpp_source.html | 6 +- BlackList_8cpp_source.html | 2 +- BookChanges_8h_source.html | 2 +- BookChanges__test_8cpp_source.html | 2 +- BookDirs_8h_source.html | 2 +- BookDirs__test_8cpp_source.html | 2 +- BookOffers_8cpp_source.html | 6 +- Book__test_8cpp_source.html | 4 +- CanDelete_8cpp_source.html | 6 +- CancelCheck_8cpp_source.html | 4 +- CancelOffer_8cpp_source.html | 2 +- CanonicalTXSet_8cpp_source.html | 75 +- CanonicalTXSet_8h_source.html | 2 +- CashCheck_8cpp_source.html | 8 +- Check__test_8cpp_source.html | 4 +- Clawback_8cpp_source.html | 8 +- Config__test_8cpp_source.html | 819 +- ConnectAttempt_8cpp_source.html | 782 +- ConnectAttempt_8h_source.html | 14 +- Connect_8cpp_source.html | 2 +- Consensus_8h_source.html | 2 +- CreateCheck_8cpp_source.html | 4 +- CreateOffer_8cpp_source.html | 8 +- CredentialHelpers_8cpp_source.html | 8 +- Credentials_8cpp_source.html | 2 +- Credentials__test_8cpp_source.html | 4 +- CrossingLimits__test_8cpp_source.html | 2 +- DID_8cpp_source.html | 6 +- DID__test_8cpp_source.html | 2 +- Database_8cpp_source.html | 2 +- DelegateSet_8cpp_source.html | 2 +- DelegateUtils_8cpp_source.html | 2 +- DelegateUtils_8h_source.html | 2 +- Delegate__test_8cpp_source.html | 6 +- DeleteAccount_8cpp_source.html | 6 +- DeleteOracle_8cpp_source.html | 2 +- DeliverMax_8cpp_source.html | 4 +- DeliverMin__test_8cpp_source.html | 2 +- DeliveredAmount__test_8cpp_source.html | 6 +- DepositAuth__test_8cpp_source.html | 8 +- DepositAuthorized_8cpp_source.html | 2 +- DepositAuthorized__test_8cpp_source.html | 6 +- DepositPreauth_8cpp_source.html | 315 +- DepositPreauth_8h_source.html | 4 +- Dir_8cpp_source.html | 4 +- Dir_8h_source.html | 2 +- DirectStep_8cpp_source.html | 4 +- Directory__test_8cpp_source.html | 4 +- Discrepancy__test_8cpp_source.html | 2 +- Env_8cpp_source.html | 10 +- Env__test_8cpp_source.html | 4 +- ErrorCodes_8cpp_source.html | 4 +- EscrowToken__test_8cpp_source.html | 8 +- Escrow_8cpp_source.html | 8 +- Escrow__test_8cpp_source.html | 2 +- Feature1_8cpp_source.html | 4 +- Feature_8cpp_source.html | 2 +- Feature_8h_source.html | 2 +- Feature__test_8cpp_source.html | 6 +- FetchInfo_8cpp_source.html | 2 +- FixNFTokenPageLinks__test_8cpp_source.html | 4 +- Flow__test_8cpp_source.html | 4 +- GRPCHandlers_8h_source.html | 2 +- GRPCServer_8cpp_source.html | 286 +- GRPCServer_8h_source.html | 12 +- GRPCTestClientBase_8h_source.html | 35 +- GatewayBalances_8cpp_source.html | 6 +- GatewayBalances__test_8cpp_source.html | 2 +- GetAggregatePrice_8cpp_source.html | 6 +- GetAggregatePrice__test_8cpp_source.html | 2 +- GetCounts_8cpp_source.html | 2 +- GetCounts__test_8cpp_source.html | 2 +- Handler_8cpp_source.html | 2 +- Handler__test_8cpp_source.html | 2 +- Handlers_8h_source.html | 2 +- HashRouter__test_8cpp_source.html | 2 +- IOUAmount_8cpp_source.html | 4 +- IOUAmount_8h_source.html | 234 +- IOUAmount__test_8cpp_source.html | 6 +- IPEndpoint__test_8cpp_source.html | 6 +- InboundLedger_8cpp_source.html | 4 +- Indexes_8h_source.html | 2 +- InvariantCheck_8cpp_source.html | 6 +- Invariants__test_8cpp_source.html | 4 +- Issue_8cpp_source.html | 8 +- JSONRPCClient_8cpp_source.html | 2 +- JSONRPCUtil_8cpp_source.html | 226 +- JSONRPCUtil_8h_source.html | 2 +- JSONRPC__test_8cpp_source.html | 2 +- JobQueue_8cpp_source.html | 2 +- JsonPropertyStream_8cpp_source.html | 2 +- KeyGeneration__test_8cpp_source.html | 4 +- LPTokenTransfer__test_8cpp_source.html | 2 +- LedgerCleaner_8cpp_source.html | 2 +- LedgerData_8cpp_source.html | 6 +- LedgerData__test_8cpp_source.html | 4 +- LedgerEntryHelpers_8h_source.html | 10 +- LedgerEntry_8cpp_source.html | 1546 +-- LedgerEntry__test_8cpp_source.html | 4359 +++---- LedgerHandler_8cpp_source.html | 4 +- LedgerHistory_8cpp_source.html | 2 +- LedgerHistory__test_8cpp_source.html | 2 +- LedgerMaster_8cpp_source.html | 6 +- LedgerMaster__test_8cpp_source.html | 78 +- LedgerRPC__test_8cpp_source.html | 4 +- LedgerReplayTask_8cpp_source.html | 419 +- LedgerReplayTask_8h_source.html | 20 +- LedgerReplay__test_8cpp_source.html | 2 +- LedgerRequest__test_8cpp_source.html | 2 +- LedgerStateFix_8cpp_source.html | 2 +- LedgerToJson_8cpp_source.html | 4 +- LedgerTrie_8h_source.html | 2 +- Ledger_8cpp_source.html | 2 +- LendingHelpers_8cpp_source.html | 3011 ++--- LendingHelpers_8h_source.html | 28 +- LendingHelpers__test_8cpp_source.html | 6 +- LoanBrokerCoverClawback_8cpp_source.html | 4 +- LoanBrokerCoverDeposit_8cpp_source.html | 2 +- LoanBrokerCoverWithdraw_8cpp_source.html | 4 +- LoanBrokerDelete_8cpp_source.html | 2 +- LoanBroker__test_8cpp_source.html | 8 +- LoanDelete_8cpp_source.html | 2 +- LoanManage_8cpp_source.html | 4 +- LoanPay_8cpp_source.html | 4 +- LoanSet_8cpp_source.html | 10 +- Loan__test_8cpp_source.html | 32 +- LogLevel_8cpp_source.html | 2 +- MPTAmount_8cpp_source.html | 2 +- MPTAmount_8h_source.html | 109 +- MPTIssue_8cpp_source.html | 6 +- MPTokenIssuanceSet_8cpp_source.html | 2 +- MPToken__test_8cpp_source.html | 22 +- Main_8cpp_source.html | 2 +- Manifest_8cpp_source.html | 73 +- Manifest_8h_source.html | 2 +- Manifest__test_8cpp_source.html | 1283 +- MultiApiJson_8h_source.html | 4 +- MultiSign__test_8cpp_source.html | 8 +- NFTOffers_8cpp_source.html | 4 +- NFTokenAcceptOffer_8cpp_source.html | 2 +- NFTokenBurn_8cpp_source.html | 4 +- NFTokenBurn__test_8cpp_source.html | 8 +- NFTokenCancelOffer_8cpp_source.html | 4 +- NFTokenCreateOffer_8cpp_source.html | 2 +- NFTokenDir__test_8cpp_source.html | 8 +- NFTokenMint_8cpp_source.html | 4 +- NFTokenModify_8cpp_source.html | 2 +- NFTokenUtils_8cpp_source.html | 2 +- NFTokenUtils_8h_source.html | 2 +- NFToken__test_8cpp_source.html | 8 +- NetworkID__test_8cpp_source.html | 2 +- NetworkOPs_8cpp_source.html | 20 +- NoRippleCheck_8cpp_source.html | 4 +- NoRipple__test_8cpp_source.html | 2 +- NodeFamily_8cpp_source.html | 168 +- NodeFamily_8h_source.html | 8 +- Node_8cpp_source.html | 1195 +- Node_8h_source.html | 16 +- Number_8cpp_source.html | 643 +- Number_8h_source.html | 14 +- Number__test_8cpp_source.html | 8 +- Offer__test_8cpp_source.html | 6 +- OpenLedger_8cpp_source.html | 6 +- Oracle_8cpp_source.html | 4 +- Oracle__test_8cpp_source.html | 1398 +- OverlayImpl_8cpp_source.html | 3037 ++--- OverlayImpl_8h_source.html | 859 +- OversizeMeta__test_8cpp_source.html | 2 +- OwnerInfo_8cpp_source.html | 2 +- OwnerInfo__test_8cpp_source.html | 4 +- PathFind_8cpp_source.html | 4 +- PathRequest_8cpp_source.html | 6 +- Path__test_8cpp_source.html | 8 +- PayChan_8cpp_source.html | 4 +- PayChan__test_8cpp_source.html | 6 +- PaySteps_8cpp_source.html | 6 +- PayStrand__test_8cpp_source.html | 2 +- PaymentSandbox__test_8cpp_source.html | 4 +- Payment_8cpp_source.html | 6 +- PeerImp_8cpp_source.html | 5705 ++++---- PeerImp_8h_source.html | 74 +- PeerReservationTable_8cpp_source.html | 6 +- PerfLogImp_8cpp_source.html | 2 +- PerfLog__test_8cpp_source.html | 6 +- PermissionedDEX__test_8cpp_source.html | 2 +- PermissionedDomainDelete_8cpp_source.html | 4 +- PermissionedDomainSet_8cpp_source.html | 4 +- PermissionedDomains__test_8cpp_source.html | 2 +- Print_8cpp_source.html | 6 +- RPCCall_8cpp_source.html | 16 +- RPCErr_8cpp_source.html | 4 +- RPCHelpers_8cpp_source.html | 6 +- RPCLedgerHelpers_8cpp_source.html | 197 +- RPCOverload__test_8cpp_source.html | 2 +- ReducedOffer__test_8cpp_source.html | 4 +- Regression__test_8cpp_source.html | 4 +- Reservations_8cpp_source.html | 2 +- RipplePathFind_8cpp_source.html | 4 +- RobustTransaction__test_8cpp_source.html | 6 +- Role_8cpp_source.html | 2 +- Roles__test_8cpp_source.html | 2 +- SHAMapDelta_8cpp_source.html | 113 +- SHAMapStore__test_8cpp_source.html | 2 +- SHAMap__test_8cpp_source.html | 378 +- SQLiteDatabase_8cpp_source.html | 16 +- STAmount_8cpp_source.html | 30 +- STAmount_8h_source.html | 492 +- STArray_8cpp_source.html | 2 +- STBitString_8h_source.html | 31 +- STCurrency_8cpp_source.html | 2 +- STInteger_8h_source.html | 13 +- STNumber_8cpp_source.html | 8 +- STObject_8cpp_source.html | 28 +- STObject_8h_source.html | 2138 +-- STObject__test_8cpp_source.html | 2 +- STParsedJSON_8cpp_source.html | 20 +- STParsedJSON__test_8cpp_source.html | 479 +- STPathSet_8cpp_source.html | 2 +- STTakesAsset_8cpp_source.html | 4 +- STTx_8cpp_source.html | 10 +- STTx__test_8cpp_source.html | 4 +- STVector256_8cpp_source.html | 2 +- STVector256_8h_source.html | 245 +- STXChainBridge_8cpp_source.html | 8 +- ServerDefinitions_8cpp_source.html | 2 +- ServerHandler_8cpp_source.html | 22 +- ServerInfo_8cpp_source.html | 2 +- ServerState_8cpp_source.html | 2 +- ServerStatus__test_8cpp_source.html | 2 +- SetAuth__test_8cpp_source.html | 2 +- SetOracle_8cpp_source.html | 4 +- SetSignerList_8cpp_source.html | 4 +- SetTrust_8cpp_source.html | 4 +- SetTrust__test_8cpp_source.html | 2 +- SignHandler_8cpp_source.html | 2 +- Sign_8cpp_source.html | 2 +- SignerEntries_8cpp_source.html | 2 +- SignerUtils_8h_source.html | 4 +- Simulate_8cpp_source.html | 8 +- Simulate__test_8cpp_source.html | 10 +- SlabAllocator_8h_source.html | 2 +- Status__test_8cpp_source.html | 2 +- StepChecks_8h_source.html | 2 +- Steps_8h_source.html | 2 +- Submit_8cpp_source.html | 2 +- Subscribe_8cpp_source.html | 8 +- Subscribe__test_8cpp_source.html | 4 +- TER_8h_source.html | 400 +- TER__test_8cpp_source.html | 16 +- TestHelpers_8cpp_source.html | 4 +- TestHelpers_8h_source.html | 4 +- TheoreticalQuality__test_8cpp_source.html | 4 +- Ticket__test_8cpp_source.html | 4 +- TransactionEntry_8cpp_source.html | 6 +- TransactionEntry__test_8cpp_source.html | 4 +- TransactionHistory__test_8cpp_source.html | 2 +- TransactionSign_8cpp_source.html | 14 +- Transaction_8cpp_source.html | 2 +- Transaction__ordering__test_8cpp_source.html | 2 +- Transaction__test_8cpp_source.html | 6 +- Transactor_8cpp_source.html | 10 +- Transactor_8h_source.html | 4 +- TrustAndBalance__test_8cpp_source.html | 2 +- TrustLine_8cpp_source.html | 2 +- TxHistory_8cpp_source.html | 4 +- TxMeta_8cpp_source.html | 2 +- TxQ_8cpp_source.html | 8 +- TxQ__test_8cpp_source.html | 2 +- Tx_8cpp_source.html | 2 +- UnlList_8cpp_source.html | 2 +- Unsubscribe_8cpp_source.html | 6 +- ValidationCreate_8cpp_source.html | 2 +- ValidatorList_8cpp_source.html | 759 +- ValidatorList_8h_source.html | 431 +- ValidatorSite_8cpp_source.html | 12 +- Value_8cpp_source.html | 28 +- VaultClawback_8cpp_source.html | 8 +- VaultCreate_8cpp_source.html | 2 +- VaultDelete_8cpp_source.html | 4 +- VaultDeposit_8cpp_source.html | 4 +- VaultInfo_8cpp_source.html | 4 +- VaultWithdraw_8cpp_source.html | 4 +- Vault__test_8cpp_source.html | 8116 ++++++------ View_8cpp_source.html | 16 +- View_8h_source.html | 4 +- View__test_8cpp_source.html | 2 +- WSClient__test_8cpp_source.html | 4 +- WalletPropose_8cpp_source.html | 2 +- Wallet_8cpp_source.html | 287 +- Wallet_8h_source.html | 12 +- XChainAttestations_8cpp_source.html | 6 +- XChainBridge_8cpp_source.html | 14 +- XChain__test_8cpp_source.html | 2 +- amount_8cpp_source.html | 189 +- applySteps_8cpp_source.html | 4 +- applySteps_8h_source.html | 2 +- apply_8cpp_source.html | 12 +- balance_8h_source.html | 2 +- batch_8h_source.html | 2 +- classJson_1_1Value.html | 78 +- classxrpl_1_1Application.html | 2 +- classxrpl_1_1ApplicationImp.html | 146 +- classxrpl_1_1ApplyContext.html | 10 +- classxrpl_1_1BaseWSPeer.html | 36 +- classxrpl_1_1CanCvtToNotTEC.html | 2 +- ...l_1_1CanCvtToNotTEC_3_01TEFcodes_01_4.html | 2 +- ...l_1_1CanCvtToNotTEC_3_01TELcodes_01_4.html | 2 +- ...l_1_1CanCvtToNotTEC_3_01TEMcodes_01_4.html | 2 +- ...l_1_1CanCvtToNotTEC_3_01TERcodes_01_4.html | 2 +- ...l_1_1CanCvtToNotTEC_3_01TEScodes_01_4.html | 2 +- classxrpl_1_1CanCvtToTER.html | 2 +- classxrpl_1_1CanCvtToTER_3_01NotTEC_01_4.html | 2 +- ...xrpl_1_1CanCvtToTER_3_01TECcodes_01_4.html | 2 +- ...xrpl_1_1CanCvtToTER_3_01TEFcodes_01_4.html | 2 +- ...xrpl_1_1CanCvtToTER_3_01TELcodes_01_4.html | 2 +- ...xrpl_1_1CanCvtToTER_3_01TEMcodes_01_4.html | 2 +- ...xrpl_1_1CanCvtToTER_3_01TERcodes_01_4.html | 2 +- ...xrpl_1_1CanCvtToTER_3_01TEScodes_01_4.html | 2 +- classxrpl_1_1CanonicalTXSet.html | 2 +- classxrpl_1_1ConnectAttempt.html | 14 +- classxrpl_1_1DepositPreauth.html | 4 +- classxrpl_1_1GRPCServer.html | 8 +- classxrpl_1_1GRPCServerImpl.html | 4 +- classxrpl_1_1IOUAmount.html | 16 +- classxrpl_1_1LedgerReplayTask.html | 20 +- classxrpl_1_1MPTAmount.html | 4 +- classxrpl_1_1ManifestCache.html | 2 +- classxrpl_1_1NodeFamily.html | 8 +- classxrpl_1_1Number.html | 8 +- classxrpl_1_1NumberSO.html | 8 +- classxrpl_1_1OverlayImpl.html | 132 +- classxrpl_1_1PeerImp.html | 110 +- classxrpl_1_1PeerImp_1_1Metrics.html | 6 +- classxrpl_1_1STAmount.html | 18 +- classxrpl_1_1STBitString.html | 2 +- classxrpl_1_1STLedgerEntry.html | 58 +- classxrpl_1_1STObject.html | 62 +- classxrpl_1_1STObject_1_1FieldErr.html | 2 +- classxrpl_1_1STObject_1_1OptionalProxy.html | 66 +- classxrpl_1_1STObject_1_1Proxy.html | 22 +- classxrpl_1_1STObject_1_1ValueProxy.html | 34 +- classxrpl_1_1STParsedJSON__test.html | 2 +- classxrpl_1_1STTx.html | 58 +- classxrpl_1_1STValidation.html | 58 +- classxrpl_1_1STVector256.html | 30 +- classxrpl_1_1ValidatorList.html | 22 +- classxrpl_1_1Vault__test.html | 30 +- classxrpl_1_1reduce__relay_1_1Slots.html | 52 +- ...sxrpl_1_1test_1_1AccountObjects__test.html | 8 +- ..._1_1test_1_1LedgerEntry__XChain__test.html | 12 +- classxrpl_1_1test_1_1LedgerEntry__test.html | 54 +- classxrpl_1_1test_1_1LedgerMaster__test.html | 4 +- classxrpl_1_1test_1_1Manifest__test.html | 14 +- ..._1TMGetObjectByHash__test_1_1PeerTest.html | 110 +- classxrpl_1_1test_1_1compression__test.html | 22 +- classxrpl_1_1test_1_1jtx_1_1IOU.html | 4 +- classxrpl_1_1test_1_1jtx_1_1MPTTester.html | 54 +- ..._1tx__reduce__relay__test_1_1PeerTest.html | 110 +- ...rpl_1_1tests_1_1SHAMapPathProof__test.html | 4 +- compression__test_8cpp_source.html | 695 +- conceptxrpl_1_1Addable.html | 4 +- conceptxrpl_1_1IsArithmetic.html | 10 +- conceptxrpl_1_1IsArithmeticCompatible.html | 8 +- conceptxrpl_1_1IsArithmeticNumber.html | 4 +- conceptxrpl_1_1IsArithmeticST.html | 8 +- conceptxrpl_1_1IsArithmeticValueUnit.html | 6 +- delegate_8cpp_source.html | 2 +- deposit_8cpp_source.html | 2 +- digest_8cpp_source.html | 135 +- envconfig_8cpp_source.html | 2 +- json__get__or__throw_8h_source.html | 12 +- json__value_8cpp_source.html | 903 +- json__value_8h_source.html | 48 +- json__writer_8cpp_source.html | 6 +- json__writer_8h_source.html | 2 +- jtx__json_8cpp_source.html | 4 +- libxrpl_2json_2Output_8cpp_source.html | 2 +- make__Overlay_8h_source.html | 4 +- ...____w_2rippled_2rippled_2CONTRIBUTING.html | 2 +- mpt_8cpp_source.html | 1104 +- mpt_8h_source.html | 52 +- namespaceJson.html | 2 +- namespaceprotocol.html | 4 +- namespacexrpl.html | 190 +- namespacexrpl_1_1detail.html | 36 +- namespacexrpl_1_1test_1_1jtx.html | 10 +- overlay_2Slot_8h_source.html | 539 +- paths_8cpp_source.html | 2 +- permissioned__dex_8cpp_source.html | 2 +- permissioned__domains_8cpp_source.html | 2 +- reduce__relay__test_8cpp_source.html | 4 +- src_2test_2jtx_2amount_8h_source.html | 8 +- structxrpl_1_1LedgerEntry.html | 8 +- structxrpl_1_1OverlayImpl_1_1Stats.html | 10 +- structxrpl_1_1OverlayImpl_1_1Timer.html | 2 +- ...txrpl_1_1OverlayImpl_1_1TrafficGauges.html | 14 +- structxrpl_1_1STObject_1_1Transform.html | 2 +- ..._1detail_1_1ExtendedPaymentComponents.html | 2 +- ...ctxrpl_1_1detail_1_1PaymentComponents.html | 2 +- structxrpl_1_1openssl__sha256__hasher.html | 6 +- structxrpl_1_1openssl__sha512__hasher.html | 6 +- structxrpl_1_1test_1_1AMM__test.html | 72 +- structxrpl_1_1test_1_1GRPCTestClientBase.html | 6 +- ...test_1_1jtx_1_1oracle_1_1Oracle__test.html | 14 +- token_8cpp_source.html | 2 +- utility_8cpp_source.html | 8 +- xchain__bridge_8cpp_source.html | 2 +- xchain__bridge_8h_source.html | 2 +- 454 files changed, 32219 insertions(+), 32060 deletions(-) diff --git a/AMMBid_8cpp_source.html b/AMMBid_8cpp_source.html index 9dca958983..2c0418d42d 100644 --- a/AMMBid_8cpp_source.html +++ b/AMMBid_8cpp_source.html @@ -472,11 +472,11 @@ $(document).ready(function() { init_codefold(0); });
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
std::uint32_t constexpr AUCTION_SLOT_DISCOUNTED_FEE_FRACTION
Definition AMMCore.h:18
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
-
Number power(Number const &f, unsigned n)
Definition Number.cpp:915
+
Number power(Number const &f, unsigned n)
Definition Number.cpp:916
@ current
This was a new validation and was added.
static std::pair< TER, bool > applyBid(ApplyContext &ctx_, Sandbox &sb, AccountID const &account_, beast::Journal j_)
Definition AMMBid.cpp:150
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2446
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt, bool validZero=false)
Validate the amount.
Definition AMMCore.cpp:67
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2616
@ temBAD_AMM_TOKENS
Definition TER.h:109
diff --git a/AMMCalc__test_8cpp_source.html b/AMMCalc__test_8cpp_source.html index b3f7c15dc4..a7ce75b9a3 100644 --- a/AMMCalc__test_8cpp_source.html +++ b/AMMCalc__test_8cpp_source.html @@ -563,7 +563,7 @@ $(document).ready(function() { init_codefold(0); });
T is_same_v
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ match
diff --git a/AMMClawback__test_8cpp_source.html b/AMMClawback__test_8cpp_source.html index 3e402ba32c..bb52eef223 100644 --- a/AMMClawback__test_8cpp_source.html +++ b/AMMClawback__test_8cpp_source.html @@ -2264,7 +2264,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value ammClawback(Account const &issuer, Account const &holder, Issue const &asset, Issue const &asset2, std::optional< STAmount > const &amount)
Definition AMM.cpp:673
bool expectLedgerEntryRoot(Env &env, Account const &acct, STAmount const &expectedValue)
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
diff --git a/AMMCore_8cpp_source.html b/AMMCore_8cpp_source.html index 1255665905..e824e705d5 100644 --- a/AMMCore_8cpp_source.html +++ b/AMMCore_8cpp_source.html @@ -228,7 +228,7 @@ $(document).ready(function() { init_codefold(0); });
@ temBAD_AMOUNT
Definition TER.h:69
std::optional< std::uint8_t > ammAuctionTimeSlot(std::uint64_t current, STObject const &auctionSlot)
Get time slot of the auction slot.
Definition AMMCore.cpp:77
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
Issue ammLPTIssue(Currency const &cur1, Currency const &cur2, AccountID const &ammAccountID)
Calculate LPT Issue from AMM asset pair.
Definition AMMCore.cpp:37
NotTEC invalidAMMAsset(Issue const &issue, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
Definition AMMCore.cpp:43
@ tesSUCCESS
Definition TER.h:225
diff --git a/AMMCreate_8cpp_source.html b/AMMCreate_8cpp_source.html index ec21e6c968..d308eb3f25 100644 --- a/AMMCreate_8cpp_source.html +++ b/AMMCreate_8cpp_source.html @@ -452,7 +452,7 @@ $(document).ready(function() { init_codefold(0); });
@ Yes
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Issue const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
Definition AMMHelpers.cpp:6
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2446
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:434
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2710
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:194
diff --git a/AMMDeposit_8cpp_source.html b/AMMDeposit_8cpp_source.html index c9468e5496..51e75ba14a 100644 --- a/AMMDeposit_8cpp_source.html +++ b/AMMDeposit_8cpp_source.html @@ -1001,7 +1001,7 @@ $(document).ready(function() { init_codefold(0); });
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2446
std::pair< STAmount, STAmount > adjustAssetInByTokens(Rules const &rules, STAmount const &balance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &tokens, std::uint16_t tfee)
Expected< std::tuple< STAmount, STAmount, STAmount >, TER > ammHolds(ReadView const &view, SLE const &ammSle, std::optional< Issue > const &optIssue1, std::optional< Issue > const &optIssue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool and LP token balances.
Definition AMMUtils.cpp:26
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
constexpr std::uint32_t tfTwoAssetIfEmpty
Definition TxFlags.h:231
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:596
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition AMMCore.h:84
diff --git a/AMMExtended__test_8cpp_source.html b/AMMExtended__test_8cpp_source.html index e39ad28017..0acd8a7fbb 100644 --- a/AMMExtended__test_8cpp_source.html +++ b/AMMExtended__test_8cpp_source.html @@ -3750,7 +3750,7 @@ $(document).ready(function() { init_codefold(0); });
bool same(STPathSet const &st1, Args const &... args)
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
XRPAmount txfee(Env const &env, std::uint16_t n)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
STPathElement allPathElements(AccountID const &a, Issue const &iss)
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
diff --git a/AMMHelpers_8cpp_source.html b/AMMHelpers_8cpp_source.html index 3c848c20ad..d5189e452d 100644 --- a/AMMHelpers_8cpp_source.html +++ b/AMMHelpers_8cpp_source.html @@ -513,7 +513,7 @@ $(document).ready(function() { init_codefold(0); });
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:596
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition AMMCore.h:84
std::optional< Number > solveQuadraticEqSmallest(Number const &a, Number const &b, Number const &c)
Solve quadratic equation to find takerGets or takerPays.
-
Number root2(Number f)
Definition Number.cpp:1010
+
Number root2(Number f)
Definition Number.cpp:1011
STAmount ammAssetIn(STAmount const &asset1Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset deposit given LP Tokens.
std::tuple< STAmount, std::optional< STAmount >, STAmount > adjustAmountsByLPTokens(STAmount const &amountBalance, STAmount const &amount, std::optional< STAmount > const &amount2, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee, IsDeposit isDeposit)
Calls adjustLPTokens() and adjusts deposit or withdraw amounts if the adjusted LP tokens are less tha...
STAmount lpTokensIn(STAmount const &asset1Balance, STAmount const &asset1Withdraw, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's withdraw amount.
diff --git a/AMMHelpers_8h_source.html b/AMMHelpers_8h_source.html index ea61bf2484..0341062e0d 100644 --- a/AMMHelpers_8h_source.html +++ b/AMMHelpers_8h_source.html @@ -636,7 +636,7 @@ $(document).ready(function() { init_codefold(0); });
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:596
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition AMMCore.h:84
std::optional< Number > solveQuadraticEqSmallest(Number const &a, Number const &b, Number const &c)
Solve quadratic equation to find takerGets or takerPays.
-
Number root2(Number f)
Definition Number.cpp:1010
+
Number root2(Number f)
Definition Number.cpp:1011
std::optional< TAmounts< TIn, TOut > > changeSpotPriceQuality(TAmounts< TIn, TOut > const &pool, Quality const &quality, std::uint16_t tfee, Rules const &rules, beast::Journal j)
Generate AMM offer so that either updated Spot Price Quality (SPQ) is equal to LOB quality (in this c...
Definition AMMHelpers.h:281
STAmount ammAssetIn(STAmount const &asset1Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset deposit given LP Tokens.
std::tuple< STAmount, std::optional< STAmount >, STAmount > adjustAmountsByLPTokens(STAmount const &amountBalance, STAmount const &amount, std::optional< STAmount > const &amount2, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee, IsDeposit isDeposit)
Calls adjustLPTokens() and adjusts deposit or withdraw amounts if the adjusted LP tokens are less tha...
diff --git a/AMMInfo_8cpp_source.html b/AMMInfo_8cpp_source.html index ca75a53ead..d970052a93 100644 --- a/AMMInfo_8cpp_source.html +++ b/AMMInfo_8cpp_source.html @@ -304,9 +304,9 @@ $(document).ready(function() { init_codefold(0); });
215} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:300
Definition Expected.h:111
diff --git a/AMMInfo__test_8cpp_source.html b/AMMInfo__test_8cpp_source.html index 45f76e0f56..f6348238f1 100644 --- a/AMMInfo__test_8cpp_source.html +++ b/AMMInfo__test_8cpp_source.html @@ -438,7 +438,7 @@ $(document).ready(function() { init_codefold(0); });
T is_same_v
void fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition AMMTest.cpp:18
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
diff --git a/AMMTest_8cpp_source.html b/AMMTest_8cpp_source.html index 98e8a51598..7fe12e07eb 100644 --- a/AMMTest_8cpp_source.html +++ b/AMMTest_8cpp_source.html @@ -380,7 +380,7 @@ $(document).ready(function() { init_codefold(0); });
275} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
std::string const & arg() const
Return the argument associated with the runner.
Definition suite.h:276
Sets the new scale and restores the old scale when it leaves scope.
Definition Number.h:800
A consumption charge.
Definition Charge.h:10
@@ -430,7 +430,7 @@ $(document).ready(function() { init_codefold(0); });
static constexpr auto apiVersionIfUnspecified
Definition ApiVersion.h:43
Charge const feeReferenceRPC
void fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition AMMTest.cpp:18
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Fund
Definition AMMTest.h:16
@ Gw
diff --git a/AMMUtils_8cpp_source.html b/AMMUtils_8cpp_source.html index 450f6c08de..69f0dcedf5 100644 --- a/AMMUtils_8cpp_source.html +++ b/AMMUtils_8cpp_source.html @@ -583,12 +583,12 @@ $(document).ready(function() { init_codefold(0); });
std::uint16_t constexpr maxDeletableAMMTrustLines
The maximum number of trustlines to delete as part of AMM account deletion cleanup.
Definition Protocol.h:265
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Definition View.cpp:392
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:3036
Expected< bool, TER > isOnlyLiquidityProvider(ReadView const &view, Issue const &ammIssue, AccountID const &lpAccount)
Return true if the Liquidity Provider is the only AMM provider, false otherwise.
Definition AMMUtils.cpp:314
@ issues
Expected< std::tuple< STAmount, STAmount, STAmount >, TER > ammHolds(ReadView const &view, SLE const &ammSle, std::optional< Issue > const &optIssue1, std::optional< Issue > const &optIssue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool and LP token balances.
Definition AMMUtils.cpp:26
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:194
void initializeFeeAuctionVote(ApplyView &view, std::shared_ptr< SLE > &ammSle, AccountID const &account, Issue const &lptIssue, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
Definition AMMUtils.cpp:269
std::pair< STAmount, STAmount > ammPoolHolds(ReadView const &view, AccountID const &ammAccountID, Issue const &issue1, Issue const &issue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool balances.
Definition AMMUtils.cpp:12
diff --git a/AMMUtils_8h_source.html b/AMMUtils_8h_source.html index 02d7ccf389..cf35e5e902 100644 --- a/AMMUtils_8h_source.html +++ b/AMMUtils_8h_source.html @@ -166,7 +166,7 @@ $(document).ready(function() { init_codefold(0); });
Expected< bool, TER > isOnlyLiquidityProvider(ReadView const &view, Issue const &ammIssue, AccountID const &lpAccount)
Return true if the Liquidity Provider is the only AMM provider, false otherwise.
Definition AMMUtils.cpp:314
STLedgerEntry SLE
Expected< std::tuple< STAmount, STAmount, STAmount >, TER > ammHolds(ReadView const &view, SLE const &ammSle, std::optional< Issue > const &optIssue1, std::optional< Issue > const &optIssue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool and LP token balances.
Definition AMMUtils.cpp:26
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void initializeFeeAuctionVote(ApplyView &view, std::shared_ptr< SLE > &ammSle, AccountID const &account, Issue const &lptIssue, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
Definition AMMUtils.cpp:269
std::pair< STAmount, STAmount > ammPoolHolds(ReadView const &view, AccountID const &ammAccountID, Issue const &issue1, Issue const &issue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool balances.
Definition AMMUtils.cpp:12
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
Definition AMMUtils.cpp:139
diff --git a/AMMVote_8cpp_source.html b/AMMVote_8cpp_source.html index e128869cef..fbd9364f31 100644 --- a/AMMVote_8cpp_source.html +++ b/AMMVote_8cpp_source.html @@ -345,7 +345,7 @@ $(document).ready(function() { init_codefold(0); });
std::uint32_t constexpr VOTE_WEIGHT_SCALE_FACTOR
Definition AMMCore.h:24
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
Definition AMMCore.cpp:55
std::uint32_t constexpr AUCTION_SLOT_DISCOUNTED_FEE_FRACTION
Definition AMMCore.h:18
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
std::uint16_t constexpr VOTE_MAX_SLOTS
Definition AMMCore.h:23
@ temBAD_FEE
Definition TER.h:72
@ tecAMM_EMPTY
Definition TER.h:313
diff --git a/AMMWithdraw_8cpp_source.html b/AMMWithdraw_8cpp_source.html index 212cfdefd9..f60f9d691f 100644 --- a/AMMWithdraw_8cpp_source.html +++ b/AMMWithdraw_8cpp_source.html @@ -1056,7 +1056,7 @@ $(document).ready(function() { init_codefold(0); });
static STAmount adjustLPTokensIn(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &lpTokensWithdraw, WithdrawAll withdrawAll)
constexpr std::uint32_t tfWithdrawSubTx
Definition TxFlags.h:232
std::pair< STAmount, STAmount > adjustAssetOutByTokens(Rules const &rules, STAmount const &balance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &tokens, std::uint16_t tfee)
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:596
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2710
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:194
diff --git a/AMM_8cpp_source.html b/AMM_8cpp_source.html index 3ce7c86f0f..cefac841f6 100644 --- a/AMM_8cpp_source.html +++ b/AMM_8cpp_source.html @@ -851,9 +851,9 @@ $(document).ready(function() { init_codefold(0); });
694} // namespace xrpl
T cbegin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
-
std::string toStyledString() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value & append(Value const &value)
Append value to array at the end.
+
std::string toStyledString() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
A currency issued by an account.
Definition Issue.h:13
void setJson(Json::Value &jv) const
Definition Issue.cpp:39
@@ -952,7 +952,7 @@ $(document).ready(function() { init_codefold(0); });
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
constexpr std::uint32_t tfWithdrawSubTx
Definition TxFlags.h:232
Json::Value to_json(Asset const &asset)
Definition Asset.h:121
-
Number root2(Number f)
Definition Number.cpp:1010
+
Number root2(Number f)
Definition Number.cpp:1011
std::pair< STAmount, STAmount > ammPoolHolds(ReadView const &view, AccountID const &ammAccountID, Issue const &issue1, Issue const &issue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool balances.
Definition AMMUtils.cpp:12
constexpr std::uint32_t tfLimitLPToken
Definition TxFlags.h:230
constexpr std::uint32_t tfLPToken
Definition TxFlags.h:224
diff --git a/AMM_8h_source.html b/AMM_8h_source.html index 602bdfa135..781aa24034 100644 --- a/AMM_8h_source.html +++ b/AMM_8h_source.html @@ -533,7 +533,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
std::string toStyledString() const
+
std::string toStyledString() const
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
A currency issued by an account.
Definition Issue.h:13
Definition STAmount.h:30
diff --git a/AMM__test_8cpp_source.html b/AMM__test_8cpp_source.html index 09040eec68..22f12ce655 100644 --- a/AMM__test_8cpp_source.html +++ b/AMM__test_8cpp_source.html @@ -1042,5422 +1042,5443 @@ $(document).ready(function() { init_codefold(0); });
955 // Equal deposit limit, tokens rounded to 0
956 testAMM(
957 [&](AMM& amm, Env& env) {
-
958 amm.deposit(DepositArg{
-
959 .asset1In = STAmount{USD, 1, -15}, .asset2In = XRPAmount{1}, .err = ter(tecAMM_INVALID_TOKENS)});
-
960 },
-
961 {.pool = {{USD(1'000'000), XRP(1'000'000)}}, .features = {features - fixAMMv1_3}});
-
962 testAMM([&](AMM& amm, Env& env) {
-
963 amm.deposit(DepositArg{
-
964 .asset1In = STAmount{USD, 1, -15}, .asset2In = XRPAmount{1}, .err = ter(tecAMM_INVALID_TOKENS)});
-
965 });
-
966
-
967 // Single deposit by asset, tokens rounded to 0
-
968 testAMM([&](AMM& amm, Env& env) {
-
969 amm.deposit(DepositArg{.asset1In = STAmount{USD, 1, -15}, .err = ter(tecAMM_INVALID_TOKENS)});
-
970 });
-
971
-
972 // Single deposit by tokens, tokens rounded to 0
-
973 testAMM([&](AMM& amm, Env& env) {
-
974 amm.deposit(DepositArg{
-
975 .tokens = IOUAmount{1, -10}, .asset1In = STAmount{USD, 1, -15}, .err = ter(tecAMM_INVALID_TOKENS)});
-
976 });
-
977
-
978 // Single deposit with EPrice, tokens rounded to 0
-
979 testAMM([&](AMM& amm, Env& env) {
-
980 amm.deposit(DepositArg{
-
981 .asset1In = STAmount{USD, 1, -15}, .maxEP = STAmount{USD, 1, -1}, .err = ter(tecAMM_INVALID_TOKENS)});
-
982 });
-
983 }
+
958 amm.deposit(
+
959 DepositArg{
+
960 .asset1In = STAmount{USD, 1, -15},
+
961 .asset2In = XRPAmount{1},
+
962 .err = ter(tecAMM_INVALID_TOKENS)});
+
963 },
+
964 {.pool = {{USD(1'000'000), XRP(1'000'000)}}, .features = {features - fixAMMv1_3}});
+
965 testAMM([&](AMM& amm, Env& env) {
+
966 amm.deposit(
+
967 DepositArg{
+
968 .asset1In = STAmount{USD, 1, -15}, .asset2In = XRPAmount{1}, .err = ter(tecAMM_INVALID_TOKENS)});
+
969 });
+
970
+
971 // Single deposit by asset, tokens rounded to 0
+
972 testAMM([&](AMM& amm, Env& env) {
+
973 amm.deposit(DepositArg{.asset1In = STAmount{USD, 1, -15}, .err = ter(tecAMM_INVALID_TOKENS)});
+
974 });
+
975
+
976 // Single deposit by tokens, tokens rounded to 0
+
977 testAMM([&](AMM& amm, Env& env) {
+
978 amm.deposit(
+
979 DepositArg{
+
980 .tokens = IOUAmount{1, -10}, .asset1In = STAmount{USD, 1, -15}, .err = ter(tecAMM_INVALID_TOKENS)});
+
981 });
+
982
+
983 // Single deposit with EPrice, tokens rounded to 0
+
984 testAMM([&](AMM& amm, Env& env) {
+
985 amm.deposit(
+
986 DepositArg{
+
987 .asset1In = STAmount{USD, 1, -15},
+
988 .maxEP = STAmount{USD, 1, -1},
+
989 .err = ter(tecAMM_INVALID_TOKENS)});
+
990 });
+
991 }
-
984
-
985 void
-
- -
987 {
-
988 testcase("Deposit");
-
989
-
990 auto const all = testable_amendments();
-
991 using namespace jtx;
992
-
993 // Equal deposit: 1000000 tokens, 10% of the current pool
-
994 testAMM([&](AMM& ammAlice, Env& env) {
-
995 auto const baseFee = env.current()->fees().base;
-
996 ammAlice.deposit(carol, 1'000'000);
-
997 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
-
998 // 30,000 less deposited 1,000
-
999 BEAST_EXPECT(expectHolding(env, carol, USD(29'000)));
-
1000 // 30,000 less deposited 1,000 and 10 drops tx fee
-
1001 BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRPAmount{29'000'000'000 - baseFee}));
-
1002 });
-
1003
-
1004 // equal asset deposit: unit test to exercise the rounding-down of
-
1005 // LPTokens in the AMMHelpers.cpp: adjustLPTokens calculations
-
1006 // The LPTokens need to have 16 significant digits and a fractional part
-
1007 for (Number const& deltaLPTokens :
-
1008 {Number{UINT64_C(100000'0000000009), -10}, Number{UINT64_C(100000'0000000001), -10}})
-
1009 {
-
1010 testAMM([&](AMM& ammAlice, Env& env) {
-
1011 // initial LPToken balance
-
1012 IOUAmount const initLPToken = ammAlice.getLPTokensBalance();
-
1013 IOUAmount const newLPTokens{deltaLPTokens};
-
1014
-
1015 // carol performs a two-asset deposit
-
1016 ammAlice.deposit(DepositArg{.account = carol, .tokens = newLPTokens});
-
1017
-
1018 IOUAmount const finalLPToken = ammAlice.getLPTokensBalance();
-
1019
-
1020 // Change in behavior due to rounding down of LPTokens:
-
1021 // there is a decrease in the observed return of LPTokens --
-
1022 // Inputs Number{UINT64_C(100000'0000000001), -10} and
-
1023 // Number{UINT64_C(100000'0000000009), -10} are both rounded
-
1024 // down to 1e5
-
1025 BEAST_EXPECT((finalLPToken - initLPToken == IOUAmount{1, 5}));
-
1026 BEAST_EXPECT(finalLPToken - initLPToken < deltaLPTokens);
+
993 void
+
+ +
995 {
+
996 testcase("Deposit");
+
997
+
998 auto const all = testable_amendments();
+
999 using namespace jtx;
+
1000
+
1001 // Equal deposit: 1000000 tokens, 10% of the current pool
+
1002 testAMM([&](AMM& ammAlice, Env& env) {
+
1003 auto const baseFee = env.current()->fees().base;
+
1004 ammAlice.deposit(carol, 1'000'000);
+
1005 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
+
1006 // 30,000 less deposited 1,000
+
1007 BEAST_EXPECT(expectHolding(env, carol, USD(29'000)));
+
1008 // 30,000 less deposited 1,000 and 10 drops tx fee
+
1009 BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRPAmount{29'000'000'000 - baseFee}));
+
1010 });
+
1011
+
1012 // equal asset deposit: unit test to exercise the rounding-down of
+
1013 // LPTokens in the AMMHelpers.cpp: adjustLPTokens calculations
+
1014 // The LPTokens need to have 16 significant digits and a fractional part
+
1015 for (Number const& deltaLPTokens :
+
1016 {Number{UINT64_C(100000'0000000009), -10}, Number{UINT64_C(100000'0000000001), -10}})
+
1017 {
+
1018 testAMM([&](AMM& ammAlice, Env& env) {
+
1019 // initial LPToken balance
+
1020 IOUAmount const initLPToken = ammAlice.getLPTokensBalance();
+
1021 IOUAmount const newLPTokens{deltaLPTokens};
+
1022
+
1023 // carol performs a two-asset deposit
+
1024 ammAlice.deposit(DepositArg{.account = carol, .tokens = newLPTokens});
+
1025
+
1026 IOUAmount const finalLPToken = ammAlice.getLPTokensBalance();
1027
-
1028 // fraction of newLPTokens/(existing LPToken balance). The
-
1029 // existing LPToken balance is 1e7
-
1030 Number const fr = deltaLPTokens / 1e7;
-
1031
-
1032 // The below equations are based on Equation 1, 2 from XLS-30d
-
1033 // specification, Section: 2.3.1.2
-
1034 Number const deltaXRP = fr * 1e10;
-
1035 Number const deltaUSD = fr * 1e4;
-
1036
-
1037 STAmount const depositUSD = STAmount{USD, deltaUSD};
-
1038
-
1039 STAmount const depositXRP = STAmount{XRP, deltaXRP};
-
1040
-
1041 // initial LPTokens (1e7) + newLPTokens
-
1042 BEAST_EXPECT(ammAlice.expectBalances(
-
1043 XRP(10'000) + depositXRP, USD(10'000) + depositUSD, IOUAmount{1, 7} + newLPTokens));
+
1028 // Change in behavior due to rounding down of LPTokens:
+
1029 // there is a decrease in the observed return of LPTokens --
+
1030 // Inputs Number{UINT64_C(100000'0000000001), -10} and
+
1031 // Number{UINT64_C(100000'0000000009), -10} are both rounded
+
1032 // down to 1e5
+
1033 BEAST_EXPECT((finalLPToken - initLPToken == IOUAmount{1, 5}));
+
1034 BEAST_EXPECT(finalLPToken - initLPToken < deltaLPTokens);
+
1035
+
1036 // fraction of newLPTokens/(existing LPToken balance). The
+
1037 // existing LPToken balance is 1e7
+
1038 Number const fr = deltaLPTokens / 1e7;
+
1039
+
1040 // The below equations are based on Equation 1, 2 from XLS-30d
+
1041 // specification, Section: 2.3.1.2
+
1042 Number const deltaXRP = fr * 1e10;
+
1043 Number const deltaUSD = fr * 1e4;
1044
-
1045 // 30,000 less deposited depositUSD
-
1046 BEAST_EXPECT(expectHolding(env, carol, USD(30'000) - depositUSD));
-
1047 // 30,000 less deposited depositXRP and 10 drops tx fee
-
1048 BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRP(30'000) - depositXRP - txfee(env, 1)));
-
1049 });
-
1050 }
-
1051
-
1052 // Equal limit deposit: deposit USD100 and XRP proportionally
-
1053 // to the pool composition not to exceed 100XRP. If the amount
-
1054 // exceeds 100XRP then deposit 100XRP and USD proportionally
-
1055 // to the pool composition not to exceed 100USD. Fail if exceeded.
-
1056 // Deposit 100USD/100XRP
-
1057 testAMM([&](AMM& ammAlice, Env&) {
-
1058 ammAlice.deposit(carol, USD(100), XRP(100));
-
1059 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0}));
-
1060 });
-
1061
-
1062 // Equal limit deposit.
-
1063 // Try to deposit 200USD/100XRP. Is truncated to 100USD/100XRP.
-
1064 testAMM([&](AMM& ammAlice, Env&) {
-
1065 ammAlice.deposit(carol, USD(200), XRP(100));
-
1066 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0}));
-
1067 });
-
1068 // Try to deposit 100USD/200XRP. Is truncated to 100USD/100XRP.
-
1069 testAMM([&](AMM& ammAlice, Env&) {
-
1070 ammAlice.deposit(carol, USD(100), XRP(200));
-
1071 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0}));
-
1072 });
-
1073
-
1074 // Single deposit: 1000 USD
-
1075 testAMM([&](AMM& ammAlice, Env&) {
-
1076 ammAlice.deposit(carol, USD(1'000));
-
1077 BEAST_EXPECT(ammAlice.expectBalances(
-
1078 XRP(10'000), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'488'088'48170151, -8}));
-
1079 });
-
1080
-
1081 // Single deposit: 1000 XRP
-
1082 testAMM([&](AMM& ammAlice, Env&) {
-
1083 ammAlice.deposit(carol, XRP(1'000));
-
1084 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(10'000), IOUAmount{10'488'088'48170151, -8}));
-
1085 });
-
1086
-
1087 // Single deposit: 100000 tokens worth of USD
-
1088 testAMM([&](AMM& ammAlice, Env&) {
-
1089 ammAlice.deposit(carol, 100000, USD(205));
-
1090 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'201), IOUAmount{10'100'000, 0}));
-
1091 });
-
1092
-
1093 // Single deposit: 100000 tokens worth of XRP
-
1094 testAMM([&](AMM& ammAlice, Env& env) {
-
1095 ammAlice.deposit(carol, 100'000, XRP(205));
-
1096 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'201), USD(10'000), IOUAmount{10'100'000, 0}));
-
1097 });
-
1098
-
1099 // Single deposit with EP not exceeding specified:
-
1100 // 100USD with EP not to exceed 0.1 (AssetIn/TokensOut)
-
1101 testAMM([&](AMM& ammAlice, Env&) {
-
1102 ammAlice.deposit(carol, USD(1'000), std::nullopt, STAmount{USD, 1, -1});
-
1103 BEAST_EXPECT(ammAlice.expectBalances(
-
1104 XRP(10'000), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'488'088'48170151, -8}));
+
1045 STAmount const depositUSD = STAmount{USD, deltaUSD};
+
1046
+
1047 STAmount const depositXRP = STAmount{XRP, deltaXRP};
+
1048
+
1049 // initial LPTokens (1e7) + newLPTokens
+
1050 BEAST_EXPECT(ammAlice.expectBalances(
+
1051 XRP(10'000) + depositXRP, USD(10'000) + depositUSD, IOUAmount{1, 7} + newLPTokens));
+
1052
+
1053 // 30,000 less deposited depositUSD
+
1054 BEAST_EXPECT(expectHolding(env, carol, USD(30'000) - depositUSD));
+
1055 // 30,000 less deposited depositXRP and 10 drops tx fee
+
1056 BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRP(30'000) - depositXRP - txfee(env, 1)));
+
1057 });
+
1058 }
+
1059
+
1060 // Equal limit deposit: deposit USD100 and XRP proportionally
+
1061 // to the pool composition not to exceed 100XRP. If the amount
+
1062 // exceeds 100XRP then deposit 100XRP and USD proportionally
+
1063 // to the pool composition not to exceed 100USD. Fail if exceeded.
+
1064 // Deposit 100USD/100XRP
+
1065 testAMM([&](AMM& ammAlice, Env&) {
+
1066 ammAlice.deposit(carol, USD(100), XRP(100));
+
1067 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0}));
+
1068 });
+
1069
+
1070 // Equal limit deposit.
+
1071 // Try to deposit 200USD/100XRP. Is truncated to 100USD/100XRP.
+
1072 testAMM([&](AMM& ammAlice, Env&) {
+
1073 ammAlice.deposit(carol, USD(200), XRP(100));
+
1074 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0}));
+
1075 });
+
1076 // Try to deposit 100USD/200XRP. Is truncated to 100USD/100XRP.
+
1077 testAMM([&](AMM& ammAlice, Env&) {
+
1078 ammAlice.deposit(carol, USD(100), XRP(200));
+
1079 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'100), IOUAmount{10'100'000, 0}));
+
1080 });
+
1081
+
1082 // Single deposit: 1000 USD
+
1083 testAMM([&](AMM& ammAlice, Env&) {
+
1084 ammAlice.deposit(carol, USD(1'000));
+
1085 BEAST_EXPECT(ammAlice.expectBalances(
+
1086 XRP(10'000), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'488'088'48170151, -8}));
+
1087 });
+
1088
+
1089 // Single deposit: 1000 XRP
+
1090 testAMM([&](AMM& ammAlice, Env&) {
+
1091 ammAlice.deposit(carol, XRP(1'000));
+
1092 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(10'000), IOUAmount{10'488'088'48170151, -8}));
+
1093 });
+
1094
+
1095 // Single deposit: 100000 tokens worth of USD
+
1096 testAMM([&](AMM& ammAlice, Env&) {
+
1097 ammAlice.deposit(carol, 100000, USD(205));
+
1098 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'201), IOUAmount{10'100'000, 0}));
+
1099 });
+
1100
+
1101 // Single deposit: 100000 tokens worth of XRP
+
1102 testAMM([&](AMM& ammAlice, Env& env) {
+
1103 ammAlice.deposit(carol, 100'000, XRP(205));
+
1104 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'201), USD(10'000), IOUAmount{10'100'000, 0}));
1105 });
1106
1107 // Single deposit with EP not exceeding specified:
-
1108 // 100USD with EP not to exceed 0.002004 (AssetIn/TokensOut)
+
1108 // 100USD with EP not to exceed 0.1 (AssetIn/TokensOut)
1109 testAMM([&](AMM& ammAlice, Env&) {
-
1110 ammAlice.deposit(carol, USD(100), std::nullopt, STAmount{USD, 2004, -6});
-
1111 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), STAmount{USD, 10'080'16, -2}, IOUAmount{10'040'000, 0}));
-
1112 });
-
1113
-
1114 // Single deposit with EP not exceeding specified:
-
1115 // 0USD with EP not to exceed 0.002004 (AssetIn/TokensOut)
-
1116 testAMM([&](AMM& ammAlice, Env&) {
-
1117 ammAlice.deposit(carol, USD(0), std::nullopt, STAmount{USD, 2004, -6});
-
1118 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), STAmount{USD, 10'080'16, -2}, IOUAmount{10'040'000, 0}));
-
1119 });
-
1120
-
1121 // IOU to IOU + transfer fee
-
1122 {
-
1123 Env env{*this};
-
1124 fund(env, gw, {alice}, {USD(20'000), BTC(0.5)}, Fund::All);
-
1125 env(rate(gw, 1.25));
-
1126 env.close();
-
1127 AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
-
1128 BEAST_EXPECT(ammAlice.expectBalances(USD(20'000), BTC(0.5), IOUAmount{100, 0}));
-
1129 BEAST_EXPECT(expectHolding(env, alice, USD(0)));
-
1130 BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
-
1131 fund(env, gw, {carol}, {USD(2'000), BTC(0.05)}, Fund::Acct);
-
1132 // no transfer fee on deposit
-
1133 ammAlice.deposit(carol, 10);
-
1134 BEAST_EXPECT(ammAlice.expectBalances(USD(22'000), BTC(0.55), IOUAmount{110, 0}));
-
1135 BEAST_EXPECT(expectHolding(env, carol, USD(0)));
-
1136 BEAST_EXPECT(expectHolding(env, carol, BTC(0)));
-
1137 }
-
1138
-
1139 // Tiny deposits
-
1140 testAMM([&](AMM& ammAlice, Env&) {
-
1141 ammAlice.deposit(carol, IOUAmount{1, -3});
-
1142 BEAST_EXPECT(ammAlice.expectBalances(
-
1143 XRPAmount{10'000'000'001}, STAmount{USD, UINT64_C(10'000'000001), -6}, IOUAmount{10'000'000'001, -3}));
-
1144 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1, -3}));
-
1145 });
-
1146 testAMM([&](AMM& ammAlice, Env&) {
-
1147 ammAlice.deposit(carol, XRPAmount{1});
-
1148 BEAST_EXPECT(
-
1149 ammAlice.expectBalances(XRPAmount{10'000'000'001}, USD(10'000), IOUAmount{1'000'000'000049999, -8}));
-
1150 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{49999, -8}));
-
1151 });
-
1152 testAMM([&](AMM& ammAlice, Env&) {
-
1153 ammAlice.deposit(carol, STAmount{USD, 1, -10});
-
1154 BEAST_EXPECT(ammAlice.expectBalances(
-
1155 XRP(10'000), STAmount{USD, UINT64_C(10'000'00000000008), -11}, IOUAmount{10'000'000'00000004, -8}));
-
1156 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{4, -8}));
-
1157 });
-
1158
-
1159 // Issuer create/deposit
-
1160 for (auto const& feat : {all, all - fixAMMv1_3})
-
1161 {
-
1162 Env env(*this, feat);
-
1163 env.fund(XRP(30000), gw);
-
1164 AMM ammGw(env, gw, XRP(10'000), USD(10'000));
-
1165 BEAST_EXPECT(ammGw.expectBalances(XRP(10'000), USD(10'000), ammGw.tokens()));
-
1166 ammGw.deposit(gw, 1'000'000);
-
1167 BEAST_EXPECT(ammGw.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000}));
-
1168 ammGw.deposit(gw, USD(1'000));
-
1169 BEAST_EXPECT(ammGw.expectBalances(
-
1170 XRP(11'000), STAmount{USD, UINT64_C(11'999'99999999998), -11}, IOUAmount{11'489'125'29307605, -8}));
-
1171 }
-
1172
-
1173 // Issuer deposit
-
1174 testAMM([&](AMM& ammAlice, Env& env) {
-
1175 ammAlice.deposit(gw, 1'000'000);
-
1176 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000}));
-
1177 ammAlice.deposit(gw, USD(1'000));
-
1178 BEAST_EXPECT(ammAlice.expectBalances(
-
1179 XRP(11'000), STAmount{USD, UINT64_C(11'999'99999999998), -11}, IOUAmount{11'489'125'29307605, -8}));
-
1180 });
-
1181
-
1182 // Min deposit
-
1183 testAMM([&](AMM& ammAlice, Env& env) {
-
1184 // Equal deposit by tokens
-
1185 ammAlice.deposit(
-
1186 carol, 1'000'000, XRP(1'000), USD(1'000), std::nullopt, tfLPToken, std::nullopt, std::nullopt);
-
1187 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
+
1110 ammAlice.deposit(carol, USD(1'000), std::nullopt, STAmount{USD, 1, -1});
+
1111 BEAST_EXPECT(ammAlice.expectBalances(
+
1112 XRP(10'000), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'488'088'48170151, -8}));
+
1113 });
+
1114
+
1115 // Single deposit with EP not exceeding specified:
+
1116 // 100USD with EP not to exceed 0.002004 (AssetIn/TokensOut)
+
1117 testAMM([&](AMM& ammAlice, Env&) {
+
1118 ammAlice.deposit(carol, USD(100), std::nullopt, STAmount{USD, 2004, -6});
+
1119 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), STAmount{USD, 10'080'16, -2}, IOUAmount{10'040'000, 0}));
+
1120 });
+
1121
+
1122 // Single deposit with EP not exceeding specified:
+
1123 // 0USD with EP not to exceed 0.002004 (AssetIn/TokensOut)
+
1124 testAMM([&](AMM& ammAlice, Env&) {
+
1125 ammAlice.deposit(carol, USD(0), std::nullopt, STAmount{USD, 2004, -6});
+
1126 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), STAmount{USD, 10'080'16, -2}, IOUAmount{10'040'000, 0}));
+
1127 });
+
1128
+
1129 // IOU to IOU + transfer fee
+
1130 {
+
1131 Env env{*this};
+
1132 fund(env, gw, {alice}, {USD(20'000), BTC(0.5)}, Fund::All);
+
1133 env(rate(gw, 1.25));
+
1134 env.close();
+
1135 AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
+
1136 BEAST_EXPECT(ammAlice.expectBalances(USD(20'000), BTC(0.5), IOUAmount{100, 0}));
+
1137 BEAST_EXPECT(expectHolding(env, alice, USD(0)));
+
1138 BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
+
1139 fund(env, gw, {carol}, {USD(2'000), BTC(0.05)}, Fund::Acct);
+
1140 // no transfer fee on deposit
+
1141 ammAlice.deposit(carol, 10);
+
1142 BEAST_EXPECT(ammAlice.expectBalances(USD(22'000), BTC(0.55), IOUAmount{110, 0}));
+
1143 BEAST_EXPECT(expectHolding(env, carol, USD(0)));
+
1144 BEAST_EXPECT(expectHolding(env, carol, BTC(0)));
+
1145 }
+
1146
+
1147 // Tiny deposits
+
1148 testAMM([&](AMM& ammAlice, Env&) {
+
1149 ammAlice.deposit(carol, IOUAmount{1, -3});
+
1150 BEAST_EXPECT(ammAlice.expectBalances(
+
1151 XRPAmount{10'000'000'001}, STAmount{USD, UINT64_C(10'000'000001), -6}, IOUAmount{10'000'000'001, -3}));
+
1152 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1, -3}));
+
1153 });
+
1154 testAMM([&](AMM& ammAlice, Env&) {
+
1155 ammAlice.deposit(carol, XRPAmount{1});
+
1156 BEAST_EXPECT(
+
1157 ammAlice.expectBalances(XRPAmount{10'000'000'001}, USD(10'000), IOUAmount{1'000'000'000049999, -8}));
+
1158 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{49999, -8}));
+
1159 });
+
1160 testAMM([&](AMM& ammAlice, Env&) {
+
1161 ammAlice.deposit(carol, STAmount{USD, 1, -10});
+
1162 BEAST_EXPECT(ammAlice.expectBalances(
+
1163 XRP(10'000), STAmount{USD, UINT64_C(10'000'00000000008), -11}, IOUAmount{10'000'000'00000004, -8}));
+
1164 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{4, -8}));
+
1165 });
+
1166
+
1167 // Issuer create/deposit
+
1168 for (auto const& feat : {all, all - fixAMMv1_3})
+
1169 {
+
1170 Env env(*this, feat);
+
1171 env.fund(XRP(30000), gw);
+
1172 AMM ammGw(env, gw, XRP(10'000), USD(10'000));
+
1173 BEAST_EXPECT(ammGw.expectBalances(XRP(10'000), USD(10'000), ammGw.tokens()));
+
1174 ammGw.deposit(gw, 1'000'000);
+
1175 BEAST_EXPECT(ammGw.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000}));
+
1176 ammGw.deposit(gw, USD(1'000));
+
1177 BEAST_EXPECT(ammGw.expectBalances(
+
1178 XRP(11'000), STAmount{USD, UINT64_C(11'999'99999999998), -11}, IOUAmount{11'489'125'29307605, -8}));
+
1179 }
+
1180
+
1181 // Issuer deposit
+
1182 testAMM([&](AMM& ammAlice, Env& env) {
+
1183 ammAlice.deposit(gw, 1'000'000);
+
1184 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000}));
+
1185 ammAlice.deposit(gw, USD(1'000));
+
1186 BEAST_EXPECT(ammAlice.expectBalances(
+
1187 XRP(11'000), STAmount{USD, UINT64_C(11'999'99999999998), -11}, IOUAmount{11'489'125'29307605, -8}));
1188 });
-
1189 testAMM([&](AMM& ammAlice, Env& env) {
-
1190 // Equal deposit by asset
-
1191 ammAlice.deposit(
-
1192 carol, 1'000'000, XRP(1'000), USD(1'000), std::nullopt, tfTwoAsset, std::nullopt, std::nullopt);
-
1193 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
-
1194 });
-
1195 testAMM([&](AMM& ammAlice, Env& env) {
-
1196 // Single deposit by asset
-
1197 ammAlice.deposit(
- -
1199 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(10'000), IOUAmount{10'488'088'48170151, -8}));
-
1200 });
-
1201 testAMM([&](AMM& ammAlice, Env& env) {
-
1202 // Single deposit by asset
-
1203 ammAlice.deposit(
- -
1205 BEAST_EXPECT(ammAlice.expectBalances(
-
1206 XRP(10'000), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'488'088'48170151, -8}));
-
1207 });
-
1208 }
+
1189
+
1190 // Min deposit
+
1191 testAMM([&](AMM& ammAlice, Env& env) {
+
1192 // Equal deposit by tokens
+
1193 ammAlice.deposit(
+
1194 carol, 1'000'000, XRP(1'000), USD(1'000), std::nullopt, tfLPToken, std::nullopt, std::nullopt);
+
1195 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
+
1196 });
+
1197 testAMM([&](AMM& ammAlice, Env& env) {
+
1198 // Equal deposit by asset
+
1199 ammAlice.deposit(
+
1200 carol, 1'000'000, XRP(1'000), USD(1'000), std::nullopt, tfTwoAsset, std::nullopt, std::nullopt);
+
1201 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
+
1202 });
+
1203 testAMM([&](AMM& ammAlice, Env& env) {
+
1204 // Single deposit by asset
+
1205 ammAlice.deposit(
+ +
1207 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(10'000), IOUAmount{10'488'088'48170151, -8}));
+
1208 });
+
1209 testAMM([&](AMM& ammAlice, Env& env) {
+
1210 // Single deposit by asset
+
1211 ammAlice.deposit(
+ +
1213 BEAST_EXPECT(ammAlice.expectBalances(
+
1214 XRP(10'000), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'488'088'48170151, -8}));
+
1215 });
+
1216 }
-
1209
-
1210 void
-
- -
1212 {
-
1213 testcase("Invalid Withdraw");
-
1214
-
1215 auto const all = testable_amendments();
-
1216 using namespace jtx;
1217
-
1218 testAMM(
-
1219 [&](AMM& ammAlice, Env& env) {
-
1220 WithdrawArg args{
-
1221 .asset1Out = XRP(100),
-
1222 .err = ter(tecAMM_BALANCE),
-
1223 };
-
1224 ammAlice.withdraw(args);
-
1225 },
-
1226 {{XRP(99), USD(99)}});
-
1227
-
1228 testAMM(
-
1229 [&](AMM& ammAlice, Env& env) {
-
1230 WithdrawArg args{
-
1231 .asset1Out = USD(100),
-
1232 .err = ter(tecAMM_BALANCE),
-
1233 };
-
1234 ammAlice.withdraw(args);
-
1235 },
-
1236 {{XRP(99), USD(99)}});
-
1237
-
1238 {
-
1239 Env env{*this};
-
1240 env.fund(XRP(30'000), gw, alice, bob);
-
1241 env.close();
-
1242 env(fset(gw, asfRequireAuth));
-
1243 env.close();
-
1244 env(trust(alice, gw["USD"](30'000), 0));
-
1245 env(trust(gw, alice["USD"](0), tfSetfAuth));
-
1246 // Bob trusts Gateway to owe him USD...
-
1247 env(trust(bob, gw["USD"](30'000), 0));
-
1248 // ...but Gateway does not authorize Bob to hold its USD.
+
1218 void
+
+ +
1220 {
+
1221 testcase("Invalid Withdraw");
+
1222
+
1223 auto const all = testable_amendments();
+
1224 using namespace jtx;
+
1225
+
1226 testAMM(
+
1227 [&](AMM& ammAlice, Env& env) {
+
1228 WithdrawArg args{
+
1229 .asset1Out = XRP(100),
+
1230 .err = ter(tecAMM_BALANCE),
+
1231 };
+
1232 ammAlice.withdraw(args);
+
1233 },
+
1234 {{XRP(99), USD(99)}});
+
1235
+
1236 testAMM(
+
1237 [&](AMM& ammAlice, Env& env) {
+
1238 WithdrawArg args{
+
1239 .asset1Out = USD(100),
+
1240 .err = ter(tecAMM_BALANCE),
+
1241 };
+
1242 ammAlice.withdraw(args);
+
1243 },
+
1244 {{XRP(99), USD(99)}});
+
1245
+
1246 {
+
1247 Env env{*this};
+
1248 env.fund(XRP(30'000), gw, alice, bob);
1249 env.close();
-
1250 env(pay(gw, alice, USD(10'000)));
+
1250 env(fset(gw, asfRequireAuth));
1251 env.close();
-
1252 AMM ammAlice(env, alice, XRP(10'000), USD(10'000));
-
1253 WithdrawArg args{
-
1254 .account = bob,
-
1255 .asset1Out = USD(100),
-
1256 .err = ter(tecNO_AUTH),
-
1257 };
-
1258 ammAlice.withdraw(args);
-
1259 }
-
1260
-
1261 testAMM([&](AMM& ammAlice, Env& env) {
-
1262 // Invalid flags
-
1263 ammAlice.withdraw(
-
1264 alice,
-
1265 1'000'000,
- - - -
1269 tfBurnable,
- - - -
1273 ammAlice.withdraw(
-
1274 alice,
-
1275 1'000'000,
+
1252 env(trust(alice, gw["USD"](30'000), 0));
+
1253 env(trust(gw, alice["USD"](0), tfSetfAuth));
+
1254 // Bob trusts Gateway to owe him USD...
+
1255 env(trust(bob, gw["USD"](30'000), 0));
+
1256 // ...but Gateway does not authorize Bob to hold its USD.
+
1257 env.close();
+
1258 env(pay(gw, alice, USD(10'000)));
+
1259 env.close();
+
1260 AMM ammAlice(env, alice, XRP(10'000), USD(10'000));
+
1261 WithdrawArg args{
+
1262 .account = bob,
+
1263 .asset1Out = USD(100),
+
1264 .err = ter(tecNO_AUTH),
+
1265 };
+
1266 ammAlice.withdraw(args);
+
1267 }
+
1268
+
1269 testAMM([&](AMM& ammAlice, Env& env) {
+
1270 // Invalid flags
+
1271 ammAlice.withdraw(
+
1272 alice,
+
1273 1'000'000,
+ + - +
1277 tfBurnable,
- - - - -
1283
-
1284 // Invalid options
- - - - - - -
1291 NotTEC>>
-
1292 invalidOptions = {
-
1293 // tokens, asset1Out, asset2Out, EPrice, flags, ter
- - - - - -
1299 {std::nullopt,
- - - - -
1304 temMALFORMED},
- - - - - - -
1311 {std::nullopt, XRP(100), USD(100), IOUAmount{250, 0}, std::nullopt, temMALFORMED},
-
1312 {1'000, XRP(100), USD(100), std::nullopt, std::nullopt, temMALFORMED},
- -
1314 for (auto const& it : invalidOptions)
-
1315 {
-
1316 ammAlice.withdraw(
-
1317 alice,
-
1318 std::get<0>(it),
-
1319 std::get<1>(it),
-
1320 std::get<2>(it),
-
1321 std::get<3>(it),
-
1322 std::get<4>(it),
- - -
1325 ter(std::get<5>(it)));
-
1326 }
-
1327
-
1328 // Invalid tokens
- - -
1331
-
1332 // Mismatched token, invalid Asset1Out issue
- -
1334
-
1335 // Mismatched token, invalid Asset2Out issue
-
1336 ammAlice.withdraw(alice, USD(100), GBP(100), std::nullopt, ter(temBAD_AMM_TOKENS));
-
1337
-
1338 // Mismatched token, Asset1Out.issue == Asset2Out.issue
-
1339 ammAlice.withdraw(alice, USD(100), USD(100), std::nullopt, ter(temBAD_AMM_TOKENS));
-
1340
-
1341 // Invalid amount value
- - -
1344 ammAlice.withdraw(alice, USD(10), std::nullopt, IOUAmount{-1}, ter(temBAD_AMOUNT));
+ + +
1281 ammAlice.withdraw(
+
1282 alice,
+
1283 1'000'000,
+ + + + + + + +
1291
+
1292 // Invalid options
+ + + + + + +
1299 NotTEC>>
+
1300 invalidOptions = {
+
1301 // tokens, asset1Out, asset2Out, EPrice, flags, ter
+ + + + + +
1307 {std::nullopt,
+ + + + +
1312 temMALFORMED},
+ + + + + + +
1319 {std::nullopt, XRP(100), USD(100), IOUAmount{250, 0}, std::nullopt, temMALFORMED},
+
1320 {1'000, XRP(100), USD(100), std::nullopt, std::nullopt, temMALFORMED},
+ +
1322 for (auto const& it : invalidOptions)
+
1323 {
+
1324 ammAlice.withdraw(
+
1325 alice,
+
1326 std::get<0>(it),
+
1327 std::get<1>(it),
+
1328 std::get<2>(it),
+
1329 std::get<3>(it),
+
1330 std::get<4>(it),
+ + +
1333 ter(std::get<5>(it)));
+
1334 }
+
1335
+
1336 // Invalid tokens
+ + +
1339
+
1340 // Mismatched token, invalid Asset1Out issue
+ +
1342
+
1343 // Mismatched token, invalid Asset2Out issue
+
1344 ammAlice.withdraw(alice, USD(100), GBP(100), std::nullopt, ter(temBAD_AMM_TOKENS));
1345
-
1346 // Invalid amount/token value, withdraw all tokens from one side
-
1347 // of the pool.
- - -
1350 ammAlice.withdraw(
-
1351 alice,
- -
1353 USD(0),
- - - - - - -
1360
-
1361 // Bad currency
- -
1363
-
1364 // Invalid Account
-
1365 Account bad("bad");
-
1366 env.memoize(bad);
-
1367 ammAlice.withdraw(
-
1368 bad,
-
1369 1'000'000,
- - - - - -
1375 seq(1),
- -
1377
-
1378 // Invalid AMM
-
1379 ammAlice.withdraw(
-
1380 alice,
-
1381 1'000,
+
1346 // Mismatched token, Asset1Out.issue == Asset2Out.issue
+
1347 ammAlice.withdraw(alice, USD(100), USD(100), std::nullopt, ter(temBAD_AMM_TOKENS));
+
1348
+
1349 // Invalid amount value
+ + +
1352 ammAlice.withdraw(alice, USD(10), std::nullopt, IOUAmount{-1}, ter(temBAD_AMOUNT));
+
1353
+
1354 // Invalid amount/token value, withdraw all tokens from one side
+
1355 // of the pool.
+ + +
1358 ammAlice.withdraw(
+
1359 alice,
+ +
1361 USD(0),
+ + + + + + +
1368
+
1369 // Bad currency
+ +
1371
+
1372 // Invalid Account
+
1373 Account bad("bad");
+
1374 env.memoize(bad);
+
1375 ammAlice.withdraw(
+
1376 bad,
+
1377 1'000'000,
+ + + + - - - -
1386 {{USD, GBP}},
- -
1388 ter(terNO_AMM));
-
1389
-
1390 // Carol is not a Liquidity Provider
- -
1392
-
1393 // Withdrawing from one side.
-
1394 // XRP by tokens
-
1395 ammAlice.withdraw(alice, IOUAmount(9'999'999'9999, -4), XRP(0), std::nullopt, ter(tecAMM_BALANCE));
-
1396 // USD by tokens
-
1397 ammAlice.withdraw(alice, IOUAmount(9'999'999'9, -1), USD(0), std::nullopt, ter(tecAMM_BALANCE));
-
1398 // XRP
- -
1400 // USD
-
1401 ammAlice.withdraw(
-
1402 alice,
-
1403 STAmount{USD, UINT64_C(9'999'9999999999999), -13},
- - - -
1407 });
-
1408
-
1409 testAMM(
-
1410 [&](AMM& ammAlice, Env& env) {
-
1411 // Withdraw entire one side of the pool.
-
1412 // Pre-amendment:
-
1413 // Equal withdraw but due to XRP rounding
-
1414 // this results in full withdraw of XRP pool only,
-
1415 // while leaving a tiny amount in USD pool.
-
1416 // Post-amendment:
-
1417 // Most of the pool is withdrawn with remaining tiny amounts
-
1418 auto err = env.enabled(fixAMMv1_3) ? ter(tesSUCCESS) : ter(tecAMM_BALANCE);
-
1419 ammAlice.withdraw(alice, IOUAmount{9'999'999'9999, -4}, std::nullopt, std::nullopt, err);
-
1420 if (env.enabled(fixAMMv1_3))
-
1421 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(1), STAmount{USD, 1, -7}, IOUAmount{1, -4}));
-
1422 },
- -
1424 0,
- -
1426 {all, all - fixAMMv1_3});
-
1427
-
1428 testAMM(
-
1429 [&](AMM& ammAlice, Env& env) {
-
1430 // Similar to above with even smaller remaining amount
-
1431 // is it ok that the pool is unbalanced?
-
1432 // Withdraw entire one side of the pool.
-
1433 // Equal withdraw but due to XRP precision limit,
-
1434 // this results in full withdraw of XRP pool only,
-
1435 // while leaving a tiny amount in USD pool.
-
1436 auto err = env.enabled(fixAMMv1_3) ? ter(tesSUCCESS) : ter(tecAMM_BALANCE);
-
1437 ammAlice.withdraw(alice, IOUAmount{9'999'999'999999999, -9}, std::nullopt, std::nullopt, err);
-
1438 if (env.enabled(fixAMMv1_3))
-
1439 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(1), STAmount{USD, 1, -11}, IOUAmount{1, -8}));
-
1440 },
- -
1442 0,
- -
1444 {all, all - fixAMMv1_3});
-
1445
-
1446 // Invalid AMM
-
1447 testAMM([&](AMM& ammAlice, Env& env) {
-
1448 ammAlice.withdrawAll(alice);
-
1449 ammAlice.withdraw(alice, 10'000, std::nullopt, std::nullopt, ter(terNO_AMM));
-
1450 });
-
1451
-
1452 // Globally frozen asset
-
1453 testAMM([&](AMM& ammAlice, Env& env) {
-
1454 env(fset(gw, asfGlobalFreeze));
-
1455 env.close();
-
1456 // Can withdraw non-frozen token
-
1457 ammAlice.withdraw(alice, XRP(100));
- -
1459 ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
-
1460 });
-
1461
-
1462 // Individually frozen (AMM) account
-
1463 testAMM([&](AMM& ammAlice, Env& env) {
-
1464 env(trust(gw, alice["USD"](0), tfSetFreeze));
-
1465 env.close();
-
1466 // Can withdraw non-frozen token
-
1467 ammAlice.withdraw(alice, XRP(100));
-
1468 ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
- -
1470 env(trust(gw, alice["USD"](0), tfClearFreeze));
-
1471 // Individually frozen AMM
-
1472 env(trust(gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze));
-
1473 // Can withdraw non-frozen token
-
1474 ammAlice.withdraw(alice, XRP(100));
-
1475 ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
- -
1477 });
-
1478
-
1479 // Carol withdraws more than she owns
-
1480 testAMM([&](AMM& ammAlice, Env&) {
-
1481 // Single deposit of 100000 worth of tokens,
-
1482 // which is 10% of the pool. Carol is LP now.
-
1483 ammAlice.deposit(carol, 1'000'000);
-
1484 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
-
1485
- -
1487 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
-
1488 });
-
1489
-
1490 // Withdraw with EPrice limit. Fails to withdraw, calculated tokens
-
1491 // to withdraw are 0.
-
1492 testAMM(
-
1493 [&](AMM& ammAlice, Env& env) {
-
1494 ammAlice.deposit(carol, 1'000'000);
-
1495 auto const err = env.enabled(fixAMMv1_3) ? ter(tecAMM_INVALID_TOKENS) : ter(tecAMM_FAILED);
-
1496 ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{500, 0}, err);
-
1497 },
- -
1499 0,
- -
1501 {all, all - fixAMMv1_3});
-
1502
-
1503 // Withdraw with EPrice limit. Fails to withdraw, calculated tokens
-
1504 // to withdraw are greater than the LP shares.
-
1505 testAMM([&](AMM& ammAlice, Env&) {
-
1506 ammAlice.deposit(carol, 1'000'000);
-
1507 ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{600, 0}, ter(tecAMM_INVALID_TOKENS));
-
1508 });
-
1509
-
1510 // Withdraw with EPrice limit. Fails to withdraw, amount1
-
1511 // to withdraw is less than 1700USD.
-
1512 testAMM([&](AMM& ammAlice, Env&) {
-
1513 ammAlice.deposit(carol, 1'000'000);
-
1514 ammAlice.withdraw(carol, USD(1'700), std::nullopt, IOUAmount{520, 0}, ter(tecAMM_FAILED));
-
1515 });
-
1516
-
1517 // Deposit/Withdraw the same amount with the trading fee
-
1518 testAMM(
-
1519 [&](AMM& ammAlice, Env&) {
-
1520 ammAlice.deposit(carol, USD(1'000));
- -
1522 },
- -
1524 1'000);
-
1525 testAMM(
-
1526 [&](AMM& ammAlice, Env&) {
-
1527 ammAlice.deposit(carol, XRP(1'000));
- -
1529 },
- -
1531 1'000);
-
1532
-
1533 // Deposit/Withdraw the same amount fails due to the tokens adjustment
-
1534 testAMM([&](AMM& ammAlice, Env&) {
-
1535 ammAlice.deposit(carol, STAmount{USD, 1, -6});
- -
1537 });
-
1538
-
1539 // Withdraw close to one side of the pool. Account's LP tokens
-
1540 // are rounded to all LP tokens.
-
1541 testAMM(
-
1542 [&](AMM& ammAlice, Env& env) {
-
1543 auto const err = env.enabled(fixAMMv1_3) ? ter(tecINVARIANT_FAILED) : ter(tecAMM_BALANCE);
-
1544 ammAlice.withdraw(
-
1545 alice, STAmount{USD, UINT64_C(9'999'999999999999), -12}, std::nullopt, std::nullopt, err);
-
1546 },
-
1547 {.features = {all, all - fixAMMv1_3}, .noLog = true});
-
1548
-
1549 // Tiny withdraw
-
1550 testAMM([&](AMM& ammAlice, Env&) {
-
1551 // XRP amount to withdraw is 0
- -
1553 // Calculated tokens to withdraw are 0
- -
1555 ammAlice.deposit(carol, STAmount{USD, 1, -10});
- - -
1558 ammAlice.withdraw(WithdrawArg{.tokens = IOUAmount{1, -10}, .err = ter(tecAMM_INVALID_TOKENS)});
-
1559 ammAlice.withdraw(WithdrawArg{
-
1560 .asset1Out = STAmount{USD, 1, -15}, .asset2Out = XRPAmount{1}, .err = ter(tecAMM_INVALID_TOKENS)});
-
1561 ammAlice.withdraw(WithdrawArg{
-
1562 .tokens = IOUAmount{1, -10}, .asset1Out = STAmount{USD, 1, -15}, .err = ter(tecAMM_INVALID_TOKENS)});
-
1563 });
-
1564 }
+
1383 seq(1),
+ +
1385
+
1386 // Invalid AMM
+
1387 ammAlice.withdraw(
+
1388 alice,
+
1389 1'000,
+ + + + +
1394 {{USD, GBP}},
+ +
1396 ter(terNO_AMM));
+
1397
+
1398 // Carol is not a Liquidity Provider
+ +
1400
+
1401 // Withdrawing from one side.
+
1402 // XRP by tokens
+
1403 ammAlice.withdraw(alice, IOUAmount(9'999'999'9999, -4), XRP(0), std::nullopt, ter(tecAMM_BALANCE));
+
1404 // USD by tokens
+
1405 ammAlice.withdraw(alice, IOUAmount(9'999'999'9, -1), USD(0), std::nullopt, ter(tecAMM_BALANCE));
+
1406 // XRP
+ +
1408 // USD
+
1409 ammAlice.withdraw(
+
1410 alice,
+
1411 STAmount{USD, UINT64_C(9'999'9999999999999), -13},
+ + + +
1415 });
+
1416
+
1417 testAMM(
+
1418 [&](AMM& ammAlice, Env& env) {
+
1419 // Withdraw entire one side of the pool.
+
1420 // Pre-amendment:
+
1421 // Equal withdraw but due to XRP rounding
+
1422 // this results in full withdraw of XRP pool only,
+
1423 // while leaving a tiny amount in USD pool.
+
1424 // Post-amendment:
+
1425 // Most of the pool is withdrawn with remaining tiny amounts
+
1426 auto err = env.enabled(fixAMMv1_3) ? ter(tesSUCCESS) : ter(tecAMM_BALANCE);
+
1427 ammAlice.withdraw(alice, IOUAmount{9'999'999'9999, -4}, std::nullopt, std::nullopt, err);
+
1428 if (env.enabled(fixAMMv1_3))
+
1429 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(1), STAmount{USD, 1, -7}, IOUAmount{1, -4}));
+
1430 },
+ +
1432 0,
+ +
1434 {all, all - fixAMMv1_3});
+
1435
+
1436 testAMM(
+
1437 [&](AMM& ammAlice, Env& env) {
+
1438 // Similar to above with even smaller remaining amount
+
1439 // is it ok that the pool is unbalanced?
+
1440 // Withdraw entire one side of the pool.
+
1441 // Equal withdraw but due to XRP precision limit,
+
1442 // this results in full withdraw of XRP pool only,
+
1443 // while leaving a tiny amount in USD pool.
+
1444 auto err = env.enabled(fixAMMv1_3) ? ter(tesSUCCESS) : ter(tecAMM_BALANCE);
+
1445 ammAlice.withdraw(alice, IOUAmount{9'999'999'999999999, -9}, std::nullopt, std::nullopt, err);
+
1446 if (env.enabled(fixAMMv1_3))
+
1447 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(1), STAmount{USD, 1, -11}, IOUAmount{1, -8}));
+
1448 },
+ +
1450 0,
+ +
1452 {all, all - fixAMMv1_3});
+
1453
+
1454 // Invalid AMM
+
1455 testAMM([&](AMM& ammAlice, Env& env) {
+
1456 ammAlice.withdrawAll(alice);
+
1457 ammAlice.withdraw(alice, 10'000, std::nullopt, std::nullopt, ter(terNO_AMM));
+
1458 });
+
1459
+
1460 // Globally frozen asset
+
1461 testAMM([&](AMM& ammAlice, Env& env) {
+
1462 env(fset(gw, asfGlobalFreeze));
+
1463 env.close();
+
1464 // Can withdraw non-frozen token
+
1465 ammAlice.withdraw(alice, XRP(100));
+ +
1467 ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
+
1468 });
+
1469
+
1470 // Individually frozen (AMM) account
+
1471 testAMM([&](AMM& ammAlice, Env& env) {
+
1472 env(trust(gw, alice["USD"](0), tfSetFreeze));
+
1473 env.close();
+
1474 // Can withdraw non-frozen token
+
1475 ammAlice.withdraw(alice, XRP(100));
+
1476 ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
+ +
1478 env(trust(gw, alice["USD"](0), tfClearFreeze));
+
1479 // Individually frozen AMM
+
1480 env(trust(gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze));
+
1481 // Can withdraw non-frozen token
+
1482 ammAlice.withdraw(alice, XRP(100));
+
1483 ammAlice.withdraw(alice, 1'000, std::nullopt, std::nullopt, ter(tecFROZEN));
+ +
1485 });
+
1486
+
1487 // Carol withdraws more than she owns
+
1488 testAMM([&](AMM& ammAlice, Env&) {
+
1489 // Single deposit of 100000 worth of tokens,
+
1490 // which is 10% of the pool. Carol is LP now.
+
1491 ammAlice.deposit(carol, 1'000'000);
+
1492 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
+
1493
+ +
1495 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
+
1496 });
+
1497
+
1498 // Withdraw with EPrice limit. Fails to withdraw, calculated tokens
+
1499 // to withdraw are 0.
+
1500 testAMM(
+
1501 [&](AMM& ammAlice, Env& env) {
+
1502 ammAlice.deposit(carol, 1'000'000);
+
1503 auto const err = env.enabled(fixAMMv1_3) ? ter(tecAMM_INVALID_TOKENS) : ter(tecAMM_FAILED);
+
1504 ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{500, 0}, err);
+
1505 },
+ +
1507 0,
+ +
1509 {all, all - fixAMMv1_3});
+
1510
+
1511 // Withdraw with EPrice limit. Fails to withdraw, calculated tokens
+
1512 // to withdraw are greater than the LP shares.
+
1513 testAMM([&](AMM& ammAlice, Env&) {
+
1514 ammAlice.deposit(carol, 1'000'000);
+
1515 ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{600, 0}, ter(tecAMM_INVALID_TOKENS));
+
1516 });
+
1517
+
1518 // Withdraw with EPrice limit. Fails to withdraw, amount1
+
1519 // to withdraw is less than 1700USD.
+
1520 testAMM([&](AMM& ammAlice, Env&) {
+
1521 ammAlice.deposit(carol, 1'000'000);
+
1522 ammAlice.withdraw(carol, USD(1'700), std::nullopt, IOUAmount{520, 0}, ter(tecAMM_FAILED));
+
1523 });
+
1524
+
1525 // Deposit/Withdraw the same amount with the trading fee
+
1526 testAMM(
+
1527 [&](AMM& ammAlice, Env&) {
+
1528 ammAlice.deposit(carol, USD(1'000));
+ +
1530 },
+ +
1532 1'000);
+
1533 testAMM(
+
1534 [&](AMM& ammAlice, Env&) {
+
1535 ammAlice.deposit(carol, XRP(1'000));
+ +
1537 },
+ +
1539 1'000);
+
1540
+
1541 // Deposit/Withdraw the same amount fails due to the tokens adjustment
+
1542 testAMM([&](AMM& ammAlice, Env&) {
+
1543 ammAlice.deposit(carol, STAmount{USD, 1, -6});
+ +
1545 });
+
1546
+
1547 // Withdraw close to one side of the pool. Account's LP tokens
+
1548 // are rounded to all LP tokens.
+
1549 testAMM(
+
1550 [&](AMM& ammAlice, Env& env) {
+
1551 auto const err = env.enabled(fixAMMv1_3) ? ter(tecINVARIANT_FAILED) : ter(tecAMM_BALANCE);
+
1552 ammAlice.withdraw(
+
1553 alice, STAmount{USD, UINT64_C(9'999'999999999999), -12}, std::nullopt, std::nullopt, err);
+
1554 },
+
1555 {.features = {all, all - fixAMMv1_3}, .noLog = true});
+
1556
+
1557 // Tiny withdraw
+
1558 testAMM([&](AMM& ammAlice, Env&) {
+
1559 // XRP amount to withdraw is 0
+ +
1561 // Calculated tokens to withdraw are 0
+ +
1563 ammAlice.deposit(carol, STAmount{USD, 1, -10});
+ + +
1566 ammAlice.withdraw(WithdrawArg{.tokens = IOUAmount{1, -10}, .err = ter(tecAMM_INVALID_TOKENS)});
+
1567 ammAlice.withdraw(
+ +
1569 .asset1Out = STAmount{USD, 1, -15}, .asset2Out = XRPAmount{1}, .err = ter(tecAMM_INVALID_TOKENS)});
+
1570 ammAlice.withdraw(
+ +
1572 .tokens = IOUAmount{1, -10},
+
1573 .asset1Out = STAmount{USD, 1, -15},
+
1574 .err = ter(tecAMM_INVALID_TOKENS)});
+
1575 });
+
1576 }
-
1565
-
1566 void
-
- -
1568 {
-
1569 testcase("Withdraw");
-
1570
-
1571 auto const all = testable_amendments();
-
1572 using namespace jtx;
-
1573
-
1574 // Equal withdrawal by Carol: 1000000 of tokens, 10% of the current
-
1575 // pool
-
1576 testAMM([&](AMM& ammAlice, Env& env) {
-
1577 auto const baseFee = env.current()->fees().base.drops();
-
1578 // Single deposit of 100000 worth of tokens,
-
1579 // which is 10% of the pool. Carol is LP now.
-
1580 ammAlice.deposit(carol, 1'000'000);
-
1581 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
-
1582 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000'000, 0}));
-
1583 // 30,000 less deposited 1,000
-
1584 BEAST_EXPECT(expectHolding(env, carol, USD(29'000)));
-
1585 // 30,000 less deposited 1,000 and 10 drops tx fee
-
1586 BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRPAmount{29'000'000'000 - baseFee}));
-
1587
-
1588 // Carol withdraws all tokens
-
1589 ammAlice.withdraw(carol, 1'000'000);
-
1590 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount(beast::Zero())));
-
1591 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
-
1592 BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRPAmount{30'000'000'000 - 2 * baseFee}));
-
1593 });
-
1594
-
1595 // Equal withdrawal by tokens 1000000, 10%
-
1596 // of the current pool
-
1597 testAMM([&](AMM& ammAlice, Env&) {
-
1598 ammAlice.withdraw(alice, 1'000'000);
-
1599 BEAST_EXPECT(ammAlice.expectBalances(XRP(9'000), USD(9'000), IOUAmount{9'000'000, 0}));
-
1600 });
-
1601
-
1602 // Equal withdrawal with a limit. Withdraw XRP200.
-
1603 // If proportional withdraw of USD is less than 100
-
1604 // then withdraw that amount, otherwise withdraw USD100
-
1605 // and proportionally withdraw XRP. It's the latter
-
1606 // in this case - XRP100/USD100.
-
1607 testAMM([&](AMM& ammAlice, Env&) {
-
1608 ammAlice.withdraw(alice, XRP(200), USD(100));
-
1609 BEAST_EXPECT(ammAlice.expectBalances(XRP(9'900), USD(9'900), IOUAmount{9'900'000, 0}));
-
1610 });
-
1611
-
1612 // Equal withdrawal with a limit. XRP100/USD100.
-
1613 testAMM([&](AMM& ammAlice, Env&) {
-
1614 ammAlice.withdraw(alice, XRP(100), USD(200));
-
1615 BEAST_EXPECT(ammAlice.expectBalances(XRP(9'900), USD(9'900), IOUAmount{9'900'000, 0}));
-
1616 });
-
1617
-
1618 // Single withdrawal by amount XRP1000
-
1619 testAMM(
-
1620 [&](AMM& ammAlice, Env& env) {
-
1621 ammAlice.withdraw(alice, XRP(1'000));
-
1622 if (!env.enabled(fixAMMv1_3))
-
1623 BEAST_EXPECT(ammAlice.expectBalances(XRP(9'000), USD(10'000), IOUAmount{9'486'832'98050514, -8}));
-
1624 else
-
1625 BEAST_EXPECT(ammAlice.expectBalances(
-
1626 XRPAmount{9'000'000'001}, USD(10'000), IOUAmount{9'486'832'98050514, -8}));
-
1627 },
- -
1629 0,
- -
1631 {all, all - fixAMMv1_3});
-
1632
-
1633 // Single withdrawal by tokens 10000.
-
1634 testAMM([&](AMM& ammAlice, Env&) {
-
1635 ammAlice.withdraw(alice, 10'000, USD(0));
-
1636 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(9980.01), IOUAmount{9'990'000, 0}));
-
1637 });
-
1638
-
1639 // Withdraw all tokens.
-
1640 testAMM([&](AMM& ammAlice, Env& env) {
-
1641 env(trust(carol, STAmount{ammAlice.lptIssue(), 10'000}));
-
1642 // Can SetTrust only for AMM LP tokens
-
1643 env(trust(carol, STAmount{Issue{EUR.currency, ammAlice.ammAccount()}, 10'000}), ter(tecNO_PERMISSION));
-
1644 env.close();
-
1645 ammAlice.withdrawAll(alice);
-
1646 BEAST_EXPECT(!ammAlice.ammExists());
-
1647
-
1648 BEAST_EXPECT(!env.le(keylet::ownerDir(ammAlice.ammAccount())));
-
1649
-
1650 // Can create AMM for the XRP/USD pair
-
1651 AMM ammCarol(env, carol, XRP(10'000), USD(10'000));
-
1652 BEAST_EXPECT(ammCarol.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0}));
-
1653 });
-
1654
-
1655 // Single deposit 1000USD, withdraw all tokens in USD
-
1656 testAMM([&](AMM& ammAlice, Env& env) {
-
1657 ammAlice.deposit(carol, USD(1'000));
-
1658 ammAlice.withdrawAll(carol, USD(0));
-
1659 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0}));
-
1660 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount(beast::Zero())));
-
1661 });
-
1662
-
1663 // Single deposit 1000USD, withdraw all tokens in XRP
-
1664 testAMM([&](AMM& ammAlice, Env&) {
-
1665 ammAlice.deposit(carol, USD(1'000));
-
1666 ammAlice.withdrawAll(carol, XRP(0));
-
1667 BEAST_EXPECT(ammAlice.expectBalances(
-
1668 XRPAmount(9'090'909'091), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'000'000, 0}));
-
1669 });
-
1670
-
1671 // Single deposit/withdraw by the same account
-
1672 testAMM(
-
1673 [&](AMM& ammAlice, Env& env) {
-
1674 // Since a smaller amount might be deposited due to
-
1675 // the lp tokens adjustment, withdrawing by tokens
-
1676 // is generally preferred to withdrawing by amount.
-
1677 auto lpTokens = ammAlice.deposit(carol, USD(1'000));
-
1678 ammAlice.withdraw(carol, lpTokens, USD(0));
-
1679 lpTokens = ammAlice.deposit(carol, STAmount(USD, 1, -6));
-
1680 ammAlice.withdraw(carol, lpTokens, USD(0));
-
1681 lpTokens = ammAlice.deposit(carol, XRPAmount(1));
-
1682 ammAlice.withdraw(carol, lpTokens, XRPAmount(0));
-
1683 if (!env.enabled(fixAMMv1_3))
-
1684 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens()));
-
1685 else
-
1686 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(10'000'000'001), USD(10'000), ammAlice.tokens()));
-
1687 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
-
1688 },
- -
1690 0,
- -
1692 {all, all - fixAMMv1_3});
-
1693
-
1694 // Single deposit by different accounts and then withdraw
-
1695 // in reverse.
-
1696 testAMM([&](AMM& ammAlice, Env&) {
-
1697 auto const carolTokens = ammAlice.deposit(carol, USD(1'000));
-
1698 auto const aliceTokens = ammAlice.deposit(alice, USD(1'000));
-
1699 ammAlice.withdraw(alice, aliceTokens, USD(0));
-
1700 ammAlice.withdraw(carol, carolTokens, USD(0));
-
1701 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens()));
-
1702 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
-
1703 BEAST_EXPECT(ammAlice.expectLPTokens(alice, ammAlice.tokens()));
-
1704 });
+
1577
+
1578 void
+
+ +
1580 {
+
1581 testcase("Withdraw");
+
1582
+
1583 auto const all = testable_amendments();
+
1584 using namespace jtx;
+
1585
+
1586 // Equal withdrawal by Carol: 1000000 of tokens, 10% of the current
+
1587 // pool
+
1588 testAMM([&](AMM& ammAlice, Env& env) {
+
1589 auto const baseFee = env.current()->fees().base.drops();
+
1590 // Single deposit of 100000 worth of tokens,
+
1591 // which is 10% of the pool. Carol is LP now.
+
1592 ammAlice.deposit(carol, 1'000'000);
+
1593 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{11'000'000, 0}));
+
1594 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000'000, 0}));
+
1595 // 30,000 less deposited 1,000
+
1596 BEAST_EXPECT(expectHolding(env, carol, USD(29'000)));
+
1597 // 30,000 less deposited 1,000 and 10 drops tx fee
+
1598 BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRPAmount{29'000'000'000 - baseFee}));
+
1599
+
1600 // Carol withdraws all tokens
+
1601 ammAlice.withdraw(carol, 1'000'000);
+
1602 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount(beast::Zero())));
+
1603 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
+
1604 BEAST_EXPECT(expectLedgerEntryRoot(env, carol, XRPAmount{30'000'000'000 - 2 * baseFee}));
+
1605 });
+
1606
+
1607 // Equal withdrawal by tokens 1000000, 10%
+
1608 // of the current pool
+
1609 testAMM([&](AMM& ammAlice, Env&) {
+
1610 ammAlice.withdraw(alice, 1'000'000);
+
1611 BEAST_EXPECT(ammAlice.expectBalances(XRP(9'000), USD(9'000), IOUAmount{9'000'000, 0}));
+
1612 });
+
1613
+
1614 // Equal withdrawal with a limit. Withdraw XRP200.
+
1615 // If proportional withdraw of USD is less than 100
+
1616 // then withdraw that amount, otherwise withdraw USD100
+
1617 // and proportionally withdraw XRP. It's the latter
+
1618 // in this case - XRP100/USD100.
+
1619 testAMM([&](AMM& ammAlice, Env&) {
+
1620 ammAlice.withdraw(alice, XRP(200), USD(100));
+
1621 BEAST_EXPECT(ammAlice.expectBalances(XRP(9'900), USD(9'900), IOUAmount{9'900'000, 0}));
+
1622 });
+
1623
+
1624 // Equal withdrawal with a limit. XRP100/USD100.
+
1625 testAMM([&](AMM& ammAlice, Env&) {
+
1626 ammAlice.withdraw(alice, XRP(100), USD(200));
+
1627 BEAST_EXPECT(ammAlice.expectBalances(XRP(9'900), USD(9'900), IOUAmount{9'900'000, 0}));
+
1628 });
+
1629
+
1630 // Single withdrawal by amount XRP1000
+
1631 testAMM(
+
1632 [&](AMM& ammAlice, Env& env) {
+
1633 ammAlice.withdraw(alice, XRP(1'000));
+
1634 if (!env.enabled(fixAMMv1_3))
+
1635 BEAST_EXPECT(ammAlice.expectBalances(XRP(9'000), USD(10'000), IOUAmount{9'486'832'98050514, -8}));
+
1636 else
+
1637 BEAST_EXPECT(ammAlice.expectBalances(
+
1638 XRPAmount{9'000'000'001}, USD(10'000), IOUAmount{9'486'832'98050514, -8}));
+
1639 },
+ +
1641 0,
+ +
1643 {all, all - fixAMMv1_3});
+
1644
+
1645 // Single withdrawal by tokens 10000.
+
1646 testAMM([&](AMM& ammAlice, Env&) {
+
1647 ammAlice.withdraw(alice, 10'000, USD(0));
+
1648 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(9980.01), IOUAmount{9'990'000, 0}));
+
1649 });
+
1650
+
1651 // Withdraw all tokens.
+
1652 testAMM([&](AMM& ammAlice, Env& env) {
+
1653 env(trust(carol, STAmount{ammAlice.lptIssue(), 10'000}));
+
1654 // Can SetTrust only for AMM LP tokens
+
1655 env(trust(carol, STAmount{Issue{EUR.currency, ammAlice.ammAccount()}, 10'000}), ter(tecNO_PERMISSION));
+
1656 env.close();
+
1657 ammAlice.withdrawAll(alice);
+
1658 BEAST_EXPECT(!ammAlice.ammExists());
+
1659
+
1660 BEAST_EXPECT(!env.le(keylet::ownerDir(ammAlice.ammAccount())));
+
1661
+
1662 // Can create AMM for the XRP/USD pair
+
1663 AMM ammCarol(env, carol, XRP(10'000), USD(10'000));
+
1664 BEAST_EXPECT(ammCarol.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0}));
+
1665 });
+
1666
+
1667 // Single deposit 1000USD, withdraw all tokens in USD
+
1668 testAMM([&](AMM& ammAlice, Env& env) {
+
1669 ammAlice.deposit(carol, USD(1'000));
+
1670 ammAlice.withdrawAll(carol, USD(0));
+
1671 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0}));
+
1672 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount(beast::Zero())));
+
1673 });
+
1674
+
1675 // Single deposit 1000USD, withdraw all tokens in XRP
+
1676 testAMM([&](AMM& ammAlice, Env&) {
+
1677 ammAlice.deposit(carol, USD(1'000));
+
1678 ammAlice.withdrawAll(carol, XRP(0));
+
1679 BEAST_EXPECT(ammAlice.expectBalances(
+
1680 XRPAmount(9'090'909'091), STAmount{USD, UINT64_C(10'999'99999999999), -11}, IOUAmount{10'000'000, 0}));
+
1681 });
+
1682
+
1683 // Single deposit/withdraw by the same account
+
1684 testAMM(
+
1685 [&](AMM& ammAlice, Env& env) {
+
1686 // Since a smaller amount might be deposited due to
+
1687 // the lp tokens adjustment, withdrawing by tokens
+
1688 // is generally preferred to withdrawing by amount.
+
1689 auto lpTokens = ammAlice.deposit(carol, USD(1'000));
+
1690 ammAlice.withdraw(carol, lpTokens, USD(0));
+
1691 lpTokens = ammAlice.deposit(carol, STAmount(USD, 1, -6));
+
1692 ammAlice.withdraw(carol, lpTokens, USD(0));
+
1693 lpTokens = ammAlice.deposit(carol, XRPAmount(1));
+
1694 ammAlice.withdraw(carol, lpTokens, XRPAmount(0));
+
1695 if (!env.enabled(fixAMMv1_3))
+
1696 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens()));
+
1697 else
+
1698 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(10'000'000'001), USD(10'000), ammAlice.tokens()));
+
1699 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
+
1700 },
+ +
1702 0,
+ +
1704 {all, all - fixAMMv1_3});
1705
-
1706 // Equal deposit 10%, withdraw all tokens
-
1707 testAMM([&](AMM& ammAlice, Env&) {
-
1708 ammAlice.deposit(carol, 1'000'000);
-
1709 ammAlice.withdrawAll(carol);
-
1710 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0}));
-
1711 });
-
1712
-
1713 // Equal deposit 10%, withdraw all tokens in USD
-
1714 testAMM([&](AMM& ammAlice, Env&) {
-
1715 ammAlice.deposit(carol, 1'000'000);
-
1716 ammAlice.withdrawAll(carol, USD(0));
-
1717 BEAST_EXPECT(ammAlice.expectBalances(
-
1718 XRP(11'000), STAmount{USD, UINT64_C(9'090'909090909092), -12}, IOUAmount{10'000'000, 0}));
-
1719 });
-
1720
-
1721 // Equal deposit 10%, withdraw all tokens in XRP
-
1722 testAMM([&](AMM& ammAlice, Env&) {
-
1723 ammAlice.deposit(carol, 1'000'000);
-
1724 ammAlice.withdrawAll(carol, XRP(0));
-
1725 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(9'090'909'091), USD(11'000), IOUAmount{10'000'000, 0}));
-
1726 });
-
1727
-
1728 // Withdraw with EPrice limit.
-
1729 testAMM(
-
1730 [&](AMM& ammAlice, Env& env) {
-
1731 ammAlice.deposit(carol, 1'000'000);
-
1732 ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{520, 0});
-
1733 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{153'846'15384616, -8}));
-
1734 if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
-
1735 BEAST_EXPECT(ammAlice.expectBalances(
-
1736 XRPAmount(11'000'000'000),
-
1737 STAmount{USD, UINT64_C(9'372'781065088757), -12},
-
1738 IOUAmount{10'153'846'15384616, -8}));
-
1739 else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
-
1740 BEAST_EXPECT(ammAlice.expectBalances(
-
1741 XRPAmount(11'000'000'000),
-
1742 STAmount{USD, UINT64_C(9'372'781065088769), -12},
-
1743 IOUAmount{10'153'846'15384616, -8}));
-
1744 else if (env.enabled(fixAMMv1_3))
-
1745 BEAST_EXPECT(ammAlice.expectBalances(
-
1746 XRPAmount(11'000'000'000),
-
1747 STAmount{USD, UINT64_C(9'372'78106508877), -11},
-
1748 IOUAmount{10'153'846'15384616, -8}));
-
1749 ammAlice.withdrawAll(carol);
-
1750 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
-
1751 },
-
1752 {.features = {all, all - fixAMMv1_3, all - fixAMMv1_1 - fixAMMv1_3}, .noLog = true});
-
1753
-
1754 // Withdraw with EPrice limit. AssetOut is 0.
-
1755 testAMM(
-
1756 [&](AMM& ammAlice, Env& env) {
-
1757 ammAlice.deposit(carol, 1'000'000);
-
1758 ammAlice.withdraw(carol, USD(0), std::nullopt, IOUAmount{520, 0});
-
1759 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{153'846'15384616, -8}));
-
1760 if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
-
1761 BEAST_EXPECT(ammAlice.expectBalances(
-
1762 XRP(11'000),
-
1763 STAmount{USD, UINT64_C(9'372'781065088757), -12},
-
1764 IOUAmount{10'153'846'15384616, -8}));
-
1765 else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
-
1766 BEAST_EXPECT(ammAlice.expectBalances(
-
1767 XRP(11'000),
-
1768 STAmount{USD, UINT64_C(9'372'781065088769), -12},
-
1769 IOUAmount{10'153'846'15384616, -8}));
-
1770 else if (env.enabled(fixAMMv1_3))
-
1771 BEAST_EXPECT(ammAlice.expectBalances(
-
1772 XRP(11'000),
-
1773 STAmount{USD, UINT64_C(9'372'78106508877), -11},
-
1774 IOUAmount{10'153'846'15384616, -8}));
-
1775 },
- -
1777 0,
- -
1779 {all, all - fixAMMv1_3, all - fixAMMv1_1 - fixAMMv1_3});
-
1780
-
1781 // IOU to IOU + transfer fee
-
1782 {
-
1783 Env env{*this};
-
1784 fund(env, gw, {alice}, {USD(20'000), BTC(0.5)}, Fund::All);
-
1785 env(rate(gw, 1.25));
-
1786 env.close();
-
1787 // no transfer fee on create
-
1788 AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
-
1789 BEAST_EXPECT(ammAlice.expectBalances(USD(20'000), BTC(0.5), IOUAmount{100, 0}));
-
1790 BEAST_EXPECT(expectHolding(env, alice, USD(0)));
-
1791 BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
-
1792 fund(env, gw, {carol}, {USD(2'000), BTC(0.05)}, Fund::Acct);
-
1793 // no transfer fee on deposit
-
1794 ammAlice.deposit(carol, 10);
-
1795 BEAST_EXPECT(ammAlice.expectBalances(USD(22'000), BTC(0.55), IOUAmount{110, 0}));
-
1796 BEAST_EXPECT(expectHolding(env, carol, USD(0)));
-
1797 BEAST_EXPECT(expectHolding(env, carol, BTC(0)));
-
1798 // no transfer fee on withdraw
-
1799 ammAlice.withdraw(carol, 10);
-
1800 BEAST_EXPECT(ammAlice.expectBalances(USD(20'000), BTC(0.5), IOUAmount{100, 0}));
-
1801 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0, 0}));
-
1802 BEAST_EXPECT(expectHolding(env, carol, USD(2'000)));
-
1803 BEAST_EXPECT(expectHolding(env, carol, BTC(0.05)));
-
1804 }
-
1805
-
1806 // Tiny withdraw
-
1807 testAMM([&](AMM& ammAlice, Env&) {
-
1808 // By tokens
-
1809 ammAlice.withdraw(alice, IOUAmount{1, -3});
-
1810 BEAST_EXPECT(ammAlice.expectBalances(
-
1811 XRPAmount{9'999'999'999}, STAmount{USD, UINT64_C(9'999'999999), -6}, IOUAmount{9'999'999'999, -3}));
-
1812 });
-
1813 testAMM(
-
1814 [&](AMM& ammAlice, Env& env) {
-
1815 // Single XRP pool
-
1816 ammAlice.withdraw(alice, std::nullopt, XRPAmount{1});
-
1817 if (!env.enabled(fixAMMv1_3))
-
1818 BEAST_EXPECT(
-
1819 ammAlice.expectBalances(XRPAmount{9'999'999'999}, USD(10'000), IOUAmount{9'999'999'9995, -4}));
-
1820 else
-
1821 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{9'999'999'9995, -4}));
-
1822 },
- -
1824 0,
- -
1826 {all, all - fixAMMv1_3});
-
1827 testAMM([&](AMM& ammAlice, Env&) {
-
1828 // Single USD pool
-
1829 ammAlice.withdraw(alice, std::nullopt, STAmount{USD, 1, -10});
-
1830 BEAST_EXPECT(ammAlice.expectBalances(
-
1831 XRP(10'000), STAmount{USD, UINT64_C(9'999'9999999999), -10}, IOUAmount{9'999'999'99999995, -8}));
-
1832 });
-
1833
-
1834 // Withdraw close to entire pool
-
1835 // Equal by tokens
-
1836 testAMM([&](AMM& ammAlice, Env&) {
-
1837 ammAlice.withdraw(alice, IOUAmount{9'999'999'999, -3});
-
1838 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{1}, STAmount{USD, 1, -6}, IOUAmount{1, -3}));
-
1839 });
-
1840 // USD by tokens
-
1841 testAMM([&](AMM& ammAlice, Env&) {
-
1842 ammAlice.withdraw(alice, IOUAmount{9'999'999}, USD(0));
-
1843 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), STAmount{USD, 1, -10}, IOUAmount{1}));
+
1706 // Single deposit by different accounts and then withdraw
+
1707 // in reverse.
+
1708 testAMM([&](AMM& ammAlice, Env&) {
+
1709 auto const carolTokens = ammAlice.deposit(carol, USD(1'000));
+
1710 auto const aliceTokens = ammAlice.deposit(alice, USD(1'000));
+
1711 ammAlice.withdraw(alice, aliceTokens, USD(0));
+
1712 ammAlice.withdraw(carol, carolTokens, USD(0));
+
1713 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens()));
+
1714 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
+
1715 BEAST_EXPECT(ammAlice.expectLPTokens(alice, ammAlice.tokens()));
+
1716 });
+
1717
+
1718 // Equal deposit 10%, withdraw all tokens
+
1719 testAMM([&](AMM& ammAlice, Env&) {
+
1720 ammAlice.deposit(carol, 1'000'000);
+
1721 ammAlice.withdrawAll(carol);
+
1722 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000, 0}));
+
1723 });
+
1724
+
1725 // Equal deposit 10%, withdraw all tokens in USD
+
1726 testAMM([&](AMM& ammAlice, Env&) {
+
1727 ammAlice.deposit(carol, 1'000'000);
+
1728 ammAlice.withdrawAll(carol, USD(0));
+
1729 BEAST_EXPECT(ammAlice.expectBalances(
+
1730 XRP(11'000), STAmount{USD, UINT64_C(9'090'909090909092), -12}, IOUAmount{10'000'000, 0}));
+
1731 });
+
1732
+
1733 // Equal deposit 10%, withdraw all tokens in XRP
+
1734 testAMM([&](AMM& ammAlice, Env&) {
+
1735 ammAlice.deposit(carol, 1'000'000);
+
1736 ammAlice.withdrawAll(carol, XRP(0));
+
1737 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount(9'090'909'091), USD(11'000), IOUAmount{10'000'000, 0}));
+
1738 });
+
1739
+
1740 // Withdraw with EPrice limit.
+
1741 testAMM(
+
1742 [&](AMM& ammAlice, Env& env) {
+
1743 ammAlice.deposit(carol, 1'000'000);
+
1744 ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{520, 0});
+
1745 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{153'846'15384616, -8}));
+
1746 if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
+
1747 BEAST_EXPECT(ammAlice.expectBalances(
+
1748 XRPAmount(11'000'000'000),
+
1749 STAmount{USD, UINT64_C(9'372'781065088757), -12},
+
1750 IOUAmount{10'153'846'15384616, -8}));
+
1751 else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
+
1752 BEAST_EXPECT(ammAlice.expectBalances(
+
1753 XRPAmount(11'000'000'000),
+
1754 STAmount{USD, UINT64_C(9'372'781065088769), -12},
+
1755 IOUAmount{10'153'846'15384616, -8}));
+
1756 else if (env.enabled(fixAMMv1_3))
+
1757 BEAST_EXPECT(ammAlice.expectBalances(
+
1758 XRPAmount(11'000'000'000),
+
1759 STAmount{USD, UINT64_C(9'372'78106508877), -11},
+
1760 IOUAmount{10'153'846'15384616, -8}));
+
1761 ammAlice.withdrawAll(carol);
+
1762 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
+
1763 },
+
1764 {.features = {all, all - fixAMMv1_3, all - fixAMMv1_1 - fixAMMv1_3}, .noLog = true});
+
1765
+
1766 // Withdraw with EPrice limit. AssetOut is 0.
+
1767 testAMM(
+
1768 [&](AMM& ammAlice, Env& env) {
+
1769 ammAlice.deposit(carol, 1'000'000);
+
1770 ammAlice.withdraw(carol, USD(0), std::nullopt, IOUAmount{520, 0});
+
1771 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{153'846'15384616, -8}));
+
1772 if (!env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
+
1773 BEAST_EXPECT(ammAlice.expectBalances(
+
1774 XRP(11'000),
+
1775 STAmount{USD, UINT64_C(9'372'781065088757), -12},
+
1776 IOUAmount{10'153'846'15384616, -8}));
+
1777 else if (env.enabled(fixAMMv1_1) && !env.enabled(fixAMMv1_3))
+
1778 BEAST_EXPECT(ammAlice.expectBalances(
+
1779 XRP(11'000),
+
1780 STAmount{USD, UINT64_C(9'372'781065088769), -12},
+
1781 IOUAmount{10'153'846'15384616, -8}));
+
1782 else if (env.enabled(fixAMMv1_3))
+
1783 BEAST_EXPECT(ammAlice.expectBalances(
+
1784 XRP(11'000),
+
1785 STAmount{USD, UINT64_C(9'372'78106508877), -11},
+
1786 IOUAmount{10'153'846'15384616, -8}));
+
1787 },
+ +
1789 0,
+ +
1791 {all, all - fixAMMv1_3, all - fixAMMv1_1 - fixAMMv1_3});
+
1792
+
1793 // IOU to IOU + transfer fee
+
1794 {
+
1795 Env env{*this};
+
1796 fund(env, gw, {alice}, {USD(20'000), BTC(0.5)}, Fund::All);
+
1797 env(rate(gw, 1.25));
+
1798 env.close();
+
1799 // no transfer fee on create
+
1800 AMM ammAlice(env, alice, USD(20'000), BTC(0.5));
+
1801 BEAST_EXPECT(ammAlice.expectBalances(USD(20'000), BTC(0.5), IOUAmount{100, 0}));
+
1802 BEAST_EXPECT(expectHolding(env, alice, USD(0)));
+
1803 BEAST_EXPECT(expectHolding(env, alice, BTC(0)));
+
1804 fund(env, gw, {carol}, {USD(2'000), BTC(0.05)}, Fund::Acct);
+
1805 // no transfer fee on deposit
+
1806 ammAlice.deposit(carol, 10);
+
1807 BEAST_EXPECT(ammAlice.expectBalances(USD(22'000), BTC(0.55), IOUAmount{110, 0}));
+
1808 BEAST_EXPECT(expectHolding(env, carol, USD(0)));
+
1809 BEAST_EXPECT(expectHolding(env, carol, BTC(0)));
+
1810 // no transfer fee on withdraw
+
1811 ammAlice.withdraw(carol, 10);
+
1812 BEAST_EXPECT(ammAlice.expectBalances(USD(20'000), BTC(0.5), IOUAmount{100, 0}));
+
1813 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0, 0}));
+
1814 BEAST_EXPECT(expectHolding(env, carol, USD(2'000)));
+
1815 BEAST_EXPECT(expectHolding(env, carol, BTC(0.05)));
+
1816 }
+
1817
+
1818 // Tiny withdraw
+
1819 testAMM([&](AMM& ammAlice, Env&) {
+
1820 // By tokens
+
1821 ammAlice.withdraw(alice, IOUAmount{1, -3});
+
1822 BEAST_EXPECT(ammAlice.expectBalances(
+
1823 XRPAmount{9'999'999'999}, STAmount{USD, UINT64_C(9'999'999999), -6}, IOUAmount{9'999'999'999, -3}));
+
1824 });
+
1825 testAMM(
+
1826 [&](AMM& ammAlice, Env& env) {
+
1827 // Single XRP pool
+
1828 ammAlice.withdraw(alice, std::nullopt, XRPAmount{1});
+
1829 if (!env.enabled(fixAMMv1_3))
+
1830 BEAST_EXPECT(
+
1831 ammAlice.expectBalances(XRPAmount{9'999'999'999}, USD(10'000), IOUAmount{9'999'999'9995, -4}));
+
1832 else
+
1833 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{9'999'999'9995, -4}));
+
1834 },
+ +
1836 0,
+ +
1838 {all, all - fixAMMv1_3});
+
1839 testAMM([&](AMM& ammAlice, Env&) {
+
1840 // Single USD pool
+
1841 ammAlice.withdraw(alice, std::nullopt, STAmount{USD, 1, -10});
+
1842 BEAST_EXPECT(ammAlice.expectBalances(
+
1843 XRP(10'000), STAmount{USD, UINT64_C(9'999'9999999999), -10}, IOUAmount{9'999'999'99999995, -8}));
1844 });
-
1845 // XRP by tokens
-
1846 testAMM([&](AMM& ammAlice, Env&) {
-
1847 ammAlice.withdraw(alice, IOUAmount{9'999'900}, XRP(0));
-
1848 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{1}, USD(10'000), IOUAmount{100}));
-
1849 });
-
1850 // USD
-
1851 testAMM([&](AMM& ammAlice, Env&) {
-
1852 ammAlice.withdraw(alice, STAmount{USD, UINT64_C(9'999'99999999999), -11});
-
1853 BEAST_EXPECT(ammAlice.expectBalances(XRP(10000), STAmount{USD, 1, -11}, IOUAmount{316227765, -9}));
-
1854 });
-
1855 // XRP
-
1856 testAMM([&](AMM& ammAlice, Env&) {
-
1857 ammAlice.withdraw(alice, XRPAmount{9'999'999'999});
-
1858 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{1}, USD(10'000), IOUAmount{100}));
-
1859 });
-
1860 }
+
1845
+
1846 // Withdraw close to entire pool
+
1847 // Equal by tokens
+
1848 testAMM([&](AMM& ammAlice, Env&) {
+
1849 ammAlice.withdraw(alice, IOUAmount{9'999'999'999, -3});
+
1850 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{1}, STAmount{USD, 1, -6}, IOUAmount{1, -3}));
+
1851 });
+
1852 // USD by tokens
+
1853 testAMM([&](AMM& ammAlice, Env&) {
+
1854 ammAlice.withdraw(alice, IOUAmount{9'999'999}, USD(0));
+
1855 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), STAmount{USD, 1, -10}, IOUAmount{1}));
+
1856 });
+
1857 // XRP by tokens
+
1858 testAMM([&](AMM& ammAlice, Env&) {
+
1859 ammAlice.withdraw(alice, IOUAmount{9'999'900}, XRP(0));
+
1860 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{1}, USD(10'000), IOUAmount{100}));
+
1861 });
+
1862 // USD
+
1863 testAMM([&](AMM& ammAlice, Env&) {
+
1864 ammAlice.withdraw(alice, STAmount{USD, UINT64_C(9'999'99999999999), -11});
+
1865 BEAST_EXPECT(ammAlice.expectBalances(XRP(10000), STAmount{USD, 1, -11}, IOUAmount{316227765, -9}));
+
1866 });
+
1867 // XRP
+
1868 testAMM([&](AMM& ammAlice, Env&) {
+
1869 ammAlice.withdraw(alice, XRPAmount{9'999'999'999});
+
1870 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{1}, USD(10'000), IOUAmount{100}));
+
1871 });
+
1872 }
-
1861
-
1862 void
-
- -
1864 {
-
1865 testcase("Invalid Fee Vote");
-
1866 using namespace jtx;
-
1867
-
1868 testAMM([&](AMM& ammAlice, Env& env) {
-
1869 // Invalid flags
- -
1871
-
1872 // Invalid fee.
- -
1874 BEAST_EXPECT(ammAlice.expectTradingFee(0));
-
1875
-
1876 // Invalid Account
-
1877 Account bad("bad");
-
1878 env.memoize(bad);
-
1879 ammAlice.vote(bad, 1'000, std::nullopt, seq(1), std::nullopt, ter(terNO_ACCOUNT));
-
1880
-
1881 // Invalid AMM
-
1882 ammAlice.vote(alice, 1'000, std::nullopt, std::nullopt, {{USD, GBP}}, ter(terNO_AMM));
+
1873
+
1874 void
+
+ +
1876 {
+
1877 testcase("Invalid Fee Vote");
+
1878 using namespace jtx;
+
1879
+
1880 testAMM([&](AMM& ammAlice, Env& env) {
+
1881 // Invalid flags
+
1883
-
1884 // Account is not LP
- -
1886 });
+
1884 // Invalid fee.
+ +
1886 BEAST_EXPECT(ammAlice.expectTradingFee(0));
1887
-
1888 // Invalid AMM
-
1889 testAMM([&](AMM& ammAlice, Env& env) {
-
1890 ammAlice.withdrawAll(alice);
- -
1892 });
-
1893 }
+
1888 // Invalid Account
+
1889 Account bad("bad");
+
1890 env.memoize(bad);
+
1891 ammAlice.vote(bad, 1'000, std::nullopt, seq(1), std::nullopt, ter(terNO_ACCOUNT));
+
1892
+
1893 // Invalid AMM
+
1894 ammAlice.vote(alice, 1'000, std::nullopt, std::nullopt, {{USD, GBP}}, ter(terNO_AMM));
+
1895
+
1896 // Account is not LP
+ +
1898 });
+
1899
+
1900 // Invalid AMM
+
1901 testAMM([&](AMM& ammAlice, Env& env) {
+
1902 ammAlice.withdrawAll(alice);
+ +
1904 });
+
1905 }
-
1894
-
1895 void
-
- -
1897 {
-
1898 testcase("Fee Vote");
-
1899 auto const all = testable_amendments();
-
1900 using namespace jtx;
-
1901
-
1902 // One vote sets fee to 1%.
-
1903 testAMM([&](AMM& ammAlice, Env& env) {
-
1904 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{0}));
-
1905 ammAlice.vote({}, 1'000);
-
1906 BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
-
1907 // Discounted fee is 1/10 of trading fee.
-
1908 BEAST_EXPECT(ammAlice.expectAuctionSlot(100, 0, IOUAmount{0}));
-
1909 });
-
1910
-
1911 auto vote = [&](AMM& ammAlice,
-
1912 Env& env,
-
1913 int i,
-
1914 int fundUSD = 100'000,
-
1915 std::uint32_t tokens = 10'000'000,
-
1916 std::vector<Account>* accounts = nullptr) {
-
1917 Account a(std::to_string(i));
-
1918 // post-amendment the amount to deposit is slightly higher
-
1919 // in order to ensure AMM invariant sqrt(asset1 * asset2) >= tokens
-
1920 // fund just one USD higher in this case, which is enough for
-
1921 // deposit to succeed
-
1922 if (env.enabled(fixAMMv1_3))
-
1923 ++fundUSD;
-
1924 fund(env, gw, {a}, {USD(fundUSD)}, Fund::Acct);
-
1925 ammAlice.deposit(a, tokens);
-
1926 ammAlice.vote(a, 50 * (i + 1));
-
1927 if (accounts)
-
1928 accounts->push_back(std::move(a));
-
1929 };
-
1930
-
1931 // Eight votes fill all voting slots, set fee 0.175%.
-
1932 testAMM(
-
1933 [&](AMM& ammAlice, Env& env) {
-
1934 for (int i = 0; i < 7; ++i)
-
1935 vote(ammAlice, env, i, 10'000);
-
1936 BEAST_EXPECT(ammAlice.expectTradingFee(175));
-
1937 },
- -
1939 0,
- -
1941 {all});
+
1906
+
1907 void
+
+ +
1909 {
+
1910 testcase("Fee Vote");
+
1911 auto const all = testable_amendments();
+
1912 using namespace jtx;
+
1913
+
1914 // One vote sets fee to 1%.
+
1915 testAMM([&](AMM& ammAlice, Env& env) {
+
1916 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{0}));
+
1917 ammAlice.vote({}, 1'000);
+
1918 BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
+
1919 // Discounted fee is 1/10 of trading fee.
+
1920 BEAST_EXPECT(ammAlice.expectAuctionSlot(100, 0, IOUAmount{0}));
+
1921 });
+
1922
+
1923 auto vote = [&](AMM& ammAlice,
+
1924 Env& env,
+
1925 int i,
+
1926 int fundUSD = 100'000,
+
1927 std::uint32_t tokens = 10'000'000,
+
1928 std::vector<Account>* accounts = nullptr) {
+
1929 Account a(std::to_string(i));
+
1930 // post-amendment the amount to deposit is slightly higher
+
1931 // in order to ensure AMM invariant sqrt(asset1 * asset2) >= tokens
+
1932 // fund just one USD higher in this case, which is enough for
+
1933 // deposit to succeed
+
1934 if (env.enabled(fixAMMv1_3))
+
1935 ++fundUSD;
+
1936 fund(env, gw, {a}, {USD(fundUSD)}, Fund::Acct);
+
1937 ammAlice.deposit(a, tokens);
+
1938 ammAlice.vote(a, 50 * (i + 1));
+
1939 if (accounts)
+
1940 accounts->push_back(std::move(a));
+
1941 };
1942
1943 // Eight votes fill all voting slots, set fee 0.175%.
-
1944 // New vote, same account, sets fee 0.225%
-
1945 testAMM([&](AMM& ammAlice, Env& env) {
-
1946 for (int i = 0; i < 7; ++i)
-
1947 vote(ammAlice, env, i);
-
1948 BEAST_EXPECT(ammAlice.expectTradingFee(175));
-
1949 Account const a("0");
-
1950 ammAlice.vote(a, 450);
-
1951 BEAST_EXPECT(ammAlice.expectTradingFee(225));
-
1952 });
-
1953
-
1954 // Eight votes fill all voting slots, set fee 0.175%.
-
1955 // New vote, new account, higher vote weight, set higher fee 0.244%
-
1956 testAMM([&](AMM& ammAlice, Env& env) {
-
1957 for (int i = 0; i < 7; ++i)
-
1958 vote(ammAlice, env, i);
-
1959 BEAST_EXPECT(ammAlice.expectTradingFee(175));
-
1960 vote(ammAlice, env, 7, 100'000, 20'000'000);
-
1961 BEAST_EXPECT(ammAlice.expectTradingFee(244));
-
1962 });
-
1963
-
1964 // Eight votes fill all voting slots, set fee 0.219%.
-
1965 // New vote, new account, higher vote weight, set smaller fee 0.206%
-
1966 testAMM([&](AMM& ammAlice, Env& env) {
-
1967 for (int i = 7; i > 0; --i)
-
1968 vote(ammAlice, env, i);
-
1969 BEAST_EXPECT(ammAlice.expectTradingFee(219));
-
1970 vote(ammAlice, env, 0, 100'000, 20'000'000);
-
1971 BEAST_EXPECT(ammAlice.expectTradingFee(206));
-
1972 });
-
1973
-
1974 // Eight votes fill all voting slots. The accounts then withdraw all
-
1975 // tokens. An account sets a new fee and the previous slots are
-
1976 // deleted.
-
1977 testAMM([&](AMM& ammAlice, Env& env) {
-
1978 std::vector<Account> accounts;
-
1979 for (int i = 0; i < 7; ++i)
-
1980 vote(ammAlice, env, i, 100'000, 10'000'000, &accounts);
-
1981 BEAST_EXPECT(ammAlice.expectTradingFee(175));
-
1982 for (int i = 0; i < 7; ++i)
-
1983 ammAlice.withdrawAll(accounts[i]);
-
1984 ammAlice.deposit(carol, 10'000'000);
-
1985 ammAlice.vote(carol, 1'000);
-
1986 // The initial LP set the fee to 1000. Carol gets 50% voting
-
1987 // power, and the new fee is 500.
-
1988 BEAST_EXPECT(ammAlice.expectTradingFee(500));
-
1989 });
-
1990
-
1991 // Eight votes fill all voting slots. The accounts then withdraw some
-
1992 // tokens. The new vote doesn't get the voting power but
-
1993 // the slots are refreshed and the fee is updated.
-
1994 testAMM([&](AMM& ammAlice, Env& env) {
-
1995 std::vector<Account> accounts;
-
1996 for (int i = 0; i < 7; ++i)
-
1997 vote(ammAlice, env, i, 100'000, 10'000'000, &accounts);
-
1998 BEAST_EXPECT(ammAlice.expectTradingFee(175));
-
1999 for (int i = 0; i < 7; ++i)
-
2000 ammAlice.withdraw(accounts[i], 9'000'000);
-
2001 ammAlice.deposit(carol, 1'000);
-
2002 // The vote is not added to the slots
-
2003 ammAlice.vote(carol, 1'000);
-
2004 auto const info = ammAlice.ammRpcInfo()[jss::amm][jss::vote_slots];
-
2005 for (std::uint16_t i = 0; i < info.size(); ++i)
-
2006 BEAST_EXPECT(info[i][jss::account] != carol.human());
-
2007 // But the slots are refreshed and the fee is changed
-
2008 BEAST_EXPECT(ammAlice.expectTradingFee(82));
-
2009 });
-
2010 }
+
1944 testAMM(
+
1945 [&](AMM& ammAlice, Env& env) {
+
1946 for (int i = 0; i < 7; ++i)
+
1947 vote(ammAlice, env, i, 10'000);
+
1948 BEAST_EXPECT(ammAlice.expectTradingFee(175));
+
1949 },
+ +
1951 0,
+ +
1953 {all});
+
1954
+
1955 // Eight votes fill all voting slots, set fee 0.175%.
+
1956 // New vote, same account, sets fee 0.225%
+
1957 testAMM([&](AMM& ammAlice, Env& env) {
+
1958 for (int i = 0; i < 7; ++i)
+
1959 vote(ammAlice, env, i);
+
1960 BEAST_EXPECT(ammAlice.expectTradingFee(175));
+
1961 Account const a("0");
+
1962 ammAlice.vote(a, 450);
+
1963 BEAST_EXPECT(ammAlice.expectTradingFee(225));
+
1964 });
+
1965
+
1966 // Eight votes fill all voting slots, set fee 0.175%.
+
1967 // New vote, new account, higher vote weight, set higher fee 0.244%
+
1968 testAMM([&](AMM& ammAlice, Env& env) {
+
1969 for (int i = 0; i < 7; ++i)
+
1970 vote(ammAlice, env, i);
+
1971 BEAST_EXPECT(ammAlice.expectTradingFee(175));
+
1972 vote(ammAlice, env, 7, 100'000, 20'000'000);
+
1973 BEAST_EXPECT(ammAlice.expectTradingFee(244));
+
1974 });
+
1975
+
1976 // Eight votes fill all voting slots, set fee 0.219%.
+
1977 // New vote, new account, higher vote weight, set smaller fee 0.206%
+
1978 testAMM([&](AMM& ammAlice, Env& env) {
+
1979 for (int i = 7; i > 0; --i)
+
1980 vote(ammAlice, env, i);
+
1981 BEAST_EXPECT(ammAlice.expectTradingFee(219));
+
1982 vote(ammAlice, env, 0, 100'000, 20'000'000);
+
1983 BEAST_EXPECT(ammAlice.expectTradingFee(206));
+
1984 });
+
1985
+
1986 // Eight votes fill all voting slots. The accounts then withdraw all
+
1987 // tokens. An account sets a new fee and the previous slots are
+
1988 // deleted.
+
1989 testAMM([&](AMM& ammAlice, Env& env) {
+
1990 std::vector<Account> accounts;
+
1991 for (int i = 0; i < 7; ++i)
+
1992 vote(ammAlice, env, i, 100'000, 10'000'000, &accounts);
+
1993 BEAST_EXPECT(ammAlice.expectTradingFee(175));
+
1994 for (int i = 0; i < 7; ++i)
+
1995 ammAlice.withdrawAll(accounts[i]);
+
1996 ammAlice.deposit(carol, 10'000'000);
+
1997 ammAlice.vote(carol, 1'000);
+
1998 // The initial LP set the fee to 1000. Carol gets 50% voting
+
1999 // power, and the new fee is 500.
+
2000 BEAST_EXPECT(ammAlice.expectTradingFee(500));
+
2001 });
+
2002
+
2003 // Eight votes fill all voting slots. The accounts then withdraw some
+
2004 // tokens. The new vote doesn't get the voting power but
+
2005 // the slots are refreshed and the fee is updated.
+
2006 testAMM([&](AMM& ammAlice, Env& env) {
+
2007 std::vector<Account> accounts;
+
2008 for (int i = 0; i < 7; ++i)
+
2009 vote(ammAlice, env, i, 100'000, 10'000'000, &accounts);
+
2010 BEAST_EXPECT(ammAlice.expectTradingFee(175));
+
2011 for (int i = 0; i < 7; ++i)
+
2012 ammAlice.withdraw(accounts[i], 9'000'000);
+
2013 ammAlice.deposit(carol, 1'000);
+
2014 // The vote is not added to the slots
+
2015 ammAlice.vote(carol, 1'000);
+
2016 auto const info = ammAlice.ammRpcInfo()[jss::amm][jss::vote_slots];
+
2017 for (std::uint16_t i = 0; i < info.size(); ++i)
+
2018 BEAST_EXPECT(info[i][jss::account] != carol.human());
+
2019 // But the slots are refreshed and the fee is changed
+
2020 BEAST_EXPECT(ammAlice.expectTradingFee(82));
+
2021 });
+
2022 }
-
2011
-
2012 void
-
- -
2014 {
-
2015 testcase("Invalid Bid");
-
2016 using namespace jtx;
-
2017 using namespace std::chrono;
-
2018
-
2019 // burn all the LPTokens through a AMMBid transaction
-
2020 {
-
2021 Env env(*this);
-
2022 fund(env, gw, {alice}, XRP(2'000), {USD(2'000)});
-
2023 AMM amm(env, gw, XRP(1'000), USD(1'000), false, 1'000);
-
2024
-
2025 // auction slot is owned by the creator of the AMM i.e. gw
-
2026 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{0}));
-
2027
-
2028 // gw attempts to burn all her LPTokens through a bid transaction
-
2029 // this transaction fails because AMMBid transaction can not burn
-
2030 // all the outstanding LPTokens
-
2031 env(amm.bid({
-
2032 .account = gw,
-
2033 .bidMin = 1'000'000,
-
2034 }),
- -
2036 }
-
2037
-
2038 // burn all the LPTokens through a AMMBid transaction
-
2039 {
-
2040 Env env(*this);
-
2041 fund(env, gw, {alice}, XRP(2'000), {USD(2'000)});
-
2042 AMM amm(env, gw, XRP(1'000), USD(1'000), false, 1'000);
-
2043
-
2044 // auction slot is owned by the creator of the AMM i.e. gw
-
2045 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{0}));
-
2046
-
2047 // gw burns all but one of its LPTokens through a bid transaction
-
2048 // this transaction succeeds because the bid price is less than
-
2049 // the total outstanding LPToken balance
-
2050 env(amm.bid({
-
2051 .account = gw,
-
2052 .bidMin = STAmount{amm.lptIssue(), UINT64_C(999'999)},
-
2053 }),
-
2054 ter(tesSUCCESS))
-
2055 .close();
-
2056
-
2057 // gw must own the auction slot
-
2058 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{999'999}));
-
2059
-
2060 // 999'999 tokens are burned, only 1 LPToken is owned by gw
-
2061 BEAST_EXPECT(amm.expectBalances(XRP(1'000), USD(1'000), IOUAmount{1}));
-
2062
-
2063 // gw owns only 1 LPToken in its balance
-
2064 BEAST_EXPECT(Number{amm.getLPTokensBalance(gw)} == 1);
-
2065
-
2066 // gw attempts to burn the last of its LPTokens in an AMMBid
-
2067 // transaction. This transaction fails because it would burn all
-
2068 // the remaining LPTokens
-
2069 env(amm.bid({
-
2070 .account = gw,
-
2071 .bidMin = 1,
-
2072 }),
- -
2074 }
-
2075
-
2076 testAMM([&](AMM& ammAlice, Env& env) {
-
2077 // Invalid flags
-
2078 env(ammAlice.bid({
-
2079 .account = carol,
-
2080 .bidMin = 0,
-
2081 .flags = tfWithdrawAll,
-
2082 }),
- -
2084
-
2085 ammAlice.deposit(carol, 1'000'000);
-
2086 // Invalid Bid price <= 0
-
2087 for (auto bid : {0, -100})
-
2088 {
-
2089 env(ammAlice.bid({
-
2090 .account = carol,
-
2091 .bidMin = bid,
-
2092 }),
-
2093 ter(temBAD_AMOUNT));
-
2094 env(ammAlice.bid({
-
2095 .account = carol,
-
2096 .bidMax = bid,
-
2097 }),
-
2098 ter(temBAD_AMOUNT));
-
2099 }
-
2100
-
2101 // Invalid Min/Max combination
-
2102 env(ammAlice.bid({
-
2103 .account = carol,
-
2104 .bidMin = 200,
-
2105 .bidMax = 100,
-
2106 }),
- -
2108
-
2109 // Invalid Account
-
2110 Account bad("bad");
-
2111 env.memoize(bad);
-
2112 env(ammAlice.bid({
-
2113 .account = bad,
-
2114 .bidMax = 100,
-
2115 }),
-
2116 seq(1),
-
2117 ter(terNO_ACCOUNT));
-
2118
-
2119 // Account is not LP
-
2120 Account const dan("dan");
-
2121 env.fund(XRP(1'000), dan);
-
2122 env(ammAlice.bid({
-
2123 .account = dan,
-
2124 .bidMin = 100,
-
2125 }),
- -
2127 env(ammAlice.bid({
-
2128 .account = dan,
-
2129 }),
- -
2131
-
2132 // Auth account is invalid.
-
2133 env(ammAlice.bid({
-
2134 .account = carol,
-
2135 .bidMin = 100,
-
2136 .authAccounts = {bob},
+
2023
+
2024 void
+
+ +
2026 {
+
2027 testcase("Invalid Bid");
+
2028 using namespace jtx;
+
2029 using namespace std::chrono;
+
2030
+
2031 // burn all the LPTokens through a AMMBid transaction
+
2032 {
+
2033 Env env(*this);
+
2034 fund(env, gw, {alice}, XRP(2'000), {USD(2'000)});
+
2035 AMM amm(env, gw, XRP(1'000), USD(1'000), false, 1'000);
+
2036
+
2037 // auction slot is owned by the creator of the AMM i.e. gw
+
2038 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{0}));
+
2039
+
2040 // gw attempts to burn all her LPTokens through a bid transaction
+
2041 // this transaction fails because AMMBid transaction can not burn
+
2042 // all the outstanding LPTokens
+
2043 env(amm.bid({
+
2044 .account = gw,
+
2045 .bidMin = 1'000'000,
+
2046 }),
+ +
2048 }
+
2049
+
2050 // burn all the LPTokens through a AMMBid transaction
+
2051 {
+
2052 Env env(*this);
+
2053 fund(env, gw, {alice}, XRP(2'000), {USD(2'000)});
+
2054 AMM amm(env, gw, XRP(1'000), USD(1'000), false, 1'000);
+
2055
+
2056 // auction slot is owned by the creator of the AMM i.e. gw
+
2057 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{0}));
+
2058
+
2059 // gw burns all but one of its LPTokens through a bid transaction
+
2060 // this transaction succeeds because the bid price is less than
+
2061 // the total outstanding LPToken balance
+
2062 env(amm.bid({
+
2063 .account = gw,
+
2064 .bidMin = STAmount{amm.lptIssue(), UINT64_C(999'999)},
+
2065 }),
+
2066 ter(tesSUCCESS))
+
2067 .close();
+
2068
+
2069 // gw must own the auction slot
+
2070 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{999'999}));
+
2071
+
2072 // 999'999 tokens are burned, only 1 LPToken is owned by gw
+
2073 BEAST_EXPECT(amm.expectBalances(XRP(1'000), USD(1'000), IOUAmount{1}));
+
2074
+
2075 // gw owns only 1 LPToken in its balance
+
2076 BEAST_EXPECT(Number{amm.getLPTokensBalance(gw)} == 1);
+
2077
+
2078 // gw attempts to burn the last of its LPTokens in an AMMBid
+
2079 // transaction. This transaction fails because it would burn all
+
2080 // the remaining LPTokens
+
2081 env(amm.bid({
+
2082 .account = gw,
+
2083 .bidMin = 1,
+
2084 }),
+ +
2086 }
+
2087
+
2088 testAMM([&](AMM& ammAlice, Env& env) {
+
2089 // Invalid flags
+
2090 env(ammAlice.bid({
+
2091 .account = carol,
+
2092 .bidMin = 0,
+
2093 .flags = tfWithdrawAll,
+
2094 }),
+ +
2096
+
2097 ammAlice.deposit(carol, 1'000'000);
+
2098 // Invalid Bid price <= 0
+
2099 for (auto bid : {0, -100})
+
2100 {
+
2101 env(ammAlice.bid({
+
2102 .account = carol,
+
2103 .bidMin = bid,
+
2104 }),
+
2105 ter(temBAD_AMOUNT));
+
2106 env(ammAlice.bid({
+
2107 .account = carol,
+
2108 .bidMax = bid,
+
2109 }),
+
2110 ter(temBAD_AMOUNT));
+
2111 }
+
2112
+
2113 // Invalid Min/Max combination
+
2114 env(ammAlice.bid({
+
2115 .account = carol,
+
2116 .bidMin = 200,
+
2117 .bidMax = 100,
+
2118 }),
+ +
2120
+
2121 // Invalid Account
+
2122 Account bad("bad");
+
2123 env.memoize(bad);
+
2124 env(ammAlice.bid({
+
2125 .account = bad,
+
2126 .bidMax = 100,
+
2127 }),
+
2128 seq(1),
+
2129 ter(terNO_ACCOUNT));
+
2130
+
2131 // Account is not LP
+
2132 Account const dan("dan");
+
2133 env.fund(XRP(1'000), dan);
+
2134 env(ammAlice.bid({
+
2135 .account = dan,
+
2136 .bidMin = 100,
2137 }),
-
2138 ter(terNO_ACCOUNT));
-
2139
-
2140 // Invalid Assets
-
2141 env(ammAlice.bid({
-
2142 .account = alice,
-
2143 .bidMax = 100,
-
2144 .assets = {{USD, GBP}},
-
2145 }),
-
2146 ter(terNO_AMM));
-
2147
-
2148 // Invalid Min/Max issue
-
2149 env(ammAlice.bid({
-
2150 .account = alice,
-
2151 .bidMax = STAmount{USD, 100},
-
2152 }),
-
2153 ter(temBAD_AMM_TOKENS));
-
2154 env(ammAlice.bid({
-
2155 .account = alice,
-
2156 .bidMin = STAmount{USD, 100},
+ +
2139 env(ammAlice.bid({
+
2140 .account = dan,
+
2141 }),
+ +
2143
+
2144 // Auth account is invalid.
+
2145 env(ammAlice.bid({
+
2146 .account = carol,
+
2147 .bidMin = 100,
+
2148 .authAccounts = {bob},
+
2149 }),
+
2150 ter(terNO_ACCOUNT));
+
2151
+
2152 // Invalid Assets
+
2153 env(ammAlice.bid({
+
2154 .account = alice,
+
2155 .bidMax = 100,
+
2156 .assets = {{USD, GBP}},
2157 }),
-
2158 ter(temBAD_AMM_TOKENS));
-
2159 });
-
2160
-
2161 // Invalid AMM
-
2162 testAMM([&](AMM& ammAlice, Env& env) {
-
2163 ammAlice.withdrawAll(alice);
-
2164 env(ammAlice.bid({
-
2165 .account = alice,
-
2166 .bidMax = 100,
-
2167 }),
-
2168 ter(terNO_AMM));
-
2169 });
-
2170
-
2171 // More than four Auth accounts.
-
2172 testAMM([&](AMM& ammAlice, Env& env) {
-
2173 Account ed("ed");
-
2174 Account bill("bill");
-
2175 Account scott("scott");
-
2176 Account james("james");
-
2177 env.fund(XRP(1'000), bob, ed, bill, scott, james);
-
2178 env.close();
-
2179 ammAlice.deposit(carol, 1'000'000);
-
2180 env(ammAlice.bid({
-
2181 .account = carol,
-
2182 .bidMin = 100,
-
2183 .authAccounts = {bob, ed, bill, scott, james},
-
2184 }),
-
2185 ter(temMALFORMED));
-
2186 });
-
2187
-
2188 // Bid price exceeds LP owned tokens
-
2189 testAMM([&](AMM& ammAlice, Env& env) {
-
2190 fund(env, gw, {bob}, XRP(1'000), {USD(100)}, Fund::Acct);
+
2158 ter(terNO_AMM));
+
2159
+
2160 // Invalid Min/Max issue
+
2161 env(ammAlice.bid({
+
2162 .account = alice,
+
2163 .bidMax = STAmount{USD, 100},
+
2164 }),
+
2165 ter(temBAD_AMM_TOKENS));
+
2166 env(ammAlice.bid({
+
2167 .account = alice,
+
2168 .bidMin = STAmount{USD, 100},
+
2169 }),
+
2170 ter(temBAD_AMM_TOKENS));
+
2171 });
+
2172
+
2173 // Invalid AMM
+
2174 testAMM([&](AMM& ammAlice, Env& env) {
+
2175 ammAlice.withdrawAll(alice);
+
2176 env(ammAlice.bid({
+
2177 .account = alice,
+
2178 .bidMax = 100,
+
2179 }),
+
2180 ter(terNO_AMM));
+
2181 });
+
2182
+
2183 // More than four Auth accounts.
+
2184 testAMM([&](AMM& ammAlice, Env& env) {
+
2185 Account ed("ed");
+
2186 Account bill("bill");
+
2187 Account scott("scott");
+
2188 Account james("james");
+
2189 env.fund(XRP(1'000), bob, ed, bill, scott, james);
+
2190 env.close();
2191 ammAlice.deposit(carol, 1'000'000);
-
2192 ammAlice.deposit(bob, 10);
-
2193 env(ammAlice.bid({
-
2194 .account = carol,
-
2195 .bidMin = 1'000'001,
+
2192 env(ammAlice.bid({
+
2193 .account = carol,
+
2194 .bidMin = 100,
+
2195 .authAccounts = {bob, ed, bill, scott, james},
2196 }),
-
2197 ter(tecAMM_INVALID_TOKENS));
-
2198 env(ammAlice.bid({
-
2199 .account = carol,
-
2200 .bidMax = 1'000'001,
-
2201 }),
-
2202 ter(tecAMM_INVALID_TOKENS));
-
2203 env(ammAlice.bid({
-
2204 .account = carol,
-
2205 .bidMin = 1'000,
-
2206 }));
-
2207 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{1'000}));
-
2208 // Slot purchase price is more than 1000 but bob only has 10 tokens
-
2209 env(ammAlice.bid({
-
2210 .account = bob,
-
2211 }),
-
2212 ter(tecAMM_INVALID_TOKENS));
-
2213 });
-
2214
-
2215 // Bid all tokens, still own the slot
-
2216 {
-
2217 Env env(*this);
-
2218 fund(env, gw, {alice, bob}, XRP(1'000), {USD(1'000)});
-
2219 AMM amm(env, gw, XRP(10), USD(1'000));
-
2220 auto const lpIssue = amm.lptIssue();
-
2221 env.trust(STAmount{lpIssue, 100}, alice);
-
2222 env.trust(STAmount{lpIssue, 50}, bob);
-
2223 env(pay(gw, alice, STAmount{lpIssue, 100}));
-
2224 env(pay(gw, bob, STAmount{lpIssue, 50}));
-
2225 env(amm.bid({.account = alice, .bidMin = 100}));
-
2226 // Alice doesn't have any more tokens, but
-
2227 // she still owns the slot.
-
2228 env(amm.bid({
-
2229 .account = bob,
-
2230 .bidMax = 50,
-
2231 }),
-
2232 ter(tecAMM_FAILED));
-
2233 }
-
2234 }
+
2197 ter(temMALFORMED));
+
2198 });
+
2199
+
2200 // Bid price exceeds LP owned tokens
+
2201 testAMM([&](AMM& ammAlice, Env& env) {
+
2202 fund(env, gw, {bob}, XRP(1'000), {USD(100)}, Fund::Acct);
+
2203 ammAlice.deposit(carol, 1'000'000);
+
2204 ammAlice.deposit(bob, 10);
+
2205 env(ammAlice.bid({
+
2206 .account = carol,
+
2207 .bidMin = 1'000'001,
+
2208 }),
+
2209 ter(tecAMM_INVALID_TOKENS));
+
2210 env(ammAlice.bid({
+
2211 .account = carol,
+
2212 .bidMax = 1'000'001,
+
2213 }),
+
2214 ter(tecAMM_INVALID_TOKENS));
+
2215 env(ammAlice.bid({
+
2216 .account = carol,
+
2217 .bidMin = 1'000,
+
2218 }));
+
2219 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{1'000}));
+
2220 // Slot purchase price is more than 1000 but bob only has 10 tokens
+
2221 env(ammAlice.bid({
+
2222 .account = bob,
+
2223 }),
+
2224 ter(tecAMM_INVALID_TOKENS));
+
2225 });
+
2226
+
2227 // Bid all tokens, still own the slot
+
2228 {
+
2229 Env env(*this);
+
2230 fund(env, gw, {alice, bob}, XRP(1'000), {USD(1'000)});
+
2231 AMM amm(env, gw, XRP(10), USD(1'000));
+
2232 auto const lpIssue = amm.lptIssue();
+
2233 env.trust(STAmount{lpIssue, 100}, alice);
+
2234 env.trust(STAmount{lpIssue, 50}, bob);
+
2235 env(pay(gw, alice, STAmount{lpIssue, 100}));
+
2236 env(pay(gw, bob, STAmount{lpIssue, 50}));
+
2237 env(amm.bid({.account = alice, .bidMin = 100}));
+
2238 // Alice doesn't have any more tokens, but
+
2239 // she still owns the slot.
+
2240 env(amm.bid({
+
2241 .account = bob,
+
2242 .bidMax = 50,
+
2243 }),
+
2244 ter(tecAMM_FAILED));
+
2245 }
+
2246 }
-
2235
-
2236 void
-
- -
2238 {
-
2239 testcase("Bid");
-
2240 using namespace jtx;
-
2241 using namespace std::chrono;
-
2242
-
2243 // For now, just disable SAV entirely, which locks in the small Number
-
2244 // mantissas
-
2245 features = features - featureSingleAssetVault - featureLendingProtocol;
-
2246
-
2247 // Auction slot initially is owned by AMM creator, who pays 0 price.
-
2248
-
2249 // Bid 110 tokens. Pay bidMin.
-
2250 testAMM(
-
2251 [&](AMM& ammAlice, Env& env) {
-
2252 ammAlice.deposit(carol, 1'000'000);
-
2253 env(ammAlice.bid({.account = carol, .bidMin = 110}));
-
2254 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110}));
-
2255 // 110 tokens are burned.
-
2256 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'890, 0}));
-
2257 },
- -
2259 0,
- -
2261 {features});
-
2262
-
2263 // Bid with min/max when the pay price is less than min.
-
2264 testAMM(
-
2265 [&](AMM& ammAlice, Env& env) {
-
2266 ammAlice.deposit(carol, 1'000'000);
-
2267 // Bid exactly 110. Pay 110 because the pay price is < 110.
-
2268 env(ammAlice.bid({.account = carol, .bidMin = 110, .bidMax = 110}));
-
2269 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110}));
-
2270 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'890}));
-
2271 // Bid exactly 180-200. Pay 180 because the pay price is < 180.
-
2272 env(ammAlice.bid({.account = alice, .bidMin = 180, .bidMax = 200}));
-
2273 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{180}));
-
2274 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'814'5, -1}));
-
2275 },
- -
2277 0,
- -
2279 {features});
-
2280
-
2281 // Start bid at bidMin 110.
-
2282 testAMM(
-
2283 [&](AMM& ammAlice, Env& env) {
-
2284 ammAlice.deposit(carol, 1'000'000);
-
2285 // Bid, pay bidMin.
-
2286 env(ammAlice.bid({.account = carol, .bidMin = 110}));
-
2287 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110}));
-
2288
-
2289 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
-
2290 ammAlice.deposit(bob, 1'000'000);
-
2291 // Bid, pay the computed price.
-
2292 env(ammAlice.bid({.account = bob}));
-
2293 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount(1155, -1)));
-
2294
-
2295 // Bid bidMax fails because the computed price is higher.
-
2296 env(ammAlice.bid({
-
2297 .account = carol,
-
2298 .bidMax = 120,
-
2299 }),
- -
2301 // Bid MaxSlotPrice succeeds - pay computed price
-
2302 env(ammAlice.bid({.account = carol, .bidMax = 600}));
-
2303 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{121'275, -3}));
-
2304
-
2305 // Bid Min/MaxSlotPrice fails because the computed price is not
-
2306 // in range
-
2307 env(ammAlice.bid({
-
2308 .account = carol,
-
2309 .bidMin = 10,
-
2310 .bidMax = 100,
+
2247
+
2248 void
+
+ +
2250 {
+
2251 testcase("Bid");
+
2252 using namespace jtx;
+
2253 using namespace std::chrono;
+
2254
+
2255 // For now, just disable SAV entirely, which locks in the small Number
+
2256 // mantissas
+
2257 features = features - featureSingleAssetVault - featureLendingProtocol;
+
2258
+
2259 // Auction slot initially is owned by AMM creator, who pays 0 price.
+
2260
+
2261 // Bid 110 tokens. Pay bidMin.
+
2262 testAMM(
+
2263 [&](AMM& ammAlice, Env& env) {
+
2264 ammAlice.deposit(carol, 1'000'000);
+
2265 env(ammAlice.bid({.account = carol, .bidMin = 110}));
+
2266 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110}));
+
2267 // 110 tokens are burned.
+
2268 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'890, 0}));
+
2269 },
+ +
2271 0,
+ +
2273 {features});
+
2274
+
2275 // Bid with min/max when the pay price is less than min.
+
2276 testAMM(
+
2277 [&](AMM& ammAlice, Env& env) {
+
2278 ammAlice.deposit(carol, 1'000'000);
+
2279 // Bid exactly 110. Pay 110 because the pay price is < 110.
+
2280 env(ammAlice.bid({.account = carol, .bidMin = 110, .bidMax = 110}));
+
2281 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110}));
+
2282 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'890}));
+
2283 // Bid exactly 180-200. Pay 180 because the pay price is < 180.
+
2284 env(ammAlice.bid({.account = alice, .bidMin = 180, .bidMax = 200}));
+
2285 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{180}));
+
2286 BEAST_EXPECT(ammAlice.expectBalances(XRP(11'000), USD(11'000), IOUAmount{10'999'814'5, -1}));
+
2287 },
+ +
2289 0,
+ +
2291 {features});
+
2292
+
2293 // Start bid at bidMin 110.
+
2294 testAMM(
+
2295 [&](AMM& ammAlice, Env& env) {
+
2296 ammAlice.deposit(carol, 1'000'000);
+
2297 // Bid, pay bidMin.
+
2298 env(ammAlice.bid({.account = carol, .bidMin = 110}));
+
2299 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110}));
+
2300
+
2301 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
+
2302 ammAlice.deposit(bob, 1'000'000);
+
2303 // Bid, pay the computed price.
+
2304 env(ammAlice.bid({.account = bob}));
+
2305 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount(1155, -1)));
+
2306
+
2307 // Bid bidMax fails because the computed price is higher.
+
2308 env(ammAlice.bid({
+
2309 .account = carol,
+
2310 .bidMax = 120,
2311 }),
-
2313 // Bid Min/MaxSlotPrice succeeds - pay computed price
-
2314 env(ammAlice.bid({.account = carol, .bidMin = 100, .bidMax = 600}));
-
2315 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{127'33875, -5}));
-
2316 },
- -
2318 0,
- -
2320 {features});
-
2321
-
2322 // Slot states.
-
2323 testAMM(
-
2324 [&](AMM& ammAlice, Env& env) {
-
2325 ammAlice.deposit(carol, 1'000'000);
-
2326
-
2327 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
-
2328 ammAlice.deposit(bob, 1'000'000);
-
2329 if (!features[fixAMMv1_3])
-
2330 BEAST_EXPECT(ammAlice.expectBalances(XRP(12'000), USD(12'000), IOUAmount{12'000'000, 0}));
-
2331 else
-
2332 BEAST_EXPECT(
-
2333 ammAlice.expectBalances(XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{12'000'000, 0}));
-
2334
-
2335 // Initial state. Pay bidMin.
-
2336 env(ammAlice.bid({.account = carol, .bidMin = 110})).close();
-
2337 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110}));
+
2313 // Bid MaxSlotPrice succeeds - pay computed price
+
2314 env(ammAlice.bid({.account = carol, .bidMax = 600}));
+
2315 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{121'275, -3}));
+
2316
+
2317 // Bid Min/MaxSlotPrice fails because the computed price is not
+
2318 // in range
+
2319 env(ammAlice.bid({
+
2320 .account = carol,
+
2321 .bidMin = 10,
+
2322 .bidMax = 100,
+
2323 }),
+ +
2325 // Bid Min/MaxSlotPrice succeeds - pay computed price
+
2326 env(ammAlice.bid({.account = carol, .bidMin = 100, .bidMax = 600}));
+
2327 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{127'33875, -5}));
+
2328 },
+ +
2330 0,
+ +
2332 {features});
+
2333
+
2334 // Slot states.
+
2335 testAMM(
+
2336 [&](AMM& ammAlice, Env& env) {
+
2337 ammAlice.deposit(carol, 1'000'000);
2338
-
2339 // 1st Interval after close, price for 0th interval.
-
2340 env(ammAlice.bid({.account = bob}));
- -
2342 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 1, IOUAmount{1'155, -1}));
-
2343
-
2344 // 10th Interval after close, price for 1st interval.
-
2345 env(ammAlice.bid({.account = carol}));
- -
2347 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 10, IOUAmount{121'275, -3}));
-
2348
-
2349 // 20th Interval (expired) after close, price for 10th interval.
-
2350 env(ammAlice.bid({.account = bob}));
- -
2352 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, std::nullopt, IOUAmount{127'33875, -5}));
-
2353
-
2354 // 0 Interval.
-
2355 env(ammAlice.bid({.account = carol, .bidMin = 110})).close();
-
2356 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, std::nullopt, IOUAmount{110}));
-
2357 // ~321.09 tokens burnt on bidding fees.
-
2358 if (!features[fixAMMv1_3])
-
2359 BEAST_EXPECT(ammAlice.expectBalances(XRP(12'000), USD(12'000), IOUAmount{11'999'678'91, -2}));
-
2360 else
-
2361 BEAST_EXPECT(
-
2362 ammAlice.expectBalances(XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{11'999'678'91, -2}));
-
2363 },
- -
2365 0,
- -
2367 {features});
-
2368
-
2369 // Pool's fee 1%. Bid bidMin.
-
2370 // Auction slot owner and auth account trade at discounted fee -
-
2371 // 1/10 of the trading fee.
-
2372 // Other accounts trade at 1% fee.
-
2373 testAMM(
-
2374 [&](AMM& ammAlice, Env& env) {
-
2375 Account const dan("dan");
-
2376 Account const ed("ed");
-
2377 fund(env, gw, {bob, dan, ed}, {USD(20'000)}, Fund::Acct);
-
2378 ammAlice.deposit(bob, 1'000'000);
-
2379 ammAlice.deposit(ed, 1'000'000);
-
2380 ammAlice.deposit(carol, 500'000);
-
2381 ammAlice.deposit(dan, 500'000);
-
2382 auto ammTokens = ammAlice.getLPTokensBalance();
-
2383 env(ammAlice.bid({
-
2384 .account = carol,
-
2385 .bidMin = 120,
-
2386 .authAccounts = {bob, ed},
-
2387 }));
-
2388 auto const slotPrice = IOUAmount{5'200};
-
2389 ammTokens -= slotPrice;
-
2390 BEAST_EXPECT(ammAlice.expectAuctionSlot(100, 0, slotPrice));
-
2391 if (!features[fixAMMv1_3])
-
2392 BEAST_EXPECT(ammAlice.expectBalances(XRP(13'000), USD(13'000), ammTokens));
-
2393 else
-
2394 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{13'000'000'003}, USD(13'000), ammTokens));
-
2395 // Discounted trade
-
2396 for (int i = 0; i < 10; ++i)
-
2397 {
-
2398 auto tokens = ammAlice.deposit(carol, USD(100));
-
2399 ammAlice.withdraw(carol, tokens, USD(0));
-
2400 tokens = ammAlice.deposit(bob, USD(100));
-
2401 ammAlice.withdraw(bob, tokens, USD(0));
-
2402 tokens = ammAlice.deposit(ed, USD(100));
-
2403 ammAlice.withdraw(ed, tokens, USD(0));
-
2404 }
-
2405 // carol, bob, and ed pay ~0.99USD in fees.
-
2406 if (!features[fixAMMv1_1])
-
2407 {
-
2408 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'499'00572620545), -11));
-
2409 BEAST_EXPECT(env.balance(bob, USD) == STAmount(USD, UINT64_C(18'999'00572616195), -11));
-
2410 BEAST_EXPECT(env.balance(ed, USD) == STAmount(USD, UINT64_C(18'999'00572611841), -11));
-
2411 // USD pool is slightly higher because of the fees.
-
2412 BEAST_EXPECT(ammAlice.expectBalances(
-
2413 XRP(13'000), STAmount(USD, UINT64_C(13'002'98282151419), -11), ammTokens));
-
2414 }
-
2415 else
-
2416 {
-
2417 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'499'00572620544), -11));
-
2418 BEAST_EXPECT(env.balance(bob, USD) == STAmount(USD, UINT64_C(18'999'00572616194), -11));
-
2419 BEAST_EXPECT(env.balance(ed, USD) == STAmount(USD, UINT64_C(18'999'0057261184), -10));
-
2420 // USD pool is slightly higher because of the fees.
-
2421 if (!features[fixAMMv1_3])
-
2422 BEAST_EXPECT(ammAlice.expectBalances(
-
2423 XRP(13'000), STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens));
-
2424 else
-
2425 BEAST_EXPECT(ammAlice.expectBalances(
-
2426 XRPAmount{13'000'000'003}, STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens));
-
2427 }
-
2428 ammTokens = ammAlice.getLPTokensBalance();
-
2429 // Trade with the fee
-
2430 for (int i = 0; i < 10; ++i)
-
2431 {
-
2432 auto const tokens = ammAlice.deposit(dan, USD(100));
-
2433 ammAlice.withdraw(dan, tokens, USD(0));
-
2434 }
-
2435 // dan pays ~9.94USD, which is ~10 times more in fees than
-
2436 // carol, bob, ed. the discounted fee is 10 times less
-
2437 // than the trading fee.
-
2438 if (!features[fixAMMv1_1])
-
2439 {
-
2440 BEAST_EXPECT(env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'056722744), -9));
-
2441 // USD pool gains more in dan's fees.
-
2442 BEAST_EXPECT(ammAlice.expectBalances(
-
2443 XRP(13'000), STAmount{USD, UINT64_C(13'012'92609877019), -11}, ammTokens));
-
2444 // Discounted fee payment
-
2445 ammAlice.deposit(carol, USD(100));
-
2446 ammTokens = ammAlice.getLPTokensBalance();
-
2447 BEAST_EXPECT(ammAlice.expectBalances(
-
2448 XRP(13'000), STAmount{USD, UINT64_C(13'112'92609877019), -11}, ammTokens));
-
2449 env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110)));
-
2450 env.close();
-
2451 // carol pays 100000 drops in fees
-
2452 // 99900668XRP swapped in for 100USD
-
2453 BEAST_EXPECT(ammAlice.expectBalances(
-
2454 XRPAmount{13'100'000'668}, STAmount{USD, UINT64_C(13'012'92609877019), -11}, ammTokens));
-
2455 }
-
2456 else
-
2457 {
-
2458 if (!features[fixAMMv1_3])
-
2459 BEAST_EXPECT(env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'05672274399), -11));
-
2460 else
-
2461 BEAST_EXPECT(env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'05672274398), -11));
-
2462 // USD pool gains more in dan's fees.
-
2463 if (!features[fixAMMv1_3])
-
2464 BEAST_EXPECT(ammAlice.expectBalances(
-
2465 XRP(13'000), STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens));
-
2466 else
-
2467 BEAST_EXPECT(ammAlice.expectBalances(
-
2468 XRPAmount{13'000'000'003}, STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens));
-
2469 // Discounted fee payment
-
2470 ammAlice.deposit(carol, USD(100));
-
2471 ammTokens = ammAlice.getLPTokensBalance();
-
2472 if (!features[fixAMMv1_3])
-
2473 BEAST_EXPECT(ammAlice.expectBalances(
-
2474 XRP(13'000), STAmount{USD, UINT64_C(13'112'92609877023), -11}, ammTokens));
-
2475 else
+
2339 fund(env, gw, {bob}, {USD(10'000)}, Fund::Acct);
+
2340 ammAlice.deposit(bob, 1'000'000);
+
2341 if (!features[fixAMMv1_3])
+
2342 BEAST_EXPECT(ammAlice.expectBalances(XRP(12'000), USD(12'000), IOUAmount{12'000'000, 0}));
+
2343 else
+
2344 BEAST_EXPECT(
+
2345 ammAlice.expectBalances(XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{12'000'000, 0}));
+
2346
+
2347 // Initial state. Pay bidMin.
+
2348 env(ammAlice.bid({.account = carol, .bidMin = 110})).close();
+
2349 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{110}));
+
2350
+
2351 // 1st Interval after close, price for 0th interval.
+
2352 env(ammAlice.bid({.account = bob}));
+ +
2354 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 1, IOUAmount{1'155, -1}));
+
2355
+
2356 // 10th Interval after close, price for 1st interval.
+
2357 env(ammAlice.bid({.account = carol}));
+ +
2359 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 10, IOUAmount{121'275, -3}));
+
2360
+
2361 // 20th Interval (expired) after close, price for 10th interval.
+
2362 env(ammAlice.bid({.account = bob}));
+ +
2364 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, std::nullopt, IOUAmount{127'33875, -5}));
+
2365
+
2366 // 0 Interval.
+
2367 env(ammAlice.bid({.account = carol, .bidMin = 110})).close();
+
2368 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, std::nullopt, IOUAmount{110}));
+
2369 // ~321.09 tokens burnt on bidding fees.
+
2370 if (!features[fixAMMv1_3])
+
2371 BEAST_EXPECT(ammAlice.expectBalances(XRP(12'000), USD(12'000), IOUAmount{11'999'678'91, -2}));
+
2372 else
+
2373 BEAST_EXPECT(
+
2374 ammAlice.expectBalances(XRPAmount{12'000'000'001}, USD(12'000), IOUAmount{11'999'678'91, -2}));
+
2375 },
+ +
2377 0,
+ +
2379 {features});
+
2380
+
2381 // Pool's fee 1%. Bid bidMin.
+
2382 // Auction slot owner and auth account trade at discounted fee -
+
2383 // 1/10 of the trading fee.
+
2384 // Other accounts trade at 1% fee.
+
2385 testAMM(
+
2386 [&](AMM& ammAlice, Env& env) {
+
2387 Account const dan("dan");
+
2388 Account const ed("ed");
+
2389 fund(env, gw, {bob, dan, ed}, {USD(20'000)}, Fund::Acct);
+
2390 ammAlice.deposit(bob, 1'000'000);
+
2391 ammAlice.deposit(ed, 1'000'000);
+
2392 ammAlice.deposit(carol, 500'000);
+
2393 ammAlice.deposit(dan, 500'000);
+
2394 auto ammTokens = ammAlice.getLPTokensBalance();
+
2395 env(ammAlice.bid({
+
2396 .account = carol,
+
2397 .bidMin = 120,
+
2398 .authAccounts = {bob, ed},
+
2399 }));
+
2400 auto const slotPrice = IOUAmount{5'200};
+
2401 ammTokens -= slotPrice;
+
2402 BEAST_EXPECT(ammAlice.expectAuctionSlot(100, 0, slotPrice));
+
2403 if (!features[fixAMMv1_3])
+
2404 BEAST_EXPECT(ammAlice.expectBalances(XRP(13'000), USD(13'000), ammTokens));
+
2405 else
+
2406 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{13'000'000'003}, USD(13'000), ammTokens));
+
2407 // Discounted trade
+
2408 for (int i = 0; i < 10; ++i)
+
2409 {
+
2410 auto tokens = ammAlice.deposit(carol, USD(100));
+
2411 ammAlice.withdraw(carol, tokens, USD(0));
+
2412 tokens = ammAlice.deposit(bob, USD(100));
+
2413 ammAlice.withdraw(bob, tokens, USD(0));
+
2414 tokens = ammAlice.deposit(ed, USD(100));
+
2415 ammAlice.withdraw(ed, tokens, USD(0));
+
2416 }
+
2417 // carol, bob, and ed pay ~0.99USD in fees.
+
2418 if (!features[fixAMMv1_1])
+
2419 {
+
2420 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'499'00572620545), -11));
+
2421 BEAST_EXPECT(env.balance(bob, USD) == STAmount(USD, UINT64_C(18'999'00572616195), -11));
+
2422 BEAST_EXPECT(env.balance(ed, USD) == STAmount(USD, UINT64_C(18'999'00572611841), -11));
+
2423 // USD pool is slightly higher because of the fees.
+
2424 BEAST_EXPECT(ammAlice.expectBalances(
+
2425 XRP(13'000), STAmount(USD, UINT64_C(13'002'98282151419), -11), ammTokens));
+
2426 }
+
2427 else
+
2428 {
+
2429 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'499'00572620544), -11));
+
2430 BEAST_EXPECT(env.balance(bob, USD) == STAmount(USD, UINT64_C(18'999'00572616194), -11));
+
2431 BEAST_EXPECT(env.balance(ed, USD) == STAmount(USD, UINT64_C(18'999'0057261184), -10));
+
2432 // USD pool is slightly higher because of the fees.
+
2433 if (!features[fixAMMv1_3])
+
2434 BEAST_EXPECT(ammAlice.expectBalances(
+
2435 XRP(13'000), STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens));
+
2436 else
+
2437 BEAST_EXPECT(ammAlice.expectBalances(
+
2438 XRPAmount{13'000'000'003}, STAmount(USD, UINT64_C(13'002'98282151422), -11), ammTokens));
+
2439 }
+
2440 ammTokens = ammAlice.getLPTokensBalance();
+
2441 // Trade with the fee
+
2442 for (int i = 0; i < 10; ++i)
+
2443 {
+
2444 auto const tokens = ammAlice.deposit(dan, USD(100));
+
2445 ammAlice.withdraw(dan, tokens, USD(0));
+
2446 }
+
2447 // dan pays ~9.94USD, which is ~10 times more in fees than
+
2448 // carol, bob, ed. the discounted fee is 10 times less
+
2449 // than the trading fee.
+
2450 if (!features[fixAMMv1_1])
+
2451 {
+
2452 BEAST_EXPECT(env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'056722744), -9));
+
2453 // USD pool gains more in dan's fees.
+
2454 BEAST_EXPECT(ammAlice.expectBalances(
+
2455 XRP(13'000), STAmount{USD, UINT64_C(13'012'92609877019), -11}, ammTokens));
+
2456 // Discounted fee payment
+
2457 ammAlice.deposit(carol, USD(100));
+
2458 ammTokens = ammAlice.getLPTokensBalance();
+
2459 BEAST_EXPECT(ammAlice.expectBalances(
+
2460 XRP(13'000), STAmount{USD, UINT64_C(13'112'92609877019), -11}, ammTokens));
+
2461 env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110)));
+
2462 env.close();
+
2463 // carol pays 100000 drops in fees
+
2464 // 99900668XRP swapped in for 100USD
+
2465 BEAST_EXPECT(ammAlice.expectBalances(
+
2466 XRPAmount{13'100'000'668}, STAmount{USD, UINT64_C(13'012'92609877019), -11}, ammTokens));
+
2467 }
+
2468 else
+
2469 {
+
2470 if (!features[fixAMMv1_3])
+
2471 BEAST_EXPECT(env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'05672274399), -11));
+
2472 else
+
2473 BEAST_EXPECT(env.balance(dan, USD) == STAmount(USD, UINT64_C(19'490'05672274398), -11));
+
2474 // USD pool gains more in dan's fees.
+
2475 if (!features[fixAMMv1_3])
2476 BEAST_EXPECT(ammAlice.expectBalances(
-
2477 XRPAmount{13'000'000'003}, STAmount{USD, UINT64_C(13'112'92609877024), -11}, ammTokens));
-
2478 env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110)));
-
2479 env.close();
-
2480 // carol pays 100000 drops in fees
-
2481 // 99900668XRP swapped in for 100USD
-
2482 if (!features[fixAMMv1_3])
-
2483 BEAST_EXPECT(ammAlice.expectBalances(
-
2484 XRPAmount{13'100'000'668}, STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens));
-
2485 else
-
2486 BEAST_EXPECT(ammAlice.expectBalances(
-
2487 XRPAmount{13'100'000'671}, STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens));
-
2488 }
-
2489 // Payment with the trading fee
-
2490 env(pay(alice, carol, XRP(100)), path(~XRP), sendmax(USD(110)));
-
2491 env.close();
-
2492 // alice pays ~1.011USD in fees, which is ~10 times more
-
2493 // than carol's fee
-
2494 // 100.099431529USD swapped in for 100XRP
-
2495 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
-
2496 {
-
2497 BEAST_EXPECT(ammAlice.expectBalances(
-
2498 XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'114'03663047264), -11}, ammTokens));
-
2499 }
-
2500 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
-
2501 {
-
2502 BEAST_EXPECT(ammAlice.expectBalances(
-
2503 XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'114'03663047269), -11}, ammTokens));
-
2504 }
-
2505 else
-
2506 {
-
2507 BEAST_EXPECT(ammAlice.expectBalances(
-
2508 XRPAmount{13'000'000'671}, STAmount{USD, UINT64_C(13'114'03663044937), -11}, ammTokens));
-
2509 }
-
2510 // Auction slot expired, no discounted fee
- -
2512 // clock is parent's based
-
2513 env.close();
-
2514 if (!features[fixAMMv1_1])
-
2515 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'399'00572620545), -11));
-
2516 else if (!features[fixAMMv1_3])
-
2517 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'399'00572620544), -11));
-
2518 ammTokens = ammAlice.getLPTokensBalance();
-
2519 for (int i = 0; i < 10; ++i)
-
2520 {
-
2521 auto const tokens = ammAlice.deposit(carol, USD(100));
-
2522 ammAlice.withdraw(carol, tokens, USD(0));
-
2523 }
-
2524 // carol pays ~9.94USD in fees, which is ~10 times more in
-
2525 // trading fees vs discounted fee.
-
2526 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
-
2527 {
-
2528 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'389'06197177128), -11));
-
2529 BEAST_EXPECT(ammAlice.expectBalances(
-
2530 XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'123'98038490681), -11}, ammTokens));
-
2531 }
-
2532 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
-
2533 {
-
2534 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'389'06197177124), -11));
-
2535 BEAST_EXPECT(ammAlice.expectBalances(
-
2536 XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'123'98038490689), -11}, ammTokens));
-
2537 }
-
2538 else
+
2477 XRP(13'000), STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens));
+
2478 else
+
2479 BEAST_EXPECT(ammAlice.expectBalances(
+
2480 XRPAmount{13'000'000'003}, STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens));
+
2481 // Discounted fee payment
+
2482 ammAlice.deposit(carol, USD(100));
+
2483 ammTokens = ammAlice.getLPTokensBalance();
+
2484 if (!features[fixAMMv1_3])
+
2485 BEAST_EXPECT(ammAlice.expectBalances(
+
2486 XRP(13'000), STAmount{USD, UINT64_C(13'112'92609877023), -11}, ammTokens));
+
2487 else
+
2488 BEAST_EXPECT(ammAlice.expectBalances(
+
2489 XRPAmount{13'000'000'003}, STAmount{USD, UINT64_C(13'112'92609877024), -11}, ammTokens));
+
2490 env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110)));
+
2491 env.close();
+
2492 // carol pays 100000 drops in fees
+
2493 // 99900668XRP swapped in for 100USD
+
2494 if (!features[fixAMMv1_3])
+
2495 BEAST_EXPECT(ammAlice.expectBalances(
+
2496 XRPAmount{13'100'000'668}, STAmount{USD, UINT64_C(13'012'92609877023), -11}, ammTokens));
+
2497 else
+
2498 BEAST_EXPECT(ammAlice.expectBalances(
+
2499 XRPAmount{13'100'000'671}, STAmount{USD, UINT64_C(13'012'92609877024), -11}, ammTokens));
+
2500 }
+
2501 // Payment with the trading fee
+
2502 env(pay(alice, carol, XRP(100)), path(~XRP), sendmax(USD(110)));
+
2503 env.close();
+
2504 // alice pays ~1.011USD in fees, which is ~10 times more
+
2505 // than carol's fee
+
2506 // 100.099431529USD swapped in for 100XRP
+
2507 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
+
2508 {
+
2509 BEAST_EXPECT(ammAlice.expectBalances(
+
2510 XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'114'03663047264), -11}, ammTokens));
+
2511 }
+
2512 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
+
2513 {
+
2514 BEAST_EXPECT(ammAlice.expectBalances(
+
2515 XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'114'03663047269), -11}, ammTokens));
+
2516 }
+
2517 else
+
2518 {
+
2519 BEAST_EXPECT(ammAlice.expectBalances(
+
2520 XRPAmount{13'000'000'671}, STAmount{USD, UINT64_C(13'114'03663044937), -11}, ammTokens));
+
2521 }
+
2522 // Auction slot expired, no discounted fee
+ +
2524 // clock is parent's based
+
2525 env.close();
+
2526 if (!features[fixAMMv1_1])
+
2527 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'399'00572620545), -11));
+
2528 else if (!features[fixAMMv1_3])
+
2529 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'399'00572620544), -11));
+
2530 ammTokens = ammAlice.getLPTokensBalance();
+
2531 for (int i = 0; i < 10; ++i)
+
2532 {
+
2533 auto const tokens = ammAlice.deposit(carol, USD(100));
+
2534 ammAlice.withdraw(carol, tokens, USD(0));
+
2535 }
+
2536 // carol pays ~9.94USD in fees, which is ~10 times more in
+
2537 // trading fees vs discounted fee.
+
2538 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
2539 {
-
2540 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'389'06197177129), -11));
+
2540 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'389'06197177128), -11));
2541 BEAST_EXPECT(ammAlice.expectBalances(
-
2542 XRPAmount{13'000'000'671}, STAmount{USD, UINT64_C(13'123'98038488352), -11}, ammTokens));
+
2542 XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'123'98038490681), -11}, ammTokens));
2543 }
-
2544 env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110)));
-
2545 env.close();
-
2546 // carol pays ~1.008XRP in trading fee, which is
-
2547 // ~10 times more than the discounted fee.
-
2548 // 99.815876XRP is swapped in for 100USD
-
2549 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
-
2550 {
-
2551 BEAST_EXPECT(ammAlice.expectBalances(
-
2552 XRPAmount(13'100'824'790), STAmount{USD, UINT64_C(13'023'98038490681), -11}, ammTokens));
-
2553 }
-
2554 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
-
2555 {
-
2556 BEAST_EXPECT(ammAlice.expectBalances(
-
2557 XRPAmount(13'100'824'790), STAmount{USD, UINT64_C(13'023'98038490689), -11}, ammTokens));
-
2558 }
-
2559 else
-
2560 {
-
2561 BEAST_EXPECT(ammAlice.expectBalances(
-
2562 XRPAmount(13'100'824'793), STAmount{USD, UINT64_C(13'023'98038488352), -11}, ammTokens));
-
2563 }
-
2564 },
- -
2566 1'000,
- -
2568 {features});
-
2569
-
2570 // Bid tiny amount
-
2571 testAMM(
-
2572 [&](AMM& ammAlice, Env& env) {
-
2573 // Bid a tiny amount
-
2574 auto const tiny = Number{STAmount::cMinValue, STAmount::cMinOffset};
-
2575 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{tiny}}));
-
2576 // Auction slot purchase price is equal to the tiny amount
-
2577 // since the minSlotPrice is 0 with no trading fee.
-
2578 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny}));
-
2579 // The purchase price is too small to affect the total tokens
-
2580 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens()));
-
2581 // Bid the tiny amount
-
2582 env(ammAlice.bid({
-
2583 .account = alice,
-
2584 .bidMin = IOUAmount{STAmount::cMinValue, STAmount::cMinOffset},
-
2585 }));
-
2586 // Pay slightly higher price
-
2587 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny * Number{105, -2}}));
-
2588 // The purchase price is still too small to affect the total
-
2589 // tokens
-
2590 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens()));
-
2591 },
- -
2593 0,
- -
2595 {features});
-
2596
-
2597 // Reset auth account
-
2598 testAMM(
-
2599 [&](AMM& ammAlice, Env& env) {
-
2600 env(ammAlice.bid({
-
2601 .account = alice,
-
2602 .bidMin = IOUAmount{100},
-
2603 .authAccounts = {carol},
-
2604 }));
-
2605 BEAST_EXPECT(ammAlice.expectAuctionSlot({carol}));
-
2606 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{100}}));
-
2607 BEAST_EXPECT(ammAlice.expectAuctionSlot({}));
-
2608 Account bob("bob");
-
2609 Account dan("dan");
-
2610 fund(env, {bob, dan}, XRP(1'000));
-
2611 env(ammAlice.bid({
-
2612 .account = alice,
-
2613 .bidMin = IOUAmount{100},
-
2614 .authAccounts = {bob, dan},
-
2615 }));
-
2616 BEAST_EXPECT(ammAlice.expectAuctionSlot({bob, dan}));
-
2617 },
- -
2619 0,
- -
2621 {features});
-
2622
-
2623 // Bid all tokens, still own the slot and trade at a discount
-
2624 {
-
2625 Env env(*this, features);
-
2626 fund(env, gw, {alice, bob}, XRP(2'000), {USD(2'000)});
-
2627 AMM amm(env, gw, XRP(1'000), USD(1'010), false, 1'000);
-
2628 auto const lpIssue = amm.lptIssue();
-
2629 env.trust(STAmount{lpIssue, 500}, alice);
-
2630 env.trust(STAmount{lpIssue, 50}, bob);
-
2631 env(pay(gw, alice, STAmount{lpIssue, 500}));
-
2632 env(pay(gw, bob, STAmount{lpIssue, 50}));
-
2633 // Alice doesn't have anymore lp tokens
-
2634 env(amm.bid({.account = alice, .bidMin = 500}));
-
2635 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{500}));
-
2636 BEAST_EXPECT(expectHolding(env, alice, STAmount{lpIssue, 0}));
-
2637 // But trades with the discounted fee since she still owns the slot.
-
2638 // Alice pays 10011 drops in fees
-
2639 env(pay(alice, bob, USD(10)), path(~USD), sendmax(XRP(11)));
-
2640 BEAST_EXPECT(amm.expectBalances(XRPAmount{1'010'010'011}, USD(1'000), IOUAmount{1'004'487'562112089, -9}));
-
2641 // Bob pays the full fee ~0.1USD
-
2642 env(pay(bob, alice, XRP(10)), path(~XRP), sendmax(USD(11)));
-
2643 if (!features[fixAMMv1_1])
-
2644 {
-
2645 BEAST_EXPECT(amm.expectBalances(
-
2646 XRPAmount{1'000'010'011},
-
2647 STAmount{USD, UINT64_C(1'010'10090898081), -11},
-
2648 IOUAmount{1'004'487'562112089, -9}));
-
2649 }
-
2650 else
-
2651 {
-
2652 BEAST_EXPECT(amm.expectBalances(
-
2653 XRPAmount{1'000'010'011},
-
2654 STAmount{USD, UINT64_C(1'010'100908980811), -12},
-
2655 IOUAmount{1'004'487'562112089, -9}));
-
2656 }
-
2657 }
-
2658
-
2659 // preflight tests
-
2660 {
-
2661 Env env(*this, features);
-
2662 auto const baseFee = env.current()->fees().base;
-
2663
-
2664 fund(env, gw, {alice, bob}, XRP(2'000), {USD(2'000)});
-
2665 AMM amm(env, gw, XRP(1'000), USD(1'010), false, 1'000);
-
2666 Json::Value tx = amm.bid({.account = alice, .bidMin = 500});
-
2667
-
2668 {
-
2669 auto jtx = env.jt(tx, seq(1), fee(baseFee));
-
2670 env.app().config().features.erase(featureAMM);
-
2671 PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
-
2672 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
-
2673 BEAST_EXPECT(pf == temDISABLED);
-
2674 env.app().config().features.insert(featureAMM);
-
2675 }
-
2676
-
2677 {
-
2678 auto jtx = env.jt(tx, seq(1), fee(baseFee));
-
2679 jtx.jv["TxnSignature"] = "deadbeef";
-
2680 jtx.stx = env.ust(jtx);
-
2681 PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
-
2682 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
-
2683 BEAST_EXPECT(pf != tesSUCCESS);
-
2684 }
-
2685
-
2686 {
-
2687 auto jtx = env.jt(tx, seq(1), fee(baseFee));
-
2688 jtx.jv["Asset2"]["currency"] = "XRP";
-
2689 jtx.jv["Asset2"].removeMember("issuer");
-
2690 jtx.stx = env.ust(jtx);
-
2691 PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
-
2692 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
-
2693 BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
-
2694 }
-
2695 }
-
2696 }
-
+
2544 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
+
2545 {
+
2546 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'389'06197177124), -11));
+
2547 BEAST_EXPECT(ammAlice.expectBalances(
+
2548 XRPAmount{13'000'000'668}, STAmount{USD, UINT64_C(13'123'98038490689), -11}, ammTokens));
+
2549 }
+
2550 else
+
2551 {
+
2552 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'389'06197177129), -11));
+
2553 BEAST_EXPECT(ammAlice.expectBalances(
+
2554 XRPAmount{13'000'000'671}, STAmount{USD, UINT64_C(13'123'98038488352), -11}, ammTokens));
+
2555 }
+
2556 env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110)));
+
2557 env.close();
+
2558 // carol pays ~1.008XRP in trading fee, which is
+
2559 // ~10 times more than the discounted fee.
+
2560 // 99.815876XRP is swapped in for 100USD
+
2561 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
+
2562 {
+
2563 BEAST_EXPECT(ammAlice.expectBalances(
+
2564 XRPAmount(13'100'824'790), STAmount{USD, UINT64_C(13'023'98038490681), -11}, ammTokens));
+
2565 }
+
2566 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
+
2567 {
+
2568 BEAST_EXPECT(ammAlice.expectBalances(
+
2569 XRPAmount(13'100'824'790), STAmount{USD, UINT64_C(13'023'98038490689), -11}, ammTokens));
+
2570 }
+
2571 else
+
2572 {
+
2573 BEAST_EXPECT(ammAlice.expectBalances(
+
2574 XRPAmount(13'100'824'793), STAmount{USD, UINT64_C(13'023'98038488352), -11}, ammTokens));
+
2575 }
+
2576 },
+ +
2578 1'000,
+ +
2580 {features});
+
2581
+
2582 // Bid tiny amount
+
2583 testAMM(
+
2584 [&](AMM& ammAlice, Env& env) {
+
2585 // Bid a tiny amount
+
2586 auto const tiny = Number{STAmount::cMinValue, STAmount::cMinOffset};
+
2587 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{tiny}}));
+
2588 // Auction slot purchase price is equal to the tiny amount
+
2589 // since the minSlotPrice is 0 with no trading fee.
+
2590 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny}));
+
2591 // The purchase price is too small to affect the total tokens
+
2592 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens()));
+
2593 // Bid the tiny amount
+
2594 env(ammAlice.bid({
+
2595 .account = alice,
+
2596 .bidMin = IOUAmount{STAmount::cMinValue, STAmount::cMinOffset},
+
2597 }));
+
2598 // Pay slightly higher price
+
2599 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny * Number{105, -2}}));
+
2600 // The purchase price is still too small to affect the total
+
2601 // tokens
+
2602 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens()));
+
2603 },
+ +
2605 0,
+ +
2607 {features});
+
2608
+
2609 // Reset auth account
+
2610 testAMM(
+
2611 [&](AMM& ammAlice, Env& env) {
+
2612 env(ammAlice.bid({
+
2613 .account = alice,
+
2614 .bidMin = IOUAmount{100},
+
2615 .authAccounts = {carol},
+
2616 }));
+
2617 BEAST_EXPECT(ammAlice.expectAuctionSlot({carol}));
+
2618 env(ammAlice.bid({.account = alice, .bidMin = IOUAmount{100}}));
+
2619 BEAST_EXPECT(ammAlice.expectAuctionSlot({}));
+
2620 Account bob("bob");
+
2621 Account dan("dan");
+
2622 fund(env, {bob, dan}, XRP(1'000));
+
2623 env(ammAlice.bid({
+
2624 .account = alice,
+
2625 .bidMin = IOUAmount{100},
+
2626 .authAccounts = {bob, dan},
+
2627 }));
+
2628 BEAST_EXPECT(ammAlice.expectAuctionSlot({bob, dan}));
+
2629 },
+ +
2631 0,
+ +
2633 {features});
+
2634
+
2635 // Bid all tokens, still own the slot and trade at a discount
+
2636 {
+
2637 Env env(*this, features);
+
2638 fund(env, gw, {alice, bob}, XRP(2'000), {USD(2'000)});
+
2639 AMM amm(env, gw, XRP(1'000), USD(1'010), false, 1'000);
+
2640 auto const lpIssue = amm.lptIssue();
+
2641 env.trust(STAmount{lpIssue, 500}, alice);
+
2642 env.trust(STAmount{lpIssue, 50}, bob);
+
2643 env(pay(gw, alice, STAmount{lpIssue, 500}));
+
2644 env(pay(gw, bob, STAmount{lpIssue, 50}));
+
2645 // Alice doesn't have anymore lp tokens
+
2646 env(amm.bid({.account = alice, .bidMin = 500}));
+
2647 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{500}));
+
2648 BEAST_EXPECT(expectHolding(env, alice, STAmount{lpIssue, 0}));
+
2649 // But trades with the discounted fee since she still owns the slot.
+
2650 // Alice pays 10011 drops in fees
+
2651 env(pay(alice, bob, USD(10)), path(~USD), sendmax(XRP(11)));
+
2652 BEAST_EXPECT(amm.expectBalances(XRPAmount{1'010'010'011}, USD(1'000), IOUAmount{1'004'487'562112089, -9}));
+
2653 // Bob pays the full fee ~0.1USD
+
2654 env(pay(bob, alice, XRP(10)), path(~XRP), sendmax(USD(11)));
+
2655 if (!features[fixAMMv1_1])
+
2656 {
+
2657 BEAST_EXPECT(amm.expectBalances(
+
2658 XRPAmount{1'000'010'011},
+
2659 STAmount{USD, UINT64_C(1'010'10090898081), -11},
+
2660 IOUAmount{1'004'487'562112089, -9}));
+
2661 }
+
2662 else
+
2663 {
+
2664 BEAST_EXPECT(amm.expectBalances(
+
2665 XRPAmount{1'000'010'011},
+
2666 STAmount{USD, UINT64_C(1'010'100908980811), -12},
+
2667 IOUAmount{1'004'487'562112089, -9}));
+
2668 }
+
2669 }
+
2670
+
2671 // preflight tests
+
2672 {
+
2673 Env env(*this, features);
+
2674 auto const baseFee = env.current()->fees().base;
+
2675
+
2676 fund(env, gw, {alice, bob}, XRP(2'000), {USD(2'000)});
+
2677 AMM amm(env, gw, XRP(1'000), USD(1'010), false, 1'000);
+
2678 Json::Value tx = amm.bid({.account = alice, .bidMin = 500});
+
2679
+
2680 {
+
2681 auto jtx = env.jt(tx, seq(1), fee(baseFee));
+
2682 env.app().config().features.erase(featureAMM);
+
2683 PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
+
2684 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
+
2685 BEAST_EXPECT(pf == temDISABLED);
+
2686 env.app().config().features.insert(featureAMM);
+
2687 }
+
2688
+
2689 {
+
2690 auto jtx = env.jt(tx, seq(1), fee(baseFee));
+
2691 jtx.jv["TxnSignature"] = "deadbeef";
+
2692 jtx.stx = env.ust(jtx);
+
2693 PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
+
2694 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
+
2695 BEAST_EXPECT(pf != tesSUCCESS);
+
2696 }
2697
-
2698 void
-
- -
2700 {
-
2701 testcase("Invalid AMM Payment");
-
2702 using namespace jtx;
-
2703 using namespace std::chrono;
-
2704 using namespace std::literals::chrono_literals;
-
2705
-
2706 // Can't pay into AMM account.
-
2707 // Can't pay out since there is no keys
-
2708 for (auto const& acct : {gw, alice})
-
2709 {
-
2710 {
-
2711 Env env(*this);
-
2712 fund(env, gw, {alice, carol}, XRP(1'000), {USD(100)});
-
2713 // XRP balance is below reserve
-
2714 AMM ammAlice(env, acct, XRP(10), USD(10));
-
2715 // Pay below reserve
-
2716 env(pay(carol, ammAlice.ammAccount(), XRP(10)), ter(tecNO_PERMISSION));
-
2717 // Pay above reserve
-
2718 env(pay(carol, ammAlice.ammAccount(), XRP(300)), ter(tecNO_PERMISSION));
-
2719 // Pay IOU
-
2720 env(pay(carol, ammAlice.ammAccount(), USD(10)), ter(tecNO_PERMISSION));
-
2721 }
+
2698 {
+
2699 auto jtx = env.jt(tx, seq(1), fee(baseFee));
+
2700 jtx.jv["Asset2"]["currency"] = "XRP";
+
2701 jtx.jv["Asset2"].removeMember("issuer");
+
2702 jtx.stx = env.ust(jtx);
+
2703 PreflightContext pfCtx(env.app(), *jtx.stx, env.current()->rules(), tapNONE, env.journal);
+
2704 auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
+
2705 BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
+
2706 }
+
2707 }
+
2708 }
+
+
2709
+
2710 void
+
+ +
2712 {
+
2713 testcase("Invalid AMM Payment");
+
2714 using namespace jtx;
+
2715 using namespace std::chrono;
+
2716 using namespace std::literals::chrono_literals;
+
2717
+
2718 // Can't pay into AMM account.
+
2719 // Can't pay out since there is no keys
+
2720 for (auto const& acct : {gw, alice})
+
2721 {
2722 {
2723 Env env(*this);
-
2724 fund(env, gw, {alice, carol}, XRP(10'000'000), {USD(10'000)});
-
2725 // XRP balance is above reserve
-
2726 AMM ammAlice(env, acct, XRP(1'000'000), USD(100));
+
2724 fund(env, gw, {alice, carol}, XRP(1'000), {USD(100)});
+
2725 // XRP balance is below reserve
+
2726 AMM ammAlice(env, acct, XRP(10), USD(10));
2727 // Pay below reserve
2728 env(pay(carol, ammAlice.ammAccount(), XRP(10)), ter(tecNO_PERMISSION));
2729 // Pay above reserve
-
2730 env(pay(carol, ammAlice.ammAccount(), XRP(1'000'000)), ter(tecNO_PERMISSION));
-
2731 }
-
2732 }
-
2733
-
2734 // Can't pay into AMM with escrow.
-
2735 testAMM([&](AMM& ammAlice, Env& env) {
-
2736 auto const baseFee = env.current()->fees().base;
-
2737 env(escrow::create(carol, ammAlice.ammAccount(), XRP(1)),
-
2738 escrow::condition(escrow::cb1),
-
2739 escrow::finish_time(env.now() + 1s),
-
2740 escrow::cancel_time(env.now() + 2s),
-
2741 fee(baseFee * 150),
- -
2743 });
-
2744
-
2745 // Can't pay into AMM with paychan.
-
2746 testAMM([&](AMM& ammAlice, Env& env) {
-
2747 auto const pk = carol.pk();
-
2748 auto const settleDelay = 100s;
-
2749 NetClock::time_point const cancelAfter = env.current()->header().parentCloseTime + 200s;
-
2750 env(paychan::create(carol, ammAlice.ammAccount(), XRP(1'000), settleDelay, pk, cancelAfter),
- -
2752 });
-
2753
-
2754 // Can't pay into AMM with checks.
-
2755 testAMM([&](AMM& ammAlice, Env& env) {
-
2756 env(check::create(env.master.id(), ammAlice.ammAccount(), XRP(100)), ter(tecNO_PERMISSION));
-
2757 });
-
2758
-
2759 // Pay amounts close to one side of the pool
-
2760 testAMM(
-
2761 [&](AMM& ammAlice, Env& env) {
-
2762 // Can't consume whole pool
-
2763 env(pay(alice, carol, USD(100)), path(~USD), sendmax(XRP(1'000'000'000)), ter(tecPATH_PARTIAL));
-
2764 env(pay(alice, carol, XRP(100)), path(~XRP), sendmax(USD(1'000'000'000)), ter(tecPATH_PARTIAL));
-
2765 // Overflow
-
2766 env(pay(alice, carol, STAmount{USD, UINT64_C(99'999999999), -9}),
-
2767 path(~USD),
-
2768 sendmax(XRP(1'000'000'000)),
- -
2770 env(pay(alice, carol, STAmount{USD, UINT64_C(999'99999999), -8}),
-
2771 path(~USD),
-
2772 sendmax(XRP(1'000'000'000)),
- -
2774 env(pay(alice, carol, STAmount{xrpIssue(), 99'999'999}),
-
2775 path(~XRP),
-
2776 sendmax(USD(1'000'000'000)),
- -
2778 // Sender doesn't have enough funds
-
2779 env(pay(alice, carol, USD(99.99)), path(~USD), sendmax(XRP(1'000'000'000)), ter(tecPATH_PARTIAL));
-
2780 env(pay(alice, carol, STAmount{xrpIssue(), 99'990'000}),
-
2781 path(~XRP),
-
2782 sendmax(USD(1'000'000'000)),
- -
2784 },
-
2785 {{XRP(100), USD(100)}});
-
2786
-
2787 // Globally frozen
-
2788 testAMM([&](AMM& ammAlice, Env& env) {
-
2789 env(fset(gw, asfGlobalFreeze));
-
2790 env.close();
-
2791 env(pay(alice, carol, USD(1)),
-
2792 path(~USD),
- -
2794 sendmax(XRP(10)),
-
2795 ter(tecPATH_DRY));
-
2796 env(pay(alice, carol, XRP(1)),
-
2797 path(~XRP),
- -
2799 sendmax(USD(10)),
-
2800 ter(tecPATH_DRY));
-
2801 });
-
2802
-
2803 // Individually frozen AMM
-
2804 testAMM([&](AMM& ammAlice, Env& env) {
-
2805 env(trust(gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze));
-
2806 env.close();
-
2807 env(pay(alice, carol, USD(1)),
-
2808 path(~USD),
- -
2810 sendmax(XRP(10)),
-
2811 ter(tecPATH_DRY));
-
2812 env(pay(alice, carol, XRP(1)),
-
2813 path(~XRP),
- -
2815 sendmax(USD(10)),
-
2816 ter(tecPATH_DRY));
-
2817 });
-
2818
-
2819 // Individually frozen accounts
-
2820 testAMM([&](AMM& ammAlice, Env& env) {
-
2821 env(trust(gw, carol["USD"](0), tfSetFreeze));
-
2822 env(trust(gw, alice["USD"](0), tfSetFreeze));
-
2823 env.close();
+
2730 env(pay(carol, ammAlice.ammAccount(), XRP(300)), ter(tecNO_PERMISSION));
+
2731 // Pay IOU
+
2732 env(pay(carol, ammAlice.ammAccount(), USD(10)), ter(tecNO_PERMISSION));
+
2733 }
+
2734 {
+
2735 Env env(*this);
+
2736 fund(env, gw, {alice, carol}, XRP(10'000'000), {USD(10'000)});
+
2737 // XRP balance is above reserve
+
2738 AMM ammAlice(env, acct, XRP(1'000'000), USD(100));
+
2739 // Pay below reserve
+
2740 env(pay(carol, ammAlice.ammAccount(), XRP(10)), ter(tecNO_PERMISSION));
+
2741 // Pay above reserve
+
2742 env(pay(carol, ammAlice.ammAccount(), XRP(1'000'000)), ter(tecNO_PERMISSION));
+
2743 }
+
2744 }
+
2745
+
2746 // Can't pay into AMM with escrow.
+
2747 testAMM([&](AMM& ammAlice, Env& env) {
+
2748 auto const baseFee = env.current()->fees().base;
+
2749 env(escrow::create(carol, ammAlice.ammAccount(), XRP(1)),
+
2750 escrow::condition(escrow::cb1),
+
2751 escrow::finish_time(env.now() + 1s),
+
2752 escrow::cancel_time(env.now() + 2s),
+
2753 fee(baseFee * 150),
+ +
2755 });
+
2756
+
2757 // Can't pay into AMM with paychan.
+
2758 testAMM([&](AMM& ammAlice, Env& env) {
+
2759 auto const pk = carol.pk();
+
2760 auto const settleDelay = 100s;
+
2761 NetClock::time_point const cancelAfter = env.current()->header().parentCloseTime + 200s;
+
2762 env(paychan::create(carol, ammAlice.ammAccount(), XRP(1'000), settleDelay, pk, cancelAfter),
+ +
2764 });
+
2765
+
2766 // Can't pay into AMM with checks.
+
2767 testAMM([&](AMM& ammAlice, Env& env) {
+
2768 env(check::create(env.master.id(), ammAlice.ammAccount(), XRP(100)), ter(tecNO_PERMISSION));
+
2769 });
+
2770
+
2771 // Pay amounts close to one side of the pool
+
2772 testAMM(
+
2773 [&](AMM& ammAlice, Env& env) {
+
2774 // Can't consume whole pool
+
2775 env(pay(alice, carol, USD(100)), path(~USD), sendmax(XRP(1'000'000'000)), ter(tecPATH_PARTIAL));
+
2776 env(pay(alice, carol, XRP(100)), path(~XRP), sendmax(USD(1'000'000'000)), ter(tecPATH_PARTIAL));
+
2777 // Overflow
+
2778 env(pay(alice, carol, STAmount{USD, UINT64_C(99'999999999), -9}),
+
2779 path(~USD),
+
2780 sendmax(XRP(1'000'000'000)),
+ +
2782 env(pay(alice, carol, STAmount{USD, UINT64_C(999'99999999), -8}),
+
2783 path(~USD),
+
2784 sendmax(XRP(1'000'000'000)),
+ +
2786 env(pay(alice, carol, STAmount{xrpIssue(), 99'999'999}),
+
2787 path(~XRP),
+
2788 sendmax(USD(1'000'000'000)),
+ +
2790 // Sender doesn't have enough funds
+
2791 env(pay(alice, carol, USD(99.99)), path(~USD), sendmax(XRP(1'000'000'000)), ter(tecPATH_PARTIAL));
+
2792 env(pay(alice, carol, STAmount{xrpIssue(), 99'990'000}),
+
2793 path(~XRP),
+
2794 sendmax(USD(1'000'000'000)),
+ +
2796 },
+
2797 {{XRP(100), USD(100)}});
+
2798
+
2799 // Globally frozen
+
2800 testAMM([&](AMM& ammAlice, Env& env) {
+
2801 env(fset(gw, asfGlobalFreeze));
+
2802 env.close();
+
2803 env(pay(alice, carol, USD(1)),
+
2804 path(~USD),
+ +
2806 sendmax(XRP(10)),
+
2807 ter(tecPATH_DRY));
+
2808 env(pay(alice, carol, XRP(1)),
+
2809 path(~XRP),
+ +
2811 sendmax(USD(10)),
+
2812 ter(tecPATH_DRY));
+
2813 });
+
2814
+
2815 // Individually frozen AMM
+
2816 testAMM([&](AMM& ammAlice, Env& env) {
+
2817 env(trust(gw, STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0}, tfSetFreeze));
+
2818 env.close();
+
2819 env(pay(alice, carol, USD(1)),
+
2820 path(~USD),
+ +
2822 sendmax(XRP(10)),
+
2823 ter(tecPATH_DRY));
2824 env(pay(alice, carol, XRP(1)),
2825 path(~XRP),
-
2826 sendmax(USD(10)),
- + +
2827 sendmax(USD(10)),
2828 ter(tecPATH_DRY));
2829 });
-
2830 }
+
2830
+
2831 // Individually frozen accounts
+
2832 testAMM([&](AMM& ammAlice, Env& env) {
+
2833 env(trust(gw, carol["USD"](0), tfSetFreeze));
+
2834 env(trust(gw, alice["USD"](0), tfSetFreeze));
+
2835 env.close();
+
2836 env(pay(alice, carol, XRP(1)),
+
2837 path(~XRP),
+
2838 sendmax(USD(10)),
+ +
2840 ter(tecPATH_DRY));
+
2841 });
+
2842 }
-
2831
-
2832 void
-
- -
2834 {
-
2835 testcase("Basic Payment");
-
2836 using namespace jtx;
-
2837
-
2838 // For now, just disable SAV entirely, which locks in the small Number
-
2839 // mantissas
-
2840 features = features - featureSingleAssetVault - featureLendingProtocol - featureLendingProtocol;
-
2841
-
2842 // Payment 100USD for 100XRP.
-
2843 // Force one path with tfNoRippleDirect.
-
2844 testAMM(
-
2845 [&](AMM& ammAlice, Env& env) {
-
2846 env.fund(jtx::XRP(30'000), bob);
-
2847 env.close();
-
2848 env(pay(bob, carol, USD(100)), path(~USD), sendmax(XRP(100)), txflags(tfNoRippleDirect));
-
2849 env.close();
-
2850 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
-
2851 // Initial balance 30,000 + 100
-
2852 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
-
2853 // Initial balance 30,000 - 100(sendmax) - 10(tx fee)
-
2854 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
-
2855 },
-
2856 {{XRP(10'000), USD(10'100)}},
-
2857 0,
- -
2859 {features});
-
2860
-
2861 // Payment 100USD for 100XRP, use default path.
-
2862 testAMM(
-
2863 [&](AMM& ammAlice, Env& env) {
-
2864 env.fund(jtx::XRP(30'000), bob);
-
2865 env.close();
-
2866 env(pay(bob, carol, USD(100)), sendmax(XRP(100)));
-
2867 env.close();
-
2868 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
-
2869 // Initial balance 30,000 + 100
-
2870 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
-
2871 // Initial balance 30,000 - 100(sendmax) - 10(tx fee)
-
2872 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
-
2873 },
-
2874 {{XRP(10'000), USD(10'100)}},
-
2875 0,
- -
2877 {features});
-
2878
-
2879 // This payment is identical to above. While it has
-
2880 // both default path and path, activeStrands has one path.
-
2881 testAMM(
-
2882 [&](AMM& ammAlice, Env& env) {
-
2883 env.fund(jtx::XRP(30'000), bob);
-
2884 env.close();
-
2885 env(pay(bob, carol, USD(100)), path(~USD), sendmax(XRP(100)));
-
2886 env.close();
-
2887 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
-
2888 // Initial balance 30,000 + 100
-
2889 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
-
2890 // Initial balance 30,000 - 100(sendmax) - 10(tx fee)
-
2891 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
-
2892 },
-
2893 {{XRP(10'000), USD(10'100)}},
-
2894 0,
- -
2896 {features});
-
2897
-
2898 // Payment with limitQuality set.
-
2899 testAMM(
-
2900 [&](AMM& ammAlice, Env& env) {
-
2901 env.fund(jtx::XRP(30'000), bob);
-
2902 env.close();
-
2903 // Pays 10USD for 10XRP. A larger payment of ~99.11USD/100XRP
-
2904 // would have been sent has it not been for limitQuality.
-
2905 env(pay(bob, carol, USD(100)),
-
2906 path(~USD),
-
2907 sendmax(XRP(100)),
- -
2909 env.close();
-
2910 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'010), USD(10'000), ammAlice.tokens()));
-
2911 // Initial balance 30,000 + 10(limited by limitQuality)
-
2912 BEAST_EXPECT(expectHolding(env, carol, USD(30'010)));
-
2913 // Initial balance 30,000 - 10(limited by limitQuality) - 10(tx
-
2914 // fee)
-
2915 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(10) - txfee(env, 1)));
-
2916
-
2917 // Fails because of limitQuality. Would have sent
-
2918 // ~98.91USD/110XRP has it not been for limitQuality.
-
2919 env(pay(bob, carol, USD(100)),
-
2920 path(~USD),
-
2921 sendmax(XRP(100)),
- -
2923 ter(tecPATH_DRY));
-
2924 env.close();
-
2925 },
-
2926 {{XRP(10'000), USD(10'010)}},
-
2927 0,
- -
2929 {features});
-
2930
-
2931 // Payment with limitQuality and transfer fee set.
-
2932 testAMM(
-
2933 [&](AMM& ammAlice, Env& env) {
-
2934 env(rate(gw, 1.1));
-
2935 env.close();
-
2936 env.fund(jtx::XRP(30'000), bob);
-
2937 env.close();
-
2938 // Pays 10USD for 10XRP. A larger payment of ~99.11USD/100XRP
-
2939 // would have been sent has it not been for limitQuality and
-
2940 // the transfer fee.
-
2941 env(pay(bob, carol, USD(100)),
-
2942 path(~USD),
-
2943 sendmax(XRP(110)),
- -
2945 env.close();
-
2946 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'010), USD(10'000), ammAlice.tokens()));
-
2947 // 10USD - 10% transfer fee
-
2948 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'009'09090909091), -11}));
-
2949 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(10) - txfee(env, 1)));
-
2950 },
-
2951 {{XRP(10'000), USD(10'010)}},
-
2952 0,
- -
2954 {features});
-
2955
-
2956 // Fail when partial payment is not set.
-
2957 testAMM(
-
2958 [&](AMM& ammAlice, Env& env) {
-
2959 env.fund(jtx::XRP(30'000), bob);
-
2960 env.close();
-
2961 env(pay(bob, carol, USD(100)),
-
2962 path(~USD),
-
2963 sendmax(XRP(100)),
- - -
2966 },
-
2967 {{XRP(10'000), USD(10'000)}},
-
2968 0,
- -
2970 {features});
-
2971
-
2972 // Non-default path (with AMM) has a better quality than default path.
-
2973 // The max possible liquidity is taken out of non-default
-
2974 // path ~29.9XRP/29.9EUR, ~29.9EUR/~29.99USD. The rest
-
2975 // is taken from the offer.
-
2976 {
-
2977 Env env(*this, features);
-
2978 fund(env, gw, {alice, carol}, {USD(30'000), EUR(30'000)}, Fund::All);
-
2979 env.close();
-
2980 env.fund(XRP(1'000), bob);
-
2981 env.close();
-
2982 auto ammEUR_XRP = AMM(env, alice, XRP(10'000), EUR(10'000));
-
2983 auto ammUSD_EUR = AMM(env, alice, EUR(10'000), USD(10'000));
-
2984 env(offer(alice, XRP(101), USD(100)), txflags(tfPassive));
-
2985 env.close();
-
2986 env(pay(bob, carol, USD(100)), path(~EUR, ~USD), sendmax(XRP(102)), txflags(tfPartialPayment));
-
2987 env.close();
-
2988 BEAST_EXPECT(ammEUR_XRP.expectBalances(
-
2989 XRPAmount(10'030'082'730), STAmount(EUR, UINT64_C(9'970'007498125468), -12), ammEUR_XRP.tokens()));
-
2990 if (!features[fixAMMv1_1])
-
2991 {
-
2992 BEAST_EXPECT(ammUSD_EUR.expectBalances(
-
2993 STAmount(USD, UINT64_C(9'970'097277662122), -12),
-
2994 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
-
2995 ammUSD_EUR.tokens()));
-
2996
-
2997 // fixReducedOffersV2 changes the expected results slightly.
-
2998 Amounts const expectedAmounts = env.closed()->rules().enabled(fixReducedOffersV2)
-
2999 ? Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233787816), -14)}
-
3000 : Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233787818), -14)};
-
3001
-
3002 BEAST_EXPECT(expectOffers(env, alice, 1, {{expectedAmounts}}));
-
3003 }
-
3004 else
-
3005 {
-
3006 BEAST_EXPECT(ammUSD_EUR.expectBalances(
-
3007 STAmount(USD, UINT64_C(9'970'097277662172), -12),
-
3008 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
-
3009 ammUSD_EUR.tokens()));
-
3010
-
3011 // fixReducedOffersV2 changes the expected results slightly.
-
3012 Amounts const expectedAmounts = env.closed()->rules().enabled(fixReducedOffersV2)
-
3013 ? Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233782839), -14)}
-
3014 : Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233782840), -14)};
-
3015
-
3016 BEAST_EXPECT(expectOffers(env, alice, 1, {{expectedAmounts}}));
-
3017 }
-
3018 // Initial 30,000 + 100
-
3019 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, 30'100}));
-
3020 // Initial 1,000 - 30082730(AMM pool) - 70798251(offer) - 10(tx fee)
-
3021 BEAST_EXPECT(expectLedgerEntryRoot(
-
3022 env, bob, XRP(1'000) - XRPAmount{30'082'730} - XRPAmount{70'798'251} - txfee(env, 1)));
-
3023 }
-
3024
-
3025 // Default path (with AMM) has a better quality than a non-default path.
-
3026 // The max possible liquidity is taken out of default
-
3027 // path ~49XRP/49USD. The rest is taken from the offer.
-
3028 testAMM(
-
3029 [&](AMM& ammAlice, Env& env) {
-
3030 env.fund(XRP(1'000), bob);
-
3031 env.close();
-
3032 env.trust(EUR(2'000), alice);
-
3033 env.close();
-
3034 env(pay(gw, alice, EUR(1'000)));
-
3035 env(offer(alice, XRP(101), EUR(100)), txflags(tfPassive));
-
3036 env.close();
-
3037 env(offer(alice, EUR(100), USD(100)), txflags(tfPassive));
-
3038 env.close();
-
3039 env(pay(bob, carol, USD(100)), path(~EUR, ~USD), sendmax(XRP(102)), txflags(tfPartialPayment));
-
3040 env.close();
-
3041 BEAST_EXPECT(ammAlice.expectBalances(
-
3042 XRPAmount(10'050'238'637), STAmount(USD, UINT64_C(9'950'01249687578), -11), ammAlice.tokens()));
-
3043 BEAST_EXPECT(expectOffers(
-
3044 env,
-
3045 alice,
-
3046 2,
-
3047 {{Amounts{XRPAmount(50'487'378), STAmount(EUR, UINT64_C(49'98750312422), -11)},
-
3048 Amounts{
-
3049 STAmount(EUR, UINT64_C(49'98750312422), -11),
-
3050 STAmount(USD, UINT64_C(49'98750312422), -11)}}}));
-
3051 // Initial 30,000 + 99.99999999999
-
3052 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'099'99999999999), -11}));
-
3053 // Initial 1,000 - 50238637(AMM pool) - 50512622(offer) - 10(tx
-
3054 // fee)
-
3055 BEAST_EXPECT(expectLedgerEntryRoot(
-
3056 env, bob, XRP(1'000) - XRPAmount{50'238'637} - XRPAmount{50'512'622} - txfee(env, 1)));
-
3057 },
- -
3059 0,
- -
3061 {features});
-
3062
-
3063 // Default path with AMM and Order Book offer. AMM is consumed first,
-
3064 // remaining amount is consumed by the offer.
-
3065 testAMM(
-
3066 [&](AMM& ammAlice, Env& env) {
-
3067 fund(env, gw, {bob}, {USD(100)}, Fund::Acct);
-
3068 env.close();
-
3069 env(offer(bob, XRP(100), USD(100)), txflags(tfPassive));
-
3070 env.close();
-
3071 env(pay(alice, carol, USD(200)), sendmax(XRP(200)), txflags(tfPartialPayment));
-
3072 env.close();
-
3073 if (!features[fixAMMv1_1])
-
3074 {
-
3075 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
-
3076 // Initial 30,000 + 200
-
3077 BEAST_EXPECT(expectHolding(env, carol, USD(30'200)));
-
3078 }
-
3079 else
-
3080 {
-
3081 BEAST_EXPECT(ammAlice.expectBalances(
-
3082 XRP(10'100), STAmount(USD, UINT64_C(10'000'00000000001), -11), ammAlice.tokens()));
-
3083 BEAST_EXPECT(expectHolding(env, carol, STAmount(USD, UINT64_C(30'199'99999999999), -11)));
-
3084 }
-
3085 // Initial 30,000 - 10000(AMM pool LP) - 100(AMM offer) -
-
3086 // - 100(offer) - 10(tx fee) - one reserve
-
3087 BEAST_EXPECT(expectLedgerEntryRoot(
-
3088 env, alice, XRP(30'000) - XRP(10'000) - XRP(100) - XRP(100) - ammCrtFee(env) - txfee(env, 1)));
-
3089 BEAST_EXPECT(expectOffers(env, bob, 0));
-
3090 },
-
3091 {{XRP(10'000), USD(10'100)}},
-
3092 0,
- -
3094 {features});
-
3095
-
3096 // Default path with AMM and Order Book offer.
-
3097 // Order Book offer is consumed first.
-
3098 // Remaining amount is consumed by AMM.
-
3099 {
-
3100 Env env(*this, features);
-
3101 fund(env, gw, {alice, bob, carol}, XRP(20'000), {USD(2'000)});
-
3102 env.close();
-
3103 env(offer(bob, XRP(50), USD(150)), txflags(tfPassive));
-
3104 env.close();
-
3105 AMM ammAlice(env, alice, XRP(1'000), USD(1'050));
-
3106 env(pay(alice, carol, USD(200)), sendmax(XRP(200)), txflags(tfPartialPayment));
-
3107 env.close();
-
3108 BEAST_EXPECT(ammAlice.expectBalances(XRP(1'050), USD(1'000), ammAlice.tokens()));
-
3109 BEAST_EXPECT(expectHolding(env, carol, USD(2'200)));
-
3110 BEAST_EXPECT(expectOffers(env, bob, 0));
-
3111 }
-
3112
-
3113 // Offer crossing XRP/IOU
-
3114 testAMM(
-
3115 [&](AMM& ammAlice, Env& env) {
-
3116 fund(env, gw, {bob}, {USD(1'000)}, Fund::Acct);
-
3117 env.close();
-
3118 env(offer(bob, USD(100), XRP(100)));
-
3119 env.close();
-
3120 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
-
3121 // Initial 1,000 + 100
-
3122 BEAST_EXPECT(expectHolding(env, bob, USD(1'100)));
-
3123 // Initial 30,000 - 100(offer) - 10(tx fee)
-
3124 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
-
3125 BEAST_EXPECT(expectOffers(env, bob, 0));
-
3126 },
-
3127 {{XRP(10'000), USD(10'100)}},
-
3128 0,
- -
3130 {features});
-
3131
-
3132 // Offer crossing IOU/IOU and transfer rate
-
3133 // Single path AMM offer
-
3134 testAMM(
-
3135 [&](AMM& ammAlice, Env& env) {
-
3136 env(rate(gw, 1.25));
-
3137 env.close();
-
3138 // This offer succeeds to cross pre- and post-amendment
-
3139 // because the strand's out amount is small enough to match
-
3140 // limitQuality value and limitOut() function in StrandFlow
-
3141 // doesn't require an adjustment to out value.
-
3142 env(offer(carol, EUR(100), GBP(100)));
-
3143 env.close();
-
3144 // No transfer fee
-
3145 BEAST_EXPECT(ammAlice.expectBalances(GBP(1'100), EUR(1'000), ammAlice.tokens()));
-
3146 // Initial 30,000 - 100(offer) - 25% transfer fee
-
3147 BEAST_EXPECT(expectHolding(env, carol, GBP(29'875)));
-
3148 // Initial 30,000 + 100(offer)
-
3149 BEAST_EXPECT(expectHolding(env, carol, EUR(30'100)));
-
3150 BEAST_EXPECT(expectOffers(env, bob, 0));
-
3151 },
-
3152 {{GBP(1'000), EUR(1'100)}},
-
3153 0,
- -
3155 {features});
-
3156 // Single-path AMM offer
-
3157 testAMM(
-
3158 [&](AMM& amm, Env& env) {
-
3159 env(rate(gw, 1.001));
-
3160 env.close();
-
3161 env(offer(carol, XRP(100), USD(55)));
-
3162 env.close();
-
3163 if (!features[fixAMMv1_1])
-
3164 {
-
3165 // Pre-amendment the transfer fee is not taken into
-
3166 // account when calculating the limit out based on
-
3167 // limitQuality. Carol pays 0.1% on the takerGets, which
-
3168 // lowers the overall quality. AMM offer is generated based
-
3169 // on higher limit out, which generates a larger offer
-
3170 // with lower quality. Consequently, the offer fails
-
3171 // to cross.
-
3172 BEAST_EXPECT(amm.expectBalances(XRP(1'000), USD(500), amm.tokens()));
-
3173 BEAST_EXPECT(expectOffers(env, carol, 1, {{Amounts{XRP(100), USD(55)}}}));
-
3174 }
-
3175 else
+
2843
+
2844 void
+
+ +
2846 {
+
2847 testcase("Basic Payment");
+
2848 using namespace jtx;
+
2849
+
2850 // For now, just disable SAV entirely, which locks in the small Number
+
2851 // mantissas
+
2852 features = features - featureSingleAssetVault - featureLendingProtocol - featureLendingProtocol;
+
2853
+
2854 // Payment 100USD for 100XRP.
+
2855 // Force one path with tfNoRippleDirect.
+
2856 testAMM(
+
2857 [&](AMM& ammAlice, Env& env) {
+
2858 env.fund(jtx::XRP(30'000), bob);
+
2859 env.close();
+
2860 env(pay(bob, carol, USD(100)), path(~USD), sendmax(XRP(100)), txflags(tfNoRippleDirect));
+
2861 env.close();
+
2862 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
+
2863 // Initial balance 30,000 + 100
+
2864 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
+
2865 // Initial balance 30,000 - 100(sendmax) - 10(tx fee)
+
2866 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
+
2867 },
+
2868 {{XRP(10'000), USD(10'100)}},
+
2869 0,
+ +
2871 {features});
+
2872
+
2873 // Payment 100USD for 100XRP, use default path.
+
2874 testAMM(
+
2875 [&](AMM& ammAlice, Env& env) {
+
2876 env.fund(jtx::XRP(30'000), bob);
+
2877 env.close();
+
2878 env(pay(bob, carol, USD(100)), sendmax(XRP(100)));
+
2879 env.close();
+
2880 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
+
2881 // Initial balance 30,000 + 100
+
2882 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
+
2883 // Initial balance 30,000 - 100(sendmax) - 10(tx fee)
+
2884 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
+
2885 },
+
2886 {{XRP(10'000), USD(10'100)}},
+
2887 0,
+ +
2889 {features});
+
2890
+
2891 // This payment is identical to above. While it has
+
2892 // both default path and path, activeStrands has one path.
+
2893 testAMM(
+
2894 [&](AMM& ammAlice, Env& env) {
+
2895 env.fund(jtx::XRP(30'000), bob);
+
2896 env.close();
+
2897 env(pay(bob, carol, USD(100)), path(~USD), sendmax(XRP(100)));
+
2898 env.close();
+
2899 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
+
2900 // Initial balance 30,000 + 100
+
2901 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
+
2902 // Initial balance 30,000 - 100(sendmax) - 10(tx fee)
+
2903 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
+
2904 },
+
2905 {{XRP(10'000), USD(10'100)}},
+
2906 0,
+ +
2908 {features});
+
2909
+
2910 // Payment with limitQuality set.
+
2911 testAMM(
+
2912 [&](AMM& ammAlice, Env& env) {
+
2913 env.fund(jtx::XRP(30'000), bob);
+
2914 env.close();
+
2915 // Pays 10USD for 10XRP. A larger payment of ~99.11USD/100XRP
+
2916 // would have been sent has it not been for limitQuality.
+
2917 env(pay(bob, carol, USD(100)),
+
2918 path(~USD),
+
2919 sendmax(XRP(100)),
+ +
2921 env.close();
+
2922 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'010), USD(10'000), ammAlice.tokens()));
+
2923 // Initial balance 30,000 + 10(limited by limitQuality)
+
2924 BEAST_EXPECT(expectHolding(env, carol, USD(30'010)));
+
2925 // Initial balance 30,000 - 10(limited by limitQuality) - 10(tx
+
2926 // fee)
+
2927 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(10) - txfee(env, 1)));
+
2928
+
2929 // Fails because of limitQuality. Would have sent
+
2930 // ~98.91USD/110XRP has it not been for limitQuality.
+
2931 env(pay(bob, carol, USD(100)),
+
2932 path(~USD),
+
2933 sendmax(XRP(100)),
+ +
2935 ter(tecPATH_DRY));
+
2936 env.close();
+
2937 },
+
2938 {{XRP(10'000), USD(10'010)}},
+
2939 0,
+ +
2941 {features});
+
2942
+
2943 // Payment with limitQuality and transfer fee set.
+
2944 testAMM(
+
2945 [&](AMM& ammAlice, Env& env) {
+
2946 env(rate(gw, 1.1));
+
2947 env.close();
+
2948 env.fund(jtx::XRP(30'000), bob);
+
2949 env.close();
+
2950 // Pays 10USD for 10XRP. A larger payment of ~99.11USD/100XRP
+
2951 // would have been sent has it not been for limitQuality and
+
2952 // the transfer fee.
+
2953 env(pay(bob, carol, USD(100)),
+
2954 path(~USD),
+
2955 sendmax(XRP(110)),
+ +
2957 env.close();
+
2958 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'010), USD(10'000), ammAlice.tokens()));
+
2959 // 10USD - 10% transfer fee
+
2960 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'009'09090909091), -11}));
+
2961 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(10) - txfee(env, 1)));
+
2962 },
+
2963 {{XRP(10'000), USD(10'010)}},
+
2964 0,
+ +
2966 {features});
+
2967
+
2968 // Fail when partial payment is not set.
+
2969 testAMM(
+
2970 [&](AMM& ammAlice, Env& env) {
+
2971 env.fund(jtx::XRP(30'000), bob);
+
2972 env.close();
+
2973 env(pay(bob, carol, USD(100)),
+
2974 path(~USD),
+
2975 sendmax(XRP(100)),
+ + +
2978 },
+
2979 {{XRP(10'000), USD(10'000)}},
+
2980 0,
+ +
2982 {features});
+
2983
+
2984 // Non-default path (with AMM) has a better quality than default path.
+
2985 // The max possible liquidity is taken out of non-default
+
2986 // path ~29.9XRP/29.9EUR, ~29.9EUR/~29.99USD. The rest
+
2987 // is taken from the offer.
+
2988 {
+
2989 Env env(*this, features);
+
2990 fund(env, gw, {alice, carol}, {USD(30'000), EUR(30'000)}, Fund::All);
+
2991 env.close();
+
2992 env.fund(XRP(1'000), bob);
+
2993 env.close();
+
2994 auto ammEUR_XRP = AMM(env, alice, XRP(10'000), EUR(10'000));
+
2995 auto ammUSD_EUR = AMM(env, alice, EUR(10'000), USD(10'000));
+
2996 env(offer(alice, XRP(101), USD(100)), txflags(tfPassive));
+
2997 env.close();
+
2998 env(pay(bob, carol, USD(100)), path(~EUR, ~USD), sendmax(XRP(102)), txflags(tfPartialPayment));
+
2999 env.close();
+
3000 BEAST_EXPECT(ammEUR_XRP.expectBalances(
+
3001 XRPAmount(10'030'082'730), STAmount(EUR, UINT64_C(9'970'007498125468), -12), ammEUR_XRP.tokens()));
+
3002 if (!features[fixAMMv1_1])
+
3003 {
+
3004 BEAST_EXPECT(ammUSD_EUR.expectBalances(
+
3005 STAmount(USD, UINT64_C(9'970'097277662122), -12),
+
3006 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
+
3007 ammUSD_EUR.tokens()));
+
3008
+
3009 // fixReducedOffersV2 changes the expected results slightly.
+
3010 Amounts const expectedAmounts = env.closed()->rules().enabled(fixReducedOffersV2)
+
3011 ? Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233787816), -14)}
+
3012 : Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233787818), -14)};
+
3013
+
3014 BEAST_EXPECT(expectOffers(env, alice, 1, {{expectedAmounts}}));
+
3015 }
+
3016 else
+
3017 {
+
3018 BEAST_EXPECT(ammUSD_EUR.expectBalances(
+
3019 STAmount(USD, UINT64_C(9'970'097277662172), -12),
+
3020 STAmount(EUR, UINT64_C(10'029'99250187452), -11),
+
3021 ammUSD_EUR.tokens()));
+
3022
+
3023 // fixReducedOffersV2 changes the expected results slightly.
+
3024 Amounts const expectedAmounts = env.closed()->rules().enabled(fixReducedOffersV2)
+
3025 ? Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233782839), -14)}
+
3026 : Amounts{XRPAmount(30'201'749), STAmount(USD, UINT64_C(29'90272233782840), -14)};
+
3027
+
3028 BEAST_EXPECT(expectOffers(env, alice, 1, {{expectedAmounts}}));
+
3029 }
+
3030 // Initial 30,000 + 100
+
3031 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, 30'100}));
+
3032 // Initial 1,000 - 30082730(AMM pool) - 70798251(offer) - 10(tx fee)
+
3033 BEAST_EXPECT(expectLedgerEntryRoot(
+
3034 env, bob, XRP(1'000) - XRPAmount{30'082'730} - XRPAmount{70'798'251} - txfee(env, 1)));
+
3035 }
+
3036
+
3037 // Default path (with AMM) has a better quality than a non-default path.
+
3038 // The max possible liquidity is taken out of default
+
3039 // path ~49XRP/49USD. The rest is taken from the offer.
+
3040 testAMM(
+
3041 [&](AMM& ammAlice, Env& env) {
+
3042 env.fund(XRP(1'000), bob);
+
3043 env.close();
+
3044 env.trust(EUR(2'000), alice);
+
3045 env.close();
+
3046 env(pay(gw, alice, EUR(1'000)));
+
3047 env(offer(alice, XRP(101), EUR(100)), txflags(tfPassive));
+
3048 env.close();
+
3049 env(offer(alice, EUR(100), USD(100)), txflags(tfPassive));
+
3050 env.close();
+
3051 env(pay(bob, carol, USD(100)), path(~EUR, ~USD), sendmax(XRP(102)), txflags(tfPartialPayment));
+
3052 env.close();
+
3053 BEAST_EXPECT(ammAlice.expectBalances(
+
3054 XRPAmount(10'050'238'637), STAmount(USD, UINT64_C(9'950'01249687578), -11), ammAlice.tokens()));
+
3055 BEAST_EXPECT(expectOffers(
+
3056 env,
+
3057 alice,
+
3058 2,
+
3059 {{Amounts{XRPAmount(50'487'378), STAmount(EUR, UINT64_C(49'98750312422), -11)},
+
3060 Amounts{
+
3061 STAmount(EUR, UINT64_C(49'98750312422), -11),
+
3062 STAmount(USD, UINT64_C(49'98750312422), -11)}}}));
+
3063 // Initial 30,000 + 99.99999999999
+
3064 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'099'99999999999), -11}));
+
3065 // Initial 1,000 - 50238637(AMM pool) - 50512622(offer) - 10(tx
+
3066 // fee)
+
3067 BEAST_EXPECT(expectLedgerEntryRoot(
+
3068 env, bob, XRP(1'000) - XRPAmount{50'238'637} - XRPAmount{50'512'622} - txfee(env, 1)));
+
3069 },
+ +
3071 0,
+ +
3073 {features});
+
3074
+
3075 // Default path with AMM and Order Book offer. AMM is consumed first,
+
3076 // remaining amount is consumed by the offer.
+
3077 testAMM(
+
3078 [&](AMM& ammAlice, Env& env) {
+
3079 fund(env, gw, {bob}, {USD(100)}, Fund::Acct);
+
3080 env.close();
+
3081 env(offer(bob, XRP(100), USD(100)), txflags(tfPassive));
+
3082 env.close();
+
3083 env(pay(alice, carol, USD(200)), sendmax(XRP(200)), txflags(tfPartialPayment));
+
3084 env.close();
+
3085 if (!features[fixAMMv1_1])
+
3086 {
+
3087 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
+
3088 // Initial 30,000 + 200
+
3089 BEAST_EXPECT(expectHolding(env, carol, USD(30'200)));
+
3090 }
+
3091 else
+
3092 {
+
3093 BEAST_EXPECT(ammAlice.expectBalances(
+
3094 XRP(10'100), STAmount(USD, UINT64_C(10'000'00000000001), -11), ammAlice.tokens()));
+
3095 BEAST_EXPECT(expectHolding(env, carol, STAmount(USD, UINT64_C(30'199'99999999999), -11)));
+
3096 }
+
3097 // Initial 30,000 - 10000(AMM pool LP) - 100(AMM offer) -
+
3098 // - 100(offer) - 10(tx fee) - one reserve
+
3099 BEAST_EXPECT(expectLedgerEntryRoot(
+
3100 env, alice, XRP(30'000) - XRP(10'000) - XRP(100) - XRP(100) - ammCrtFee(env) - txfee(env, 1)));
+
3101 BEAST_EXPECT(expectOffers(env, bob, 0));
+
3102 },
+
3103 {{XRP(10'000), USD(10'100)}},
+
3104 0,
+ +
3106 {features});
+
3107
+
3108 // Default path with AMM and Order Book offer.
+
3109 // Order Book offer is consumed first.
+
3110 // Remaining amount is consumed by AMM.
+
3111 {
+
3112 Env env(*this, features);
+
3113 fund(env, gw, {alice, bob, carol}, XRP(20'000), {USD(2'000)});
+
3114 env.close();
+
3115 env(offer(bob, XRP(50), USD(150)), txflags(tfPassive));
+
3116 env.close();
+
3117 AMM ammAlice(env, alice, XRP(1'000), USD(1'050));
+
3118 env(pay(alice, carol, USD(200)), sendmax(XRP(200)), txflags(tfPartialPayment));
+
3119 env.close();
+
3120 BEAST_EXPECT(ammAlice.expectBalances(XRP(1'050), USD(1'000), ammAlice.tokens()));
+
3121 BEAST_EXPECT(expectHolding(env, carol, USD(2'200)));
+
3122 BEAST_EXPECT(expectOffers(env, bob, 0));
+
3123 }
+
3124
+
3125 // Offer crossing XRP/IOU
+
3126 testAMM(
+
3127 [&](AMM& ammAlice, Env& env) {
+
3128 fund(env, gw, {bob}, {USD(1'000)}, Fund::Acct);
+
3129 env.close();
+
3130 env(offer(bob, USD(100), XRP(100)));
+
3131 env.close();
+
3132 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'100), USD(10'000), ammAlice.tokens()));
+
3133 // Initial 1,000 + 100
+
3134 BEAST_EXPECT(expectHolding(env, bob, USD(1'100)));
+
3135 // Initial 30,000 - 100(offer) - 10(tx fee)
+
3136 BEAST_EXPECT(expectLedgerEntryRoot(env, bob, XRP(30'000) - XRP(100) - txfee(env, 1)));
+
3137 BEAST_EXPECT(expectOffers(env, bob, 0));
+
3138 },
+
3139 {{XRP(10'000), USD(10'100)}},
+
3140 0,
+ +
3142 {features});
+
3143
+
3144 // Offer crossing IOU/IOU and transfer rate
+
3145 // Single path AMM offer
+
3146 testAMM(
+
3147 [&](AMM& ammAlice, Env& env) {
+
3148 env(rate(gw, 1.25));
+
3149 env.close();
+
3150 // This offer succeeds to cross pre- and post-amendment
+
3151 // because the strand's out amount is small enough to match
+
3152 // limitQuality value and limitOut() function in StrandFlow
+
3153 // doesn't require an adjustment to out value.
+
3154 env(offer(carol, EUR(100), GBP(100)));
+
3155 env.close();
+
3156 // No transfer fee
+
3157 BEAST_EXPECT(ammAlice.expectBalances(GBP(1'100), EUR(1'000), ammAlice.tokens()));
+
3158 // Initial 30,000 - 100(offer) - 25% transfer fee
+
3159 BEAST_EXPECT(expectHolding(env, carol, GBP(29'875)));
+
3160 // Initial 30,000 + 100(offer)
+
3161 BEAST_EXPECT(expectHolding(env, carol, EUR(30'100)));
+
3162 BEAST_EXPECT(expectOffers(env, bob, 0));
+
3163 },
+
3164 {{GBP(1'000), EUR(1'100)}},
+
3165 0,
+ +
3167 {features});
+
3168 // Single-path AMM offer
+
3169 testAMM(
+
3170 [&](AMM& amm, Env& env) {
+
3171 env(rate(gw, 1.001));
+
3172 env.close();
+
3173 env(offer(carol, XRP(100), USD(55)));
+
3174 env.close();
+
3175 if (!features[fixAMMv1_1])
3176 {
-
3177 // Post-amendment the transfer fee is taken into account
-
3178 // when calculating the limit out based on limitQuality.
-
3179 // This increases the limitQuality and decreases
-
3180 // the limit out. Consequently, AMM offer size is decreased,
-
3181 // and the quality is increased, matching the overall
-
3182 // quality.
-
3183 // AMM offer ~50USD/91XRP
-
3184 BEAST_EXPECT(amm.expectBalances(
-
3185 XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens()));
-
3186 // Offer ~91XRP/49.99USD
-
3187 BEAST_EXPECT(
-
3188 expectOffers(env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}}));
-
3189 // Carol pays 0.1% fee on ~50USD =~ 0.05USD
-
3190 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'949'94999999494), -11));
-
3191 }
-
3192 },
-
3193 {{XRP(1'000), USD(500)}},
-
3194 0,
- -
3196 {features});
-
3197 testAMM(
-
3198 [&](AMM& amm, Env& env) {
-
3199 env(rate(gw, 1.001));
-
3200 env.close();
-
3201 env(offer(carol, XRP(10), USD(5.5)));
-
3202 env.close();
-
3203 if (!features[fixAMMv1_1])
-
3204 {
-
3205 BEAST_EXPECT(
-
3206 amm.expectBalances(XRP(990), STAmount{USD, UINT64_C(505'050505050505), -12}, amm.tokens()));
-
3207 BEAST_EXPECT(expectOffers(env, carol, 0));
-
3208 }
-
3209 else
-
3210 {
-
3211 BEAST_EXPECT(
-
3212 amm.expectBalances(XRP(990), STAmount{USD, UINT64_C(505'0505050505051), -13}, amm.tokens()));
-
3213 BEAST_EXPECT(expectOffers(env, carol, 0));
-
3214 }
-
3215 },
-
3216 {{XRP(1'000), USD(500)}},
-
3217 0,
- -
3219 {features});
-
3220 // Multi-path AMM offer
-
3221 testAMM(
-
3222 [&](AMM& ammAlice, Env& env) {
-
3223 Account const ed("ed");
-
3224 fund(env, gw, {bob, ed}, XRP(30'000), {GBP(2'000), EUR(2'000)}, Fund::Acct);
-
3225 env(rate(gw, 1.25));
-
3226 env.close();
-
3227 // The auto-bridge is worse quality than AMM, is not consumed
-
3228 // first and initially forces multi-path AMM offer generation.
-
3229 // Multi-path AMM offers are consumed until their quality
-
3230 // is less than the auto-bridge offers quality. Auto-bridge
-
3231 // offers are consumed afterward. Then the behavior is
-
3232 // different pre-amendment and post-amendment.
-
3233 env(offer(bob, GBP(10), XRP(10)), txflags(tfPassive));
-
3234 env(offer(ed, XRP(10), EUR(10)), txflags(tfPassive));
-
3235 env.close();
-
3236 env(offer(carol, EUR(100), GBP(100)));
-
3237 env.close();
-
3238 if (!features[fixAMMv1_1])
-
3239 {
-
3240 // After the auto-bridge offers are consumed, single path
-
3241 // AMM offer is generated with the limit out not taking
-
3242 // into consideration the transfer fee. This results
-
3243 // in an overall lower quality offer than the limit quality
-
3244 // and the single path AMM offer fails to consume.
-
3245 // Total consumed ~37.06GBP/39.32EUR
-
3246 BEAST_EXPECT(ammAlice.expectBalances(
-
3247 STAmount{GBP, UINT64_C(1'037'06583722133), -11},
-
3248 STAmount{EUR, UINT64_C(1'060'684828792831), -12},
-
3249 ammAlice.tokens()));
-
3250 // Consumed offer ~49.32EUR/49.32GBP
-
3251 BEAST_EXPECT(expectOffers(
-
3252 env,
-
3253 carol,
-
3254 1,
-
3255 {Amounts{
-
3256 STAmount{EUR, UINT64_C(50'684828792831), -12},
-
3257 STAmount{GBP, UINT64_C(50'684828792831), -12}}}));
-
3258 BEAST_EXPECT(expectOffers(env, bob, 0));
-
3259 BEAST_EXPECT(expectOffers(env, ed, 0));
-
3260
-
3261 // Initial 30,000 - ~47.06(offers = 37.06(AMM) + 10(LOB))
-
3262 // * 1.25
-
3263 // = 58.825 = ~29941.17
-
3264 // carol bought ~72.93EUR at the cost of ~70.68GBP
-
3265 // the offer is partially consumed
-
3266 BEAST_EXPECT(expectHolding(env, carol, STAmount{GBP, UINT64_C(29'941'16770347333), -11}));
-
3267 // Initial 30,000 + ~49.3(offers = 39.3(AMM) + 10(LOB))
-
3268 BEAST_EXPECT(expectHolding(env, carol, STAmount{EUR, UINT64_C(30'049'31517120716), -11}));
-
3269 }
-
3270 else
-
3271 {
-
3272 // After the auto-bridge offers are consumed, single path
-
3273 // AMM offer is generated with the limit out taking
-
3274 // into consideration the transfer fee. This results
-
3275 // in an overall quality offer matching the limit quality
-
3276 // and the single path AMM offer is consumed. More
-
3277 // liquidity is consumed overall in post-amendment.
-
3278 // Total consumed ~60.68GBP/62.93EUR
-
3279 BEAST_EXPECT(ammAlice.expectBalances(
-
3280 STAmount{GBP, UINT64_C(1'060'684828792832), -12},
-
3281 STAmount{EUR, UINT64_C(1'037'06583722134), -11},
-
3282 ammAlice.tokens()));
-
3283 // Consumed offer ~72.93EUR/72.93GBP
-
3284 BEAST_EXPECT(expectOffers(
-
3285 env,
-
3286 carol,
-
3287 1,
-
3288 {Amounts{
-
3289 STAmount{EUR, UINT64_C(27'06583722134028), -14},
-
3290 STAmount{GBP, UINT64_C(27'06583722134028), -14}}}));
-
3291 BEAST_EXPECT(expectOffers(env, bob, 0));
-
3292 BEAST_EXPECT(expectOffers(env, ed, 0));
-
3293
-
3294 // Initial 30,000 - ~70.68(offers = 60.68(AMM) + 10(LOB))
-
3295 // * 1.25
-
3296 // = 88.35 = ~29911.64
-
3297 // carol bought ~72.93EUR at the cost of ~70.68GBP
-
3298 // the offer is partially consumed
-
3299 BEAST_EXPECT(expectHolding(env, carol, STAmount{GBP, UINT64_C(29'911'64396400896), -11}));
-
3300 // Initial 30,000 + ~72.93(offers = 62.93(AMM) + 10(LOB))
-
3301 BEAST_EXPECT(expectHolding(env, carol, STAmount{EUR, UINT64_C(30'072'93416277865), -11}));
-
3302 }
-
3303 // Initial 2000 + 10 = 2010
-
3304 BEAST_EXPECT(expectHolding(env, bob, GBP(2'010)));
-
3305 // Initial 2000 - 10 * 1.25 = 1987.5
-
3306 BEAST_EXPECT(expectHolding(env, ed, EUR(1'987.5)));
-
3307 },
-
3308 {{GBP(1'000), EUR(1'100)}},
-
3309 0,
- -
3311 {features});
-
3312
-
3313 // Payment and transfer fee
-
3314 // Scenario:
-
3315 // Bob sends 125GBP to pay 80EUR to Carol
-
3316 // Payment execution:
-
3317 // bob's 125GBP/1.25 = 100GBP
-
3318 // 100GBP/100EUR AMM offer
-
3319 // 100EUR/1.25 = 80EUR paid to carol
-
3320 testAMM(
-
3321 [&](AMM& ammAlice, Env& env) {
-
3322 fund(env, gw, {bob}, {GBP(200), EUR(200)}, Fund::Acct);
-
3323 env(rate(gw, 1.25));
-
3324 env.close();
-
3325 env(pay(bob, carol, EUR(100)), path(~EUR), sendmax(GBP(125)), txflags(tfPartialPayment));
-
3326 env.close();
-
3327 BEAST_EXPECT(ammAlice.expectBalances(GBP(1'100), EUR(1'000), ammAlice.tokens()));
-
3328 BEAST_EXPECT(expectHolding(env, bob, GBP(75)));
-
3329 BEAST_EXPECT(expectHolding(env, carol, EUR(30'080)));
-
3330 },
-
3331 {{GBP(1'000), EUR(1'100)}},
-
3332 0,
- -
3334 {features});
-
3335
-
3336 // Payment and transfer fee, multiple steps
-
3337 // Scenario:
-
3338 // Dan's offer 200CAN/200GBP
-
3339 // AMM 1000GBP/10125EUR
-
3340 // Ed's offer 200EUR/200USD
-
3341 // Bob sends 195.3125CAN to pay 100USD to Carol
-
3342 // Payment execution:
-
3343 // bob's 195.3125CAN/1.25 = 156.25CAN -> dan's offer
-
3344 // 156.25CAN/156.25GBP 156.25GBP/1.25 = 125GBP -> AMM's offer
-
3345 // 125GBP/125EUR 125EUR/1.25 = 100EUR -> ed's offer
-
3346 // 100EUR/100USD 100USD/1.25 = 80USD paid to carol
-
3347 testAMM(
-
3348 [&](AMM& ammAlice, Env& env) {
-
3349 Account const dan("dan");
-
3350 Account const ed("ed");
-
3351 auto const CAN = gw["CAN"];
-
3352 fund(env, gw, {dan}, {CAN(200), GBP(200)}, Fund::Acct);
-
3353 fund(env, gw, {ed}, {EUR(200), USD(200)}, Fund::Acct);
-
3354 fund(env, gw, {bob}, {CAN(195.3125)}, Fund::Acct);
-
3355 env(trust(carol, USD(100)));
-
3356 env(rate(gw, 1.25));
-
3357 env.close();
-
3358 env(offer(dan, CAN(200), GBP(200)));
-
3359 env(offer(ed, EUR(200), USD(200)));
-
3360 env.close();
-
3361 env(pay(bob, carol, USD(100)),
-
3362 path(~GBP, ~EUR, ~USD),
-
3363 sendmax(CAN(195.3125)),
- -
3365 env.close();
-
3366 BEAST_EXPECT(expectHolding(env, bob, CAN(0)));
-
3367 BEAST_EXPECT(expectHolding(env, dan, CAN(356.25), GBP(43.75)));
-
3368 BEAST_EXPECT(ammAlice.expectBalances(GBP(10'125), EUR(10'000), ammAlice.tokens()));
-
3369 BEAST_EXPECT(expectHolding(env, ed, EUR(300), USD(100)));
-
3370 BEAST_EXPECT(expectHolding(env, carol, USD(80)));
-
3371 },
-
3372 {{GBP(10'000), EUR(10'125)}},
-
3373 0,
- -
3375 {features});
-
3376
-
3377 // Pay amounts close to one side of the pool
-
3378 testAMM(
-
3379 [&](AMM& ammAlice, Env& env) {
-
3380 env(pay(alice, carol, USD(99.99)),
-
3381 path(~USD),
-
3382 sendmax(XRP(1)),
- -
3384 ter(tesSUCCESS));
-
3385 env(pay(alice, carol, USD(100)),
-
3386 path(~USD),
-
3387 sendmax(XRP(1)),
- -
3389 ter(tesSUCCESS));
-
3390 env(pay(alice, carol, XRP(100)),
-
3391 path(~XRP),
-
3392 sendmax(USD(1)),
- -
3394 ter(tesSUCCESS));
-
3395 env(pay(alice, carol, STAmount{xrpIssue(), 99'999'900}),
-
3396 path(~XRP),
-
3397 sendmax(USD(1)),
- -
3399 ter(tesSUCCESS));
-
3400 },
-
3401 {{XRP(100), USD(100)}},
-
3402 0,
- -
3404 {features});
-
3405
-
3406 // Multiple paths/steps
-
3407 {
-
3408 Env env(*this, features);
-
3409 auto const ETH = gw["ETH"];
-
3410 fund(env, gw, {alice}, XRP(100'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
-
3411 fund(env, gw, {carol, bob}, XRP(1'000), {USD(200)}, Fund::Acct);
-
3412 AMM xrp_eur(env, alice, XRP(10'100), EUR(10'000));
-
3413 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
-
3414 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
-
3415 AMM xrp_usd(env, alice, XRP(10'150), USD(10'200));
-
3416 AMM xrp_eth(env, alice, XRP(10'000), ETH(10'100));
-
3417 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
-
3418 AMM eur_usd(env, alice, EUR(10'100), USD(10'000));
-
3419 env(pay(bob, carol, USD(100)),
-
3420 path(~EUR, ~BTC, ~USD),
-
3421 path(~USD),
-
3422 path(~ETH, ~EUR, ~USD),
-
3423 sendmax(XRP(200)));
-
3424 if (!features[fixAMMv1_1])
-
3425 {
-
3426 // XRP-ETH-EUR-USD
-
3427 // This path provides ~26.06USD/26.2XRP
-
3428 BEAST_EXPECT(xrp_eth.expectBalances(
-
3429 XRPAmount(10'026'208'900), STAmount{ETH, UINT64_C(10'073'65779244494), -11}, xrp_eth.tokens()));
-
3430 BEAST_EXPECT(eth_eur.expectBalances(
-
3431 STAmount{ETH, UINT64_C(10'926'34220755506), -11},
-
3432 STAmount{EUR, UINT64_C(10'973'54232078752), -11},
-
3433 eth_eur.tokens()));
-
3434 BEAST_EXPECT(eur_usd.expectBalances(
-
3435 STAmount{EUR, UINT64_C(10'126'45767921248), -11},
-
3436 STAmount{USD, UINT64_C(9'973'93151712086), -11},
-
3437 eur_usd.tokens()));
-
3438 // XRP-USD path
-
3439 // This path provides ~73.9USD/74.1XRP
-
3440 BEAST_EXPECT(xrp_usd.expectBalances(
-
3441 XRPAmount(10'224'106'246), STAmount{USD, UINT64_C(10'126'06848287914), -11}, xrp_usd.tokens()));
-
3442 }
-
3443 else
-
3444 {
-
3445 BEAST_EXPECT(xrp_eth.expectBalances(
-
3446 XRPAmount(10'026'208'900), STAmount{ETH, UINT64_C(10'073'65779244461), -11}, xrp_eth.tokens()));
-
3447 BEAST_EXPECT(eth_eur.expectBalances(
-
3448 STAmount{ETH, UINT64_C(10'926'34220755539), -11},
-
3449 STAmount{EUR, UINT64_C(10'973'5423207872), -10},
-
3450 eth_eur.tokens()));
-
3451 BEAST_EXPECT(eur_usd.expectBalances(
-
3452 STAmount{EUR, UINT64_C(10'126'4576792128), -10},
-
3453 STAmount{USD, UINT64_C(9'973'93151712057), -11},
-
3454 eur_usd.tokens()));
-
3455 // XRP-USD path
-
3456 // This path provides ~73.9USD/74.1XRP
-
3457 BEAST_EXPECT(xrp_usd.expectBalances(
-
3458 XRPAmount(10'224'106'246), STAmount{USD, UINT64_C(10'126'06848287943), -11}, xrp_usd.tokens()));
-
3459 }
-
3460
-
3461 // XRP-EUR-BTC-USD
-
3462 // This path doesn't provide any liquidity due to how
-
3463 // offers are generated in multi-path. Analytical solution
-
3464 // shows a different distribution:
-
3465 // XRP-EUR-BTC-USD 11.6USD/11.64XRP, XRP-USD 60.7USD/60.8XRP,
-
3466 // XRP-ETH-EUR-USD 27.6USD/27.6XRP
-
3467 BEAST_EXPECT(xrp_eur.expectBalances(XRP(10'100), EUR(10'000), xrp_eur.tokens()));
-
3468 BEAST_EXPECT(eur_btc.expectBalances(EUR(10'000), BTC(10'200), eur_btc.tokens()));
-
3469 BEAST_EXPECT(btc_usd.expectBalances(BTC(10'100), USD(10'000), btc_usd.tokens()));
-
3470
-
3471 BEAST_EXPECT(expectHolding(env, carol, USD(300)));
-
3472 }
-
3473
-
3474 // Dependent AMM
-
3475 {
-
3476 Env env(*this, features);
-
3477 auto const ETH = gw["ETH"];
-
3478 fund(env, gw, {alice}, XRP(40'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
-
3479 fund(env, gw, {carol, bob}, XRP(1000), {USD(200)}, Fund::Acct);
-
3480 AMM xrp_eur(env, alice, XRP(10'100), EUR(10'000));
-
3481 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
-
3482 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
-
3483 AMM xrp_eth(env, alice, XRP(10'000), ETH(10'100));
-
3484 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
-
3485 env(pay(bob, carol, USD(100)), path(~EUR, ~BTC, ~USD), path(~ETH, ~EUR, ~BTC, ~USD), sendmax(XRP(200)));
-
3486 if (!features[fixAMMv1_1])
-
3487 {
-
3488 // XRP-EUR-BTC-USD path provides ~17.8USD/~18.7XRP
-
3489 // XRP-ETH-EUR-BTC-USD path provides ~82.2USD/82.4XRP
-
3490 BEAST_EXPECT(xrp_eur.expectBalances(
-
3491 XRPAmount(10'118'738'472), STAmount{EUR, UINT64_C(9'981'544436337968), -12}, xrp_eur.tokens()));
-
3492 BEAST_EXPECT(eur_btc.expectBalances(
-
3493 STAmount{EUR, UINT64_C(10'101'16096785173), -11},
-
3494 STAmount{BTC, UINT64_C(10'097'91426968066), -11},
-
3495 eur_btc.tokens()));
-
3496 BEAST_EXPECT(btc_usd.expectBalances(
-
3497 STAmount{BTC, UINT64_C(10'202'08573031934), -11}, USD(9'900), btc_usd.tokens()));
-
3498 BEAST_EXPECT(xrp_eth.expectBalances(
-
3499 XRPAmount(10'082'446'397), STAmount{ETH, UINT64_C(10'017'41072778012), -11}, xrp_eth.tokens()));
-
3500 BEAST_EXPECT(eth_eur.expectBalances(
-
3501 STAmount{ETH, UINT64_C(10'982'58927221988), -11},
-
3502 STAmount{EUR, UINT64_C(10'917'2945958103), -10},
-
3503 eth_eur.tokens()));
-
3504 }
-
3505 else
-
3506 {
-
3507 BEAST_EXPECT(xrp_eur.expectBalances(
-
3508 XRPAmount(10'118'738'472), STAmount{EUR, UINT64_C(9'981'544436337923), -12}, xrp_eur.tokens()));
-
3509 BEAST_EXPECT(eur_btc.expectBalances(
-
3510 STAmount{EUR, UINT64_C(10'101'16096785188), -11},
-
3511 STAmount{BTC, UINT64_C(10'097'91426968059), -11},
-
3512 eur_btc.tokens()));
-
3513 BEAST_EXPECT(btc_usd.expectBalances(
-
3514 STAmount{BTC, UINT64_C(10'202'08573031941), -11}, USD(9'900), btc_usd.tokens()));
-
3515 BEAST_EXPECT(xrp_eth.expectBalances(
-
3516 XRPAmount(10'082'446'397), STAmount{ETH, UINT64_C(10'017'41072777996), -11}, xrp_eth.tokens()));
-
3517 BEAST_EXPECT(eth_eur.expectBalances(
-
3518 STAmount{ETH, UINT64_C(10'982'58927222004), -11},
-
3519 STAmount{EUR, UINT64_C(10'917'2945958102), -10},
-
3520 eth_eur.tokens()));
-
3521 }
-
3522 BEAST_EXPECT(expectHolding(env, carol, USD(300)));
-
3523 }
-
3524
-
3525 // AMM offers limit
-
3526 // Consuming 30 CLOB offers, results in hitting 30 AMM offers limit.
-
3527 testAMM(
-
3528 [&](AMM& ammAlice, Env& env) {
-
3529 env.fund(XRP(1'000), bob);
-
3530 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
-
3531 env(trust(alice, EUR(200)));
-
3532 for (int i = 0; i < 30; ++i)
-
3533 env(offer(alice, EUR(1.0 + 0.01 * i), XRP(1)));
-
3534 // This is worse quality offer than 30 offers above.
-
3535 // It will not be consumed because of AMM offers limit.
-
3536 env(offer(alice, EUR(140), XRP(100)));
-
3537 env(pay(bob, carol, USD(100)),
-
3538 path(~XRP, ~USD),
-
3539 sendmax(EUR(400)),
- -
3541 if (!features[fixAMMv1_1])
-
3542 {
-
3543 // Carol gets ~29.91USD because of the AMM offers limit
-
3544 BEAST_EXPECT(ammAlice.expectBalances(
-
3545 XRP(10'030), STAmount{USD, UINT64_C(9'970'089730807577), -12}, ammAlice.tokens()));
-
3546 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'029'91026919241), -11}));
-
3547 }
-
3548 else
-
3549 {
-
3550 BEAST_EXPECT(ammAlice.expectBalances(
-
3551 XRP(10'030), STAmount{USD, UINT64_C(9'970'089730807827), -12}, ammAlice.tokens()));
-
3552 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'029'91026919217), -11}));
-
3553 }
-
3554 BEAST_EXPECT(expectOffers(env, alice, 1, {{{EUR(140), XRP(100)}}}));
-
3555 },
- -
3557 0,
- -
3559 {features});
-
3560 // This payment is fulfilled
-
3561 testAMM(
-
3562 [&](AMM& ammAlice, Env& env) {
-
3563 env.fund(XRP(1'000), bob);
-
3564 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
-
3565 env(trust(alice, EUR(200)));
-
3566 for (int i = 0; i < 29; ++i)
-
3567 env(offer(alice, EUR(1.0 + 0.01 * i), XRP(1)));
-
3568 // This is worse quality offer than 30 offers above.
-
3569 // It will not be consumed because of AMM offers limit.
-
3570 env(offer(alice, EUR(140), XRP(100)));
-
3571 env(pay(bob, carol, USD(100)),
-
3572 path(~XRP, ~USD),
-
3573 sendmax(EUR(400)),
- -
3575 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{10'101'010'102}, USD(9'900), ammAlice.tokens()));
-
3576 if (!features[fixAMMv1_1])
-
3577 {
-
3578 // Carol gets ~100USD
-
3579 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'099'99999999999), -11}));
-
3580 }
-
3581 else
-
3582 {
-
3583 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
-
3584 }
-
3585 BEAST_EXPECT(
-
3586 expectOffers(env, alice, 1, {{{STAmount{EUR, UINT64_C(39'1858572), -7}, XRPAmount{27'989'898}}}}));
-
3587 },
- -
3589 0,
- -
3591 {features});
-
3592
-
3593 // Offer crossing with AMM and another offer. AMM has a better
-
3594 // quality and is consumed first.
-
3595 {
-
3596 Env env(*this, features);
-
3597 fund(env, gw, {alice, carol, bob}, XRP(30'000), {USD(30'000)});
-
3598 env(offer(bob, XRP(100), USD(100.001)));
-
3599 AMM ammAlice(env, alice, XRP(10'000), USD(10'100));
-
3600 env(offer(carol, USD(100), XRP(100)));
-
3601 if (!features[fixAMMv1_1])
-
3602 {
-
3603 BEAST_EXPECT(ammAlice.expectBalances(
-
3604 XRPAmount{10'049'825'373}, STAmount{USD, UINT64_C(10'049'92586949302), -11}, ammAlice.tokens()));
-
3605 BEAST_EXPECT(expectOffers(
-
3606 env, bob, 1, {{{XRPAmount{50'074'629}, STAmount{USD, UINT64_C(50'07513050698), -11}}}}));
-
3607 }
-
3608 else
-
3609 {
-
3610 BEAST_EXPECT(ammAlice.expectBalances(
-
3611 XRPAmount{10'049'825'372}, STAmount{USD, UINT64_C(10'049'92587049303), -11}, ammAlice.tokens()));
-
3612 BEAST_EXPECT(expectOffers(
-
3613 env, bob, 1, {{{XRPAmount{50'074'628}, STAmount{USD, UINT64_C(50'07512950697), -11}}}}));
-
3614 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
-
3615 }
-
3616 }
-
3617
-
3618 // Individually frozen account
-
3619 testAMM(
-
3620 [&](AMM& ammAlice, Env& env) {
-
3621 env(trust(gw, carol["USD"](0), tfSetFreeze));
-
3622 env(trust(gw, alice["USD"](0), tfSetFreeze));
-
3623 env.close();
-
3624 env(pay(alice, carol, USD(1)),
-
3625 path(~USD),
-
3626 sendmax(XRP(10)),
- -
3628 ter(tesSUCCESS));
-
3629 },
- -
3631 0,
- -
3633 {features});
-
3634 }
+
3177 // Pre-amendment the transfer fee is not taken into
+
3178 // account when calculating the limit out based on
+
3179 // limitQuality. Carol pays 0.1% on the takerGets, which
+
3180 // lowers the overall quality. AMM offer is generated based
+
3181 // on higher limit out, which generates a larger offer
+
3182 // with lower quality. Consequently, the offer fails
+
3183 // to cross.
+
3184 BEAST_EXPECT(amm.expectBalances(XRP(1'000), USD(500), amm.tokens()));
+
3185 BEAST_EXPECT(expectOffers(env, carol, 1, {{Amounts{XRP(100), USD(55)}}}));
+
3186 }
+
3187 else
+
3188 {
+
3189 // Post-amendment the transfer fee is taken into account
+
3190 // when calculating the limit out based on limitQuality.
+
3191 // This increases the limitQuality and decreases
+
3192 // the limit out. Consequently, AMM offer size is decreased,
+
3193 // and the quality is increased, matching the overall
+
3194 // quality.
+
3195 // AMM offer ~50USD/91XRP
+
3196 BEAST_EXPECT(amm.expectBalances(
+
3197 XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens()));
+
3198 // Offer ~91XRP/49.99USD
+
3199 BEAST_EXPECT(
+
3200 expectOffers(env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}}));
+
3201 // Carol pays 0.1% fee on ~50USD =~ 0.05USD
+
3202 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(29'949'94999999494), -11));
+
3203 }
+
3204 },
+
3205 {{XRP(1'000), USD(500)}},
+
3206 0,
+ +
3208 {features});
+
3209 testAMM(
+
3210 [&](AMM& amm, Env& env) {
+
3211 env(rate(gw, 1.001));
+
3212 env.close();
+
3213 env(offer(carol, XRP(10), USD(5.5)));
+
3214 env.close();
+
3215 if (!features[fixAMMv1_1])
+
3216 {
+
3217 BEAST_EXPECT(
+
3218 amm.expectBalances(XRP(990), STAmount{USD, UINT64_C(505'050505050505), -12}, amm.tokens()));
+
3219 BEAST_EXPECT(expectOffers(env, carol, 0));
+
3220 }
+
3221 else
+
3222 {
+
3223 BEAST_EXPECT(
+
3224 amm.expectBalances(XRP(990), STAmount{USD, UINT64_C(505'0505050505051), -13}, amm.tokens()));
+
3225 BEAST_EXPECT(expectOffers(env, carol, 0));
+
3226 }
+
3227 },
+
3228 {{XRP(1'000), USD(500)}},
+
3229 0,
+ +
3231 {features});
+
3232 // Multi-path AMM offer
+
3233 testAMM(
+
3234 [&](AMM& ammAlice, Env& env) {
+
3235 Account const ed("ed");
+
3236 fund(env, gw, {bob, ed}, XRP(30'000), {GBP(2'000), EUR(2'000)}, Fund::Acct);
+
3237 env(rate(gw, 1.25));
+
3238 env.close();
+
3239 // The auto-bridge is worse quality than AMM, is not consumed
+
3240 // first and initially forces multi-path AMM offer generation.
+
3241 // Multi-path AMM offers are consumed until their quality
+
3242 // is less than the auto-bridge offers quality. Auto-bridge
+
3243 // offers are consumed afterward. Then the behavior is
+
3244 // different pre-amendment and post-amendment.
+
3245 env(offer(bob, GBP(10), XRP(10)), txflags(tfPassive));
+
3246 env(offer(ed, XRP(10), EUR(10)), txflags(tfPassive));
+
3247 env.close();
+
3248 env(offer(carol, EUR(100), GBP(100)));
+
3249 env.close();
+
3250 if (!features[fixAMMv1_1])
+
3251 {
+
3252 // After the auto-bridge offers are consumed, single path
+
3253 // AMM offer is generated with the limit out not taking
+
3254 // into consideration the transfer fee. This results
+
3255 // in an overall lower quality offer than the limit quality
+
3256 // and the single path AMM offer fails to consume.
+
3257 // Total consumed ~37.06GBP/39.32EUR
+
3258 BEAST_EXPECT(ammAlice.expectBalances(
+
3259 STAmount{GBP, UINT64_C(1'037'06583722133), -11},
+
3260 STAmount{EUR, UINT64_C(1'060'684828792831), -12},
+
3261 ammAlice.tokens()));
+
3262 // Consumed offer ~49.32EUR/49.32GBP
+
3263 BEAST_EXPECT(expectOffers(
+
3264 env,
+
3265 carol,
+
3266 1,
+
3267 {Amounts{
+
3268 STAmount{EUR, UINT64_C(50'684828792831), -12},
+
3269 STAmount{GBP, UINT64_C(50'684828792831), -12}}}));
+
3270 BEAST_EXPECT(expectOffers(env, bob, 0));
+
3271 BEAST_EXPECT(expectOffers(env, ed, 0));
+
3272
+
3273 // Initial 30,000 - ~47.06(offers = 37.06(AMM) + 10(LOB))
+
3274 // * 1.25
+
3275 // = 58.825 = ~29941.17
+
3276 // carol bought ~72.93EUR at the cost of ~70.68GBP
+
3277 // the offer is partially consumed
+
3278 BEAST_EXPECT(expectHolding(env, carol, STAmount{GBP, UINT64_C(29'941'16770347333), -11}));
+
3279 // Initial 30,000 + ~49.3(offers = 39.3(AMM) + 10(LOB))
+
3280 BEAST_EXPECT(expectHolding(env, carol, STAmount{EUR, UINT64_C(30'049'31517120716), -11}));
+
3281 }
+
3282 else
+
3283 {
+
3284 // After the auto-bridge offers are consumed, single path
+
3285 // AMM offer is generated with the limit out taking
+
3286 // into consideration the transfer fee. This results
+
3287 // in an overall quality offer matching the limit quality
+
3288 // and the single path AMM offer is consumed. More
+
3289 // liquidity is consumed overall in post-amendment.
+
3290 // Total consumed ~60.68GBP/62.93EUR
+
3291 BEAST_EXPECT(ammAlice.expectBalances(
+
3292 STAmount{GBP, UINT64_C(1'060'684828792832), -12},
+
3293 STAmount{EUR, UINT64_C(1'037'06583722134), -11},
+
3294 ammAlice.tokens()));
+
3295 // Consumed offer ~72.93EUR/72.93GBP
+
3296 BEAST_EXPECT(expectOffers(
+
3297 env,
+
3298 carol,
+
3299 1,
+
3300 {Amounts{
+
3301 STAmount{EUR, UINT64_C(27'06583722134028), -14},
+
3302 STAmount{GBP, UINT64_C(27'06583722134028), -14}}}));
+
3303 BEAST_EXPECT(expectOffers(env, bob, 0));
+
3304 BEAST_EXPECT(expectOffers(env, ed, 0));
+
3305
+
3306 // Initial 30,000 - ~70.68(offers = 60.68(AMM) + 10(LOB))
+
3307 // * 1.25
+
3308 // = 88.35 = ~29911.64
+
3309 // carol bought ~72.93EUR at the cost of ~70.68GBP
+
3310 // the offer is partially consumed
+
3311 BEAST_EXPECT(expectHolding(env, carol, STAmount{GBP, UINT64_C(29'911'64396400896), -11}));
+
3312 // Initial 30,000 + ~72.93(offers = 62.93(AMM) + 10(LOB))
+
3313 BEAST_EXPECT(expectHolding(env, carol, STAmount{EUR, UINT64_C(30'072'93416277865), -11}));
+
3314 }
+
3315 // Initial 2000 + 10 = 2010
+
3316 BEAST_EXPECT(expectHolding(env, bob, GBP(2'010)));
+
3317 // Initial 2000 - 10 * 1.25 = 1987.5
+
3318 BEAST_EXPECT(expectHolding(env, ed, EUR(1'987.5)));
+
3319 },
+
3320 {{GBP(1'000), EUR(1'100)}},
+
3321 0,
+ +
3323 {features});
+
3324
+
3325 // Payment and transfer fee
+
3326 // Scenario:
+
3327 // Bob sends 125GBP to pay 80EUR to Carol
+
3328 // Payment execution:
+
3329 // bob's 125GBP/1.25 = 100GBP
+
3330 // 100GBP/100EUR AMM offer
+
3331 // 100EUR/1.25 = 80EUR paid to carol
+
3332 testAMM(
+
3333 [&](AMM& ammAlice, Env& env) {
+
3334 fund(env, gw, {bob}, {GBP(200), EUR(200)}, Fund::Acct);
+
3335 env(rate(gw, 1.25));
+
3336 env.close();
+
3337 env(pay(bob, carol, EUR(100)), path(~EUR), sendmax(GBP(125)), txflags(tfPartialPayment));
+
3338 env.close();
+
3339 BEAST_EXPECT(ammAlice.expectBalances(GBP(1'100), EUR(1'000), ammAlice.tokens()));
+
3340 BEAST_EXPECT(expectHolding(env, bob, GBP(75)));
+
3341 BEAST_EXPECT(expectHolding(env, carol, EUR(30'080)));
+
3342 },
+
3343 {{GBP(1'000), EUR(1'100)}},
+
3344 0,
+ +
3346 {features});
+
3347
+
3348 // Payment and transfer fee, multiple steps
+
3349 // Scenario:
+
3350 // Dan's offer 200CAN/200GBP
+
3351 // AMM 1000GBP/10125EUR
+
3352 // Ed's offer 200EUR/200USD
+
3353 // Bob sends 195.3125CAN to pay 100USD to Carol
+
3354 // Payment execution:
+
3355 // bob's 195.3125CAN/1.25 = 156.25CAN -> dan's offer
+
3356 // 156.25CAN/156.25GBP 156.25GBP/1.25 = 125GBP -> AMM's offer
+
3357 // 125GBP/125EUR 125EUR/1.25 = 100EUR -> ed's offer
+
3358 // 100EUR/100USD 100USD/1.25 = 80USD paid to carol
+
3359 testAMM(
+
3360 [&](AMM& ammAlice, Env& env) {
+
3361 Account const dan("dan");
+
3362 Account const ed("ed");
+
3363 auto const CAN = gw["CAN"];
+
3364 fund(env, gw, {dan}, {CAN(200), GBP(200)}, Fund::Acct);
+
3365 fund(env, gw, {ed}, {EUR(200), USD(200)}, Fund::Acct);
+
3366 fund(env, gw, {bob}, {CAN(195.3125)}, Fund::Acct);
+
3367 env(trust(carol, USD(100)));
+
3368 env(rate(gw, 1.25));
+
3369 env.close();
+
3370 env(offer(dan, CAN(200), GBP(200)));
+
3371 env(offer(ed, EUR(200), USD(200)));
+
3372 env.close();
+
3373 env(pay(bob, carol, USD(100)),
+
3374 path(~GBP, ~EUR, ~USD),
+
3375 sendmax(CAN(195.3125)),
+ +
3377 env.close();
+
3378 BEAST_EXPECT(expectHolding(env, bob, CAN(0)));
+
3379 BEAST_EXPECT(expectHolding(env, dan, CAN(356.25), GBP(43.75)));
+
3380 BEAST_EXPECT(ammAlice.expectBalances(GBP(10'125), EUR(10'000), ammAlice.tokens()));
+
3381 BEAST_EXPECT(expectHolding(env, ed, EUR(300), USD(100)));
+
3382 BEAST_EXPECT(expectHolding(env, carol, USD(80)));
+
3383 },
+
3384 {{GBP(10'000), EUR(10'125)}},
+
3385 0,
+ +
3387 {features});
+
3388
+
3389 // Pay amounts close to one side of the pool
+
3390 testAMM(
+
3391 [&](AMM& ammAlice, Env& env) {
+
3392 env(pay(alice, carol, USD(99.99)),
+
3393 path(~USD),
+
3394 sendmax(XRP(1)),
+ +
3396 ter(tesSUCCESS));
+
3397 env(pay(alice, carol, USD(100)),
+
3398 path(~USD),
+
3399 sendmax(XRP(1)),
+ +
3401 ter(tesSUCCESS));
+
3402 env(pay(alice, carol, XRP(100)),
+
3403 path(~XRP),
+
3404 sendmax(USD(1)),
+ +
3406 ter(tesSUCCESS));
+
3407 env(pay(alice, carol, STAmount{xrpIssue(), 99'999'900}),
+
3408 path(~XRP),
+
3409 sendmax(USD(1)),
+ +
3411 ter(tesSUCCESS));
+
3412 },
+
3413 {{XRP(100), USD(100)}},
+
3414 0,
+ +
3416 {features});
+
3417
+
3418 // Multiple paths/steps
+
3419 {
+
3420 Env env(*this, features);
+
3421 auto const ETH = gw["ETH"];
+
3422 fund(env, gw, {alice}, XRP(100'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
+
3423 fund(env, gw, {carol, bob}, XRP(1'000), {USD(200)}, Fund::Acct);
+
3424 AMM xrp_eur(env, alice, XRP(10'100), EUR(10'000));
+
3425 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
+
3426 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
+
3427 AMM xrp_usd(env, alice, XRP(10'150), USD(10'200));
+
3428 AMM xrp_eth(env, alice, XRP(10'000), ETH(10'100));
+
3429 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
+
3430 AMM eur_usd(env, alice, EUR(10'100), USD(10'000));
+
3431 env(pay(bob, carol, USD(100)),
+
3432 path(~EUR, ~BTC, ~USD),
+
3433 path(~USD),
+
3434 path(~ETH, ~EUR, ~USD),
+
3435 sendmax(XRP(200)));
+
3436 if (!features[fixAMMv1_1])
+
3437 {
+
3438 // XRP-ETH-EUR-USD
+
3439 // This path provides ~26.06USD/26.2XRP
+
3440 BEAST_EXPECT(xrp_eth.expectBalances(
+
3441 XRPAmount(10'026'208'900), STAmount{ETH, UINT64_C(10'073'65779244494), -11}, xrp_eth.tokens()));
+
3442 BEAST_EXPECT(eth_eur.expectBalances(
+
3443 STAmount{ETH, UINT64_C(10'926'34220755506), -11},
+
3444 STAmount{EUR, UINT64_C(10'973'54232078752), -11},
+
3445 eth_eur.tokens()));
+
3446 BEAST_EXPECT(eur_usd.expectBalances(
+
3447 STAmount{EUR, UINT64_C(10'126'45767921248), -11},
+
3448 STAmount{USD, UINT64_C(9'973'93151712086), -11},
+
3449 eur_usd.tokens()));
+
3450 // XRP-USD path
+
3451 // This path provides ~73.9USD/74.1XRP
+
3452 BEAST_EXPECT(xrp_usd.expectBalances(
+
3453 XRPAmount(10'224'106'246), STAmount{USD, UINT64_C(10'126'06848287914), -11}, xrp_usd.tokens()));
+
3454 }
+
3455 else
+
3456 {
+
3457 BEAST_EXPECT(xrp_eth.expectBalances(
+
3458 XRPAmount(10'026'208'900), STAmount{ETH, UINT64_C(10'073'65779244461), -11}, xrp_eth.tokens()));
+
3459 BEAST_EXPECT(eth_eur.expectBalances(
+
3460 STAmount{ETH, UINT64_C(10'926'34220755539), -11},
+
3461 STAmount{EUR, UINT64_C(10'973'5423207872), -10},
+
3462 eth_eur.tokens()));
+
3463 BEAST_EXPECT(eur_usd.expectBalances(
+
3464 STAmount{EUR, UINT64_C(10'126'4576792128), -10},
+
3465 STAmount{USD, UINT64_C(9'973'93151712057), -11},
+
3466 eur_usd.tokens()));
+
3467 // XRP-USD path
+
3468 // This path provides ~73.9USD/74.1XRP
+
3469 BEAST_EXPECT(xrp_usd.expectBalances(
+
3470 XRPAmount(10'224'106'246), STAmount{USD, UINT64_C(10'126'06848287943), -11}, xrp_usd.tokens()));
+
3471 }
+
3472
+
3473 // XRP-EUR-BTC-USD
+
3474 // This path doesn't provide any liquidity due to how
+
3475 // offers are generated in multi-path. Analytical solution
+
3476 // shows a different distribution:
+
3477 // XRP-EUR-BTC-USD 11.6USD/11.64XRP, XRP-USD 60.7USD/60.8XRP,
+
3478 // XRP-ETH-EUR-USD 27.6USD/27.6XRP
+
3479 BEAST_EXPECT(xrp_eur.expectBalances(XRP(10'100), EUR(10'000), xrp_eur.tokens()));
+
3480 BEAST_EXPECT(eur_btc.expectBalances(EUR(10'000), BTC(10'200), eur_btc.tokens()));
+
3481 BEAST_EXPECT(btc_usd.expectBalances(BTC(10'100), USD(10'000), btc_usd.tokens()));
+
3482
+
3483 BEAST_EXPECT(expectHolding(env, carol, USD(300)));
+
3484 }
+
3485
+
3486 // Dependent AMM
+
3487 {
+
3488 Env env(*this, features);
+
3489 auto const ETH = gw["ETH"];
+
3490 fund(env, gw, {alice}, XRP(40'000), {EUR(50'000), BTC(50'000), ETH(50'000), USD(50'000)});
+
3491 fund(env, gw, {carol, bob}, XRP(1000), {USD(200)}, Fund::Acct);
+
3492 AMM xrp_eur(env, alice, XRP(10'100), EUR(10'000));
+
3493 AMM eur_btc(env, alice, EUR(10'000), BTC(10'200));
+
3494 AMM btc_usd(env, alice, BTC(10'100), USD(10'000));
+
3495 AMM xrp_eth(env, alice, XRP(10'000), ETH(10'100));
+
3496 AMM eth_eur(env, alice, ETH(10'900), EUR(11'000));
+
3497 env(pay(bob, carol, USD(100)), path(~EUR, ~BTC, ~USD), path(~ETH, ~EUR, ~BTC, ~USD), sendmax(XRP(200)));
+
3498 if (!features[fixAMMv1_1])
+
3499 {
+
3500 // XRP-EUR-BTC-USD path provides ~17.8USD/~18.7XRP
+
3501 // XRP-ETH-EUR-BTC-USD path provides ~82.2USD/82.4XRP
+
3502 BEAST_EXPECT(xrp_eur.expectBalances(
+
3503 XRPAmount(10'118'738'472), STAmount{EUR, UINT64_C(9'981'544436337968), -12}, xrp_eur.tokens()));
+
3504 BEAST_EXPECT(eur_btc.expectBalances(
+
3505 STAmount{EUR, UINT64_C(10'101'16096785173), -11},
+
3506 STAmount{BTC, UINT64_C(10'097'91426968066), -11},
+
3507 eur_btc.tokens()));
+
3508 BEAST_EXPECT(btc_usd.expectBalances(
+
3509 STAmount{BTC, UINT64_C(10'202'08573031934), -11}, USD(9'900), btc_usd.tokens()));
+
3510 BEAST_EXPECT(xrp_eth.expectBalances(
+
3511 XRPAmount(10'082'446'397), STAmount{ETH, UINT64_C(10'017'41072778012), -11}, xrp_eth.tokens()));
+
3512 BEAST_EXPECT(eth_eur.expectBalances(
+
3513 STAmount{ETH, UINT64_C(10'982'58927221988), -11},
+
3514 STAmount{EUR, UINT64_C(10'917'2945958103), -10},
+
3515 eth_eur.tokens()));
+
3516 }
+
3517 else
+
3518 {
+
3519 BEAST_EXPECT(xrp_eur.expectBalances(
+
3520 XRPAmount(10'118'738'472), STAmount{EUR, UINT64_C(9'981'544436337923), -12}, xrp_eur.tokens()));
+
3521 BEAST_EXPECT(eur_btc.expectBalances(
+
3522 STAmount{EUR, UINT64_C(10'101'16096785188), -11},
+
3523 STAmount{BTC, UINT64_C(10'097'91426968059), -11},
+
3524 eur_btc.tokens()));
+
3525 BEAST_EXPECT(btc_usd.expectBalances(
+
3526 STAmount{BTC, UINT64_C(10'202'08573031941), -11}, USD(9'900), btc_usd.tokens()));
+
3527 BEAST_EXPECT(xrp_eth.expectBalances(
+
3528 XRPAmount(10'082'446'397), STAmount{ETH, UINT64_C(10'017'41072777996), -11}, xrp_eth.tokens()));
+
3529 BEAST_EXPECT(eth_eur.expectBalances(
+
3530 STAmount{ETH, UINT64_C(10'982'58927222004), -11},
+
3531 STAmount{EUR, UINT64_C(10'917'2945958102), -10},
+
3532 eth_eur.tokens()));
+
3533 }
+
3534 BEAST_EXPECT(expectHolding(env, carol, USD(300)));
+
3535 }
+
3536
+
3537 // AMM offers limit
+
3538 // Consuming 30 CLOB offers, results in hitting 30 AMM offers limit.
+
3539 testAMM(
+
3540 [&](AMM& ammAlice, Env& env) {
+
3541 env.fund(XRP(1'000), bob);
+
3542 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
+
3543 env(trust(alice, EUR(200)));
+
3544 for (int i = 0; i < 30; ++i)
+
3545 env(offer(alice, EUR(1.0 + 0.01 * i), XRP(1)));
+
3546 // This is worse quality offer than 30 offers above.
+
3547 // It will not be consumed because of AMM offers limit.
+
3548 env(offer(alice, EUR(140), XRP(100)));
+
3549 env(pay(bob, carol, USD(100)),
+
3550 path(~XRP, ~USD),
+
3551 sendmax(EUR(400)),
+ +
3553 if (!features[fixAMMv1_1])
+
3554 {
+
3555 // Carol gets ~29.91USD because of the AMM offers limit
+
3556 BEAST_EXPECT(ammAlice.expectBalances(
+
3557 XRP(10'030), STAmount{USD, UINT64_C(9'970'089730807577), -12}, ammAlice.tokens()));
+
3558 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'029'91026919241), -11}));
+
3559 }
+
3560 else
+
3561 {
+
3562 BEAST_EXPECT(ammAlice.expectBalances(
+
3563 XRP(10'030), STAmount{USD, UINT64_C(9'970'089730807827), -12}, ammAlice.tokens()));
+
3564 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'029'91026919217), -11}));
+
3565 }
+
3566 BEAST_EXPECT(expectOffers(env, alice, 1, {{{EUR(140), XRP(100)}}}));
+
3567 },
+ +
3569 0,
+ +
3571 {features});
+
3572 // This payment is fulfilled
+
3573 testAMM(
+
3574 [&](AMM& ammAlice, Env& env) {
+
3575 env.fund(XRP(1'000), bob);
+
3576 fund(env, gw, {bob}, {EUR(400)}, Fund::IOUOnly);
+
3577 env(trust(alice, EUR(200)));
+
3578 for (int i = 0; i < 29; ++i)
+
3579 env(offer(alice, EUR(1.0 + 0.01 * i), XRP(1)));
+
3580 // This is worse quality offer than 30 offers above.
+
3581 // It will not be consumed because of AMM offers limit.
+
3582 env(offer(alice, EUR(140), XRP(100)));
+
3583 env(pay(bob, carol, USD(100)),
+
3584 path(~XRP, ~USD),
+
3585 sendmax(EUR(400)),
+ +
3587 BEAST_EXPECT(ammAlice.expectBalances(XRPAmount{10'101'010'102}, USD(9'900), ammAlice.tokens()));
+
3588 if (!features[fixAMMv1_1])
+
3589 {
+
3590 // Carol gets ~100USD
+
3591 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'099'99999999999), -11}));
+
3592 }
+
3593 else
+
3594 {
+
3595 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
+
3596 }
+
3597 BEAST_EXPECT(
+
3598 expectOffers(env, alice, 1, {{{STAmount{EUR, UINT64_C(39'1858572), -7}, XRPAmount{27'989'898}}}}));
+
3599 },
+ +
3601 0,
+ +
3603 {features});
+
3604
+
3605 // Offer crossing with AMM and another offer. AMM has a better
+
3606 // quality and is consumed first.
+
3607 {
+
3608 Env env(*this, features);
+
3609 fund(env, gw, {alice, carol, bob}, XRP(30'000), {USD(30'000)});
+
3610 env(offer(bob, XRP(100), USD(100.001)));
+
3611 AMM ammAlice(env, alice, XRP(10'000), USD(10'100));
+
3612 env(offer(carol, USD(100), XRP(100)));
+
3613 if (!features[fixAMMv1_1])
+
3614 {
+
3615 BEAST_EXPECT(ammAlice.expectBalances(
+
3616 XRPAmount{10'049'825'373}, STAmount{USD, UINT64_C(10'049'92586949302), -11}, ammAlice.tokens()));
+
3617 BEAST_EXPECT(expectOffers(
+
3618 env, bob, 1, {{{XRPAmount{50'074'629}, STAmount{USD, UINT64_C(50'07513050698), -11}}}}));
+
3619 }
+
3620 else
+
3621 {
+
3622 BEAST_EXPECT(ammAlice.expectBalances(
+
3623 XRPAmount{10'049'825'372}, STAmount{USD, UINT64_C(10'049'92587049303), -11}, ammAlice.tokens()));
+
3624 BEAST_EXPECT(expectOffers(
+
3625 env, bob, 1, {{{XRPAmount{50'074'628}, STAmount{USD, UINT64_C(50'07512950697), -11}}}}));
+
3626 BEAST_EXPECT(expectHolding(env, carol, USD(30'100)));
+
3627 }
+
3628 }
+
3629
+
3630 // Individually frozen account
+
3631 testAMM(
+
3632 [&](AMM& ammAlice, Env& env) {
+
3633 env(trust(gw, carol["USD"](0), tfSetFreeze));
+
3634 env(trust(gw, alice["USD"](0), tfSetFreeze));
+
3635 env.close();
+
3636 env(pay(alice, carol, USD(1)),
+
3637 path(~USD),
+
3638 sendmax(XRP(10)),
+ +
3640 ter(tesSUCCESS));
+
3641 },
+ +
3643 0,
+ +
3645 {features});
+
3646 }
-
3635
-
3636 void
-
- -
3638 {
-
3639 testcase("AMM Tokens");
-
3640 using namespace jtx;
-
3641
-
3642 // Offer crossing with AMM LPTokens and XRP.
-
3643 testAMM([&](AMM& ammAlice, Env& env) {
-
3644 auto const baseFee = env.current()->fees().base.drops();
-
3645 auto const token1 = ammAlice.lptIssue();
-
3646 auto priceXRP = ammAssetOut(
-
3647 STAmount{XRPAmount{10'000'000'000}}, STAmount{token1, 10'000'000}, STAmount{token1, 5'000'000}, 0);
-
3648 // Carol places an order to buy LPTokens
-
3649 env(offer(carol, STAmount{token1, 5'000'000}, priceXRP));
-
3650 // Alice places an order to sell LPTokens
-
3651 env(offer(alice, priceXRP, STAmount{token1, 5'000'000}));
-
3652 // Pool's LPTokens balance doesn't change
-
3653 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000}));
-
3654 // Carol is Liquidity Provider
-
3655 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{5'000'000}));
-
3656 BEAST_EXPECT(ammAlice.expectLPTokens(alice, IOUAmount{5'000'000}));
-
3657 // Carol votes
-
3658 ammAlice.vote(carol, 1'000);
-
3659 BEAST_EXPECT(ammAlice.expectTradingFee(500));
-
3660 ammAlice.vote(carol, 0);
-
3661 BEAST_EXPECT(ammAlice.expectTradingFee(0));
-
3662 // Carol bids
-
3663 env(ammAlice.bid({.account = carol, .bidMin = 100}));
-
3664 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{4'999'900}));
-
3665 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{100}));
-
3666 BEAST_EXPECT(accountBalance(env, carol) == std::to_string(22500000000 - 4 * baseFee));
-
3667 priceXRP = ammAssetOut(
-
3668 STAmount{XRPAmount{10'000'000'000}}, STAmount{token1, 9'999'900}, STAmount{token1, 4'999'900}, 0);
-
3669 // Carol withdraws
-
3670 ammAlice.withdrawAll(carol, XRP(0));
-
3671 BEAST_EXPECT(accountBalance(env, carol) == std::to_string(29999949999 - 5 * baseFee));
-
3672 BEAST_EXPECT(
-
3673 ammAlice.expectBalances(XRPAmount{10'000'000'000} - priceXRP, USD(10'000), IOUAmount{5'000'000}));
-
3674 BEAST_EXPECT(ammAlice.expectLPTokens(alice, IOUAmount{5'000'000}));
-
3675 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
-
3676 });
-
3677
-
3678 // Offer crossing with two AMM LPTokens.
-
3679 testAMM([&](AMM& ammAlice, Env& env) {
-
3680 ammAlice.deposit(carol, 1'000'000);
-
3681 fund(env, gw, {alice, carol}, {EUR(10'000)}, Fund::IOUOnly);
-
3682 AMM ammAlice1(env, alice, XRP(10'000), EUR(10'000));
-
3683 ammAlice1.deposit(carol, 1'000'000);
-
3684 auto const token1 = ammAlice.lptIssue();
-
3685 auto const token2 = ammAlice1.lptIssue();
-
3686 env(offer(alice, STAmount{token1, 100}, STAmount{token2, 100}), txflags(tfPassive));
-
3687 env.close();
-
3688 BEAST_EXPECT(expectOffers(env, alice, 1));
-
3689 env(offer(carol, STAmount{token2, 100}, STAmount{token1, 100}));
-
3690 env.close();
-
3691 BEAST_EXPECT(
-
3692 expectHolding(env, alice, STAmount{token1, 10'000'100}) &&
-
3693 expectHolding(env, alice, STAmount{token2, 9'999'900}));
-
3694 BEAST_EXPECT(
-
3695 expectHolding(env, carol, STAmount{token2, 1'000'100}) &&
-
3696 expectHolding(env, carol, STAmount{token1, 999'900}));
-
3697 BEAST_EXPECT(expectOffers(env, alice, 0) && expectOffers(env, carol, 0));
-
3698 });
-
3699
-
3700 // LPs pay LPTokens directly. Must trust set because the trust line
-
3701 // is checked for the limit, which is 0 in the AMM auto-created
-
3702 // trust line.
-
3703 testAMM([&](AMM& ammAlice, Env& env) {
-
3704 auto const token1 = ammAlice.lptIssue();
-
3705 env.trust(STAmount{token1, 2'000'000}, carol);
-
3706 env.close();
-
3707 ammAlice.deposit(carol, 1'000'000);
-
3708 BEAST_EXPECT(
-
3709 ammAlice.expectLPTokens(alice, IOUAmount{10'000'000, 0}) &&
-
3710 ammAlice.expectLPTokens(carol, IOUAmount{1'000'000, 0}));
-
3711 // Pool balance doesn't change, only tokens moved from
-
3712 // one line to another.
-
3713 env(pay(alice, carol, STAmount{token1, 100}));
-
3714 env.close();
-
3715 BEAST_EXPECT(
-
3716 // Alice initial token1 10,000,000 - 100
-
3717 ammAlice.expectLPTokens(alice, IOUAmount{9'999'900, 0}) &&
-
3718 // Carol initial token1 1,000,000 + 100
-
3719 ammAlice.expectLPTokens(carol, IOUAmount{1'000'100, 0}));
-
3720
-
3721 env.trust(STAmount{token1, 20'000'000}, alice);
-
3722 env.close();
-
3723 env(pay(carol, alice, STAmount{token1, 100}));
-
3724 env.close();
-
3725 // Back to the original balance
-
3726 BEAST_EXPECT(
-
3727 ammAlice.expectLPTokens(alice, IOUAmount{10'000'000, 0}) &&
-
3728 ammAlice.expectLPTokens(carol, IOUAmount{1'000'000, 0}));
-
3729 });
-
3730 }
+
3647
+
3648 void
+
+ +
3650 {
+
3651 testcase("AMM Tokens");
+
3652 using namespace jtx;
+
3653
+
3654 // Offer crossing with AMM LPTokens and XRP.
+
3655 testAMM([&](AMM& ammAlice, Env& env) {
+
3656 auto const baseFee = env.current()->fees().base.drops();
+
3657 auto const token1 = ammAlice.lptIssue();
+
3658 auto priceXRP = ammAssetOut(
+
3659 STAmount{XRPAmount{10'000'000'000}}, STAmount{token1, 10'000'000}, STAmount{token1, 5'000'000}, 0);
+
3660 // Carol places an order to buy LPTokens
+
3661 env(offer(carol, STAmount{token1, 5'000'000}, priceXRP));
+
3662 // Alice places an order to sell LPTokens
+
3663 env(offer(alice, priceXRP, STAmount{token1, 5'000'000}));
+
3664 // Pool's LPTokens balance doesn't change
+
3665 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000}));
+
3666 // Carol is Liquidity Provider
+
3667 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{5'000'000}));
+
3668 BEAST_EXPECT(ammAlice.expectLPTokens(alice, IOUAmount{5'000'000}));
+
3669 // Carol votes
+
3670 ammAlice.vote(carol, 1'000);
+
3671 BEAST_EXPECT(ammAlice.expectTradingFee(500));
+
3672 ammAlice.vote(carol, 0);
+
3673 BEAST_EXPECT(ammAlice.expectTradingFee(0));
+
3674 // Carol bids
+
3675 env(ammAlice.bid({.account = carol, .bidMin = 100}));
+
3676 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{4'999'900}));
+
3677 BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{100}));
+
3678 BEAST_EXPECT(accountBalance(env, carol) == std::to_string(22500000000 - 4 * baseFee));
+
3679 priceXRP = ammAssetOut(
+
3680 STAmount{XRPAmount{10'000'000'000}}, STAmount{token1, 9'999'900}, STAmount{token1, 4'999'900}, 0);
+
3681 // Carol withdraws
+
3682 ammAlice.withdrawAll(carol, XRP(0));
+
3683 BEAST_EXPECT(accountBalance(env, carol) == std::to_string(29999949999 - 5 * baseFee));
+
3684 BEAST_EXPECT(
+
3685 ammAlice.expectBalances(XRPAmount{10'000'000'000} - priceXRP, USD(10'000), IOUAmount{5'000'000}));
+
3686 BEAST_EXPECT(ammAlice.expectLPTokens(alice, IOUAmount{5'000'000}));
+
3687 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
+
3688 });
+
3689
+
3690 // Offer crossing with two AMM LPTokens.
+
3691 testAMM([&](AMM& ammAlice, Env& env) {
+
3692 ammAlice.deposit(carol, 1'000'000);
+
3693 fund(env, gw, {alice, carol}, {EUR(10'000)}, Fund::IOUOnly);
+
3694 AMM ammAlice1(env, alice, XRP(10'000), EUR(10'000));
+
3695 ammAlice1.deposit(carol, 1'000'000);
+
3696 auto const token1 = ammAlice.lptIssue();
+
3697 auto const token2 = ammAlice1.lptIssue();
+
3698 env(offer(alice, STAmount{token1, 100}, STAmount{token2, 100}), txflags(tfPassive));
+
3699 env.close();
+
3700 BEAST_EXPECT(expectOffers(env, alice, 1));
+
3701 env(offer(carol, STAmount{token2, 100}, STAmount{token1, 100}));
+
3702 env.close();
+
3703 BEAST_EXPECT(
+
3704 expectHolding(env, alice, STAmount{token1, 10'000'100}) &&
+
3705 expectHolding(env, alice, STAmount{token2, 9'999'900}));
+
3706 BEAST_EXPECT(
+
3707 expectHolding(env, carol, STAmount{token2, 1'000'100}) &&
+
3708 expectHolding(env, carol, STAmount{token1, 999'900}));
+
3709 BEAST_EXPECT(expectOffers(env, alice, 0) && expectOffers(env, carol, 0));
+
3710 });
+
3711
+
3712 // LPs pay LPTokens directly. Must trust set because the trust line
+
3713 // is checked for the limit, which is 0 in the AMM auto-created
+
3714 // trust line.
+
3715 testAMM([&](AMM& ammAlice, Env& env) {
+
3716 auto const token1 = ammAlice.lptIssue();
+
3717 env.trust(STAmount{token1, 2'000'000}, carol);
+
3718 env.close();
+
3719 ammAlice.deposit(carol, 1'000'000);
+
3720 BEAST_EXPECT(
+
3721 ammAlice.expectLPTokens(alice, IOUAmount{10'000'000, 0}) &&
+
3722 ammAlice.expectLPTokens(carol, IOUAmount{1'000'000, 0}));
+
3723 // Pool balance doesn't change, only tokens moved from
+
3724 // one line to another.
+
3725 env(pay(alice, carol, STAmount{token1, 100}));
+
3726 env.close();
+
3727 BEAST_EXPECT(
+
3728 // Alice initial token1 10,000,000 - 100
+
3729 ammAlice.expectLPTokens(alice, IOUAmount{9'999'900, 0}) &&
+
3730 // Carol initial token1 1,000,000 + 100
+
3731 ammAlice.expectLPTokens(carol, IOUAmount{1'000'100, 0}));
+
3732
+
3733 env.trust(STAmount{token1, 20'000'000}, alice);
+
3734 env.close();
+
3735 env(pay(carol, alice, STAmount{token1, 100}));
+
3736 env.close();
+
3737 // Back to the original balance
+
3738 BEAST_EXPECT(
+
3739 ammAlice.expectLPTokens(alice, IOUAmount{10'000'000, 0}) &&
+
3740 ammAlice.expectLPTokens(carol, IOUAmount{1'000'000, 0}));
+
3741 });
+
3742 }
-
3731
-
3732 void
-
- -
3734 {
-
3735 testcase("Amendment");
- -
3737 FeatureBitset const noAMM{all - featureAMM};
-
3738 FeatureBitset const noNumber{all - fixUniversalNumber};
-
3739 FeatureBitset const noAMMAndNumber{all - featureAMM - fixUniversalNumber};
-
3740 using namespace jtx;
-
3741
-
3742 for (auto const& feature : {noAMM, noNumber, noAMMAndNumber})
-
3743 {
-
3744 Env env{*this, feature};
-
3745 fund(env, gw, {alice}, {USD(1'000)}, Fund::All);
-
3746 AMM amm(env, alice, XRP(1'000), USD(1'000), ter(temDISABLED));
-
3747
-
3748 env(amm.bid({.bidMax = 1000}), ter(temMALFORMED));
-
3749 env(amm.bid({}), ter(temDISABLED));
-
3750 amm.vote(VoteArg{.tfee = 100, .err = ter(temDISABLED)});
-
3751 amm.withdraw(WithdrawArg{.tokens = 100, .err = ter(temMALFORMED)});
-
3752 amm.withdraw(WithdrawArg{.err = ter(temDISABLED)});
-
3753 amm.deposit(DepositArg{.asset1In = USD(100), .err = ter(temDISABLED)});
-
3754 amm.ammDelete(alice, ter(temDISABLED));
-
3755 }
-
3756 }
+
3743
+
3744 void
+
+ +
3746 {
+
3747 testcase("Amendment");
+ +
3749 FeatureBitset const noAMM{all - featureAMM};
+
3750 FeatureBitset const noNumber{all - fixUniversalNumber};
+
3751 FeatureBitset const noAMMAndNumber{all - featureAMM - fixUniversalNumber};
+
3752 using namespace jtx;
+
3753
+
3754 for (auto const& feature : {noAMM, noNumber, noAMMAndNumber})
+
3755 {
+
3756 Env env{*this, feature};
+
3757 fund(env, gw, {alice}, {USD(1'000)}, Fund::All);
+
3758 AMM amm(env, alice, XRP(1'000), USD(1'000), ter(temDISABLED));
+
3759
+
3760 env(amm.bid({.bidMax = 1000}), ter(temMALFORMED));
+
3761 env(amm.bid({}), ter(temDISABLED));
+
3762 amm.vote(VoteArg{.tfee = 100, .err = ter(temDISABLED)});
+
3763 amm.withdraw(WithdrawArg{.tokens = 100, .err = ter(temMALFORMED)});
+
3764 amm.withdraw(WithdrawArg{.err = ter(temDISABLED)});
+
3765 amm.deposit(DepositArg{.asset1In = USD(100), .err = ter(temDISABLED)});
+
3766 amm.ammDelete(alice, ter(temDISABLED));
+
3767 }
+
3768 }
-
3757
-
3758 void
-
- -
3760 {
-
3761 testcase("Flags");
-
3762 using namespace jtx;
-
3763
-
3764 testAMM([&](AMM& ammAlice, Env& env) {
-
3765 auto const info = env.rpc(
-
3766 "json", "account_info", std::string("{\"account\": \"" + to_string(ammAlice.ammAccount()) + "\"}"));
-
3767 auto const flags = info[jss::result][jss::account_data][jss::Flags].asUInt();
- -
3769 });
-
3770 }
+
3769
+
3770 void
+
+ +
3772 {
+
3773 testcase("Flags");
+
3774 using namespace jtx;
+
3775
+
3776 testAMM([&](AMM& ammAlice, Env& env) {
+
3777 auto const info = env.rpc(
+
3778 "json", "account_info", std::string("{\"account\": \"" + to_string(ammAlice.ammAccount()) + "\"}"));
+
3779 auto const flags = info[jss::result][jss::account_data][jss::Flags].asUInt();
+ +
3781 });
+
3782 }
-
3771
-
3772 void
-
- -
3774 {
-
3775 testcase("Rippling");
-
3776 using namespace jtx;
-
3777
-
3778 // Rippling via AMM fails because AMM trust line has 0 limit.
-
3779 // Set up two issuers, A and B. Have each issue a token called TST.
-
3780 // Have another account C hold TST from both issuers,
-
3781 // and create an AMM for this pair.
-
3782 // Have a fourth account, D, create a trust line to the AMM for TST.
-
3783 // Send a payment delivering TST.AMM from C to D, using SendMax in
-
3784 // TST.A (or B) and a path through the AMM account. By normal
-
3785 // rippling rules, this would have caused the AMM's balances
-
3786 // to shift at a 1:1 rate with no fee applied has it not been
-
3787 // for 0 limit.
-
3788 {
-
3789 Env env(*this);
-
3790 auto const A = Account("A");
-
3791 auto const B = Account("B");
-
3792 auto const TSTA = A["TST"];
-
3793 auto const TSTB = B["TST"];
-
3794 auto const C = Account("C");
-
3795 auto const D = Account("D");
-
3796
-
3797 env.fund(XRP(10'000), A);
-
3798 env.fund(XRP(10'000), B);
-
3799 env.fund(XRP(10'000), C);
-
3800 env.fund(XRP(10'000), D);
-
3801
-
3802 env.trust(TSTA(10'000), C);
-
3803 env.trust(TSTB(10'000), C);
-
3804 env(pay(A, C, TSTA(10'000)));
-
3805 env(pay(B, C, TSTB(10'000)));
-
3806 AMM amm(env, C, TSTA(5'000), TSTB(5'000));
-
3807 auto const ammIss = Issue(TSTA.currency, amm.ammAccount());
+
3783
+
3784 void
+
+ +
3786 {
+
3787 testcase("Rippling");
+
3788 using namespace jtx;
+
3789
+
3790 // Rippling via AMM fails because AMM trust line has 0 limit.
+
3791 // Set up two issuers, A and B. Have each issue a token called TST.
+
3792 // Have another account C hold TST from both issuers,
+
3793 // and create an AMM for this pair.
+
3794 // Have a fourth account, D, create a trust line to the AMM for TST.
+
3795 // Send a payment delivering TST.AMM from C to D, using SendMax in
+
3796 // TST.A (or B) and a path through the AMM account. By normal
+
3797 // rippling rules, this would have caused the AMM's balances
+
3798 // to shift at a 1:1 rate with no fee applied has it not been
+
3799 // for 0 limit.
+
3800 {
+
3801 Env env(*this);
+
3802 auto const A = Account("A");
+
3803 auto const B = Account("B");
+
3804 auto const TSTA = A["TST"];
+
3805 auto const TSTB = B["TST"];
+
3806 auto const C = Account("C");
+
3807 auto const D = Account("D");
3808
-
3809 // Can SetTrust only for AMM LP tokens
-
3810 env(trust(D, STAmount{ammIss, 10'000}), ter(tecNO_PERMISSION));
-
3811 env.close();
-
3812
-
3813 // The payment would fail because of above, but check just in case
-
3814 env(pay(C, D, STAmount{ammIss, 10}),
-
3815 sendmax(TSTA(100)),
-
3816 path(amm.ammAccount()),
- -
3818 ter(tecPATH_DRY));
-
3819 }
-
3820 }
+
3809 env.fund(XRP(10'000), A);
+
3810 env.fund(XRP(10'000), B);
+
3811 env.fund(XRP(10'000), C);
+
3812 env.fund(XRP(10'000), D);
+
3813
+
3814 env.trust(TSTA(10'000), C);
+
3815 env.trust(TSTB(10'000), C);
+
3816 env(pay(A, C, TSTA(10'000)));
+
3817 env(pay(B, C, TSTB(10'000)));
+
3818 AMM amm(env, C, TSTA(5'000), TSTB(5'000));
+
3819 auto const ammIss = Issue(TSTA.currency, amm.ammAccount());
+
3820
+
3821 // Can SetTrust only for AMM LP tokens
+
3822 env(trust(D, STAmount{ammIss, 10'000}), ter(tecNO_PERMISSION));
+
3823 env.close();
+
3824
+
3825 // The payment would fail because of above, but check just in case
+
3826 env(pay(C, D, STAmount{ammIss, 10}),
+
3827 sendmax(TSTA(100)),
+
3828 path(amm.ammAccount()),
+ +
3830 ter(tecPATH_DRY));
+
3831 }
+
3832 }
-
3821
-
3822 void
-
- -
3824 {
-
3825 testcase("AMMAndCLOB, offer quality change");
-
3826 using namespace jtx;
-
3827 auto const gw = Account("gw");
-
3828 auto const TST = gw["TST"];
-
3829 auto const LP1 = Account("LP1");
-
3830 auto const LP2 = Account("LP2");
-
3831
-
3832 auto prep = [&](auto const& offerCb, auto const& expectCb) {
-
3833 Env env(*this, features);
-
3834 env.fund(XRP(30'000'000'000), gw);
-
3835 env(offer(gw, XRP(11'500'000'000), TST(1'000'000'000)));
-
3836
-
3837 env.fund(XRP(10'000), LP1);
-
3838 env.fund(XRP(10'000), LP2);
-
3839 env(offer(LP1, TST(25), XRPAmount(287'500'000)));
-
3840
-
3841 // Either AMM or CLOB offer
-
3842 offerCb(env);
+
3833
+
3834 void
+
+ +
3836 {
+
3837 testcase("AMMAndCLOB, offer quality change");
+
3838 using namespace jtx;
+
3839 auto const gw = Account("gw");
+
3840 auto const TST = gw["TST"];
+
3841 auto const LP1 = Account("LP1");
+
3842 auto const LP2 = Account("LP2");
3843
-
3844 env(offer(LP2, TST(25), XRPAmount(287'500'000)));
-
3845
-
3846 expectCb(env);
-
3847 };
+
3844 auto prep = [&](auto const& offerCb, auto const& expectCb) {
+
3845 Env env(*this, features);
+
3846 env.fund(XRP(30'000'000'000), gw);
+
3847 env(offer(gw, XRP(11'500'000'000), TST(1'000'000'000)));
3848
-
3849 // If we replace AMM with an equivalent CLOB offer, which AMM generates
-
3850 // when it is consumed, then the result must be equivalent, too.
-
3851 std::string lp2TSTBalance;
-
3852 std::string lp2TakerGets;
-
3853 std::string lp2TakerPays;
-
3854 // Execute with AMM first
-
3855 prep(
-
3856 [&](Env& env) { AMM amm(env, LP1, TST(25), XRP(250)); },
-
3857 [&](Env& env) {
-
3858 lp2TSTBalance = getAccountLines(env, LP2, TST)["lines"][0u]["balance"].asString();
-
3859 auto const offer = getAccountOffers(env, LP2)["offers"][0u];
-
3860 lp2TakerGets = offer["taker_gets"].asString();
-
3861 lp2TakerPays = offer["taker_pays"]["value"].asString();
-
3862 });
-
3863 // Execute with CLOB offer
-
3864 prep(
-
3865 [&](Env& env) {
-
3866 if (!features[fixAMMv1_1])
-
3867 env(offer(LP1, XRPAmount{18'095'133}, STAmount{TST, UINT64_C(1'68737984885388), -14}),
- -
3869 else
-
3870 env(offer(LP1, XRPAmount{18'095'132}, STAmount{TST, UINT64_C(1'68737976189735), -14}),
- -
3872 },
-
3873 [&](Env& env) {
-
3874 BEAST_EXPECT(lp2TSTBalance == getAccountLines(env, LP2, TST)["lines"][0u]["balance"].asString());
-
3875 auto const offer = getAccountOffers(env, LP2)["offers"][0u];
-
3876 BEAST_EXPECT(lp2TakerGets == offer["taker_gets"].asString());
-
3877 BEAST_EXPECT(lp2TakerPays == offer["taker_pays"]["value"].asString());
-
3878 });
-
3879 }
+
3849 env.fund(XRP(10'000), LP1);
+
3850 env.fund(XRP(10'000), LP2);
+
3851 env(offer(LP1, TST(25), XRPAmount(287'500'000)));
+
3852
+
3853 // Either AMM or CLOB offer
+
3854 offerCb(env);
+
3855
+
3856 env(offer(LP2, TST(25), XRPAmount(287'500'000)));
+
3857
+
3858 expectCb(env);
+
3859 };
+
3860
+
3861 // If we replace AMM with an equivalent CLOB offer, which AMM generates
+
3862 // when it is consumed, then the result must be equivalent, too.
+
3863 std::string lp2TSTBalance;
+
3864 std::string lp2TakerGets;
+
3865 std::string lp2TakerPays;
+
3866 // Execute with AMM first
+
3867 prep(
+
3868 [&](Env& env) { AMM amm(env, LP1, TST(25), XRP(250)); },
+
3869 [&](Env& env) {
+
3870 lp2TSTBalance = getAccountLines(env, LP2, TST)["lines"][0u]["balance"].asString();
+
3871 auto const offer = getAccountOffers(env, LP2)["offers"][0u];
+
3872 lp2TakerGets = offer["taker_gets"].asString();
+
3873 lp2TakerPays = offer["taker_pays"]["value"].asString();
+
3874 });
+
3875 // Execute with CLOB offer
+
3876 prep(
+
3877 [&](Env& env) {
+
3878 if (!features[fixAMMv1_1])
+
3879 env(offer(LP1, XRPAmount{18'095'133}, STAmount{TST, UINT64_C(1'68737984885388), -14}),
+ +
3881 else
+
3882 env(offer(LP1, XRPAmount{18'095'132}, STAmount{TST, UINT64_C(1'68737976189735), -14}),
+ +
3884 },
+
3885 [&](Env& env) {
+
3886 BEAST_EXPECT(lp2TSTBalance == getAccountLines(env, LP2, TST)["lines"][0u]["balance"].asString());
+
3887 auto const offer = getAccountOffers(env, LP2)["offers"][0u];
+
3888 BEAST_EXPECT(lp2TakerGets == offer["taker_gets"].asString());
+
3889 BEAST_EXPECT(lp2TakerPays == offer["taker_pays"]["value"].asString());
+
3890 });
+
3891 }
-
3880
-
3881 void
-
- -
3883 {
-
3884 testcase("Trading Fee");
-
3885 using namespace jtx;
-
3886
-
3887 // Single Deposit, 1% fee
-
3888 testAMM(
-
3889 [&](AMM& ammAlice, Env& env) {
-
3890 // No fee
-
3891 ammAlice.deposit(carol, USD(3'000));
-
3892 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000}));
-
3893 ammAlice.withdrawAll(carol, USD(3'000));
-
3894 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
-
3895 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
-
3896 // Set fee to 1%
-
3897 ammAlice.vote(alice, 1'000);
-
3898 BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
-
3899 // Carol gets fewer LPToken ~994, because of the single deposit
-
3900 // fee
-
3901 ammAlice.deposit(carol, USD(3'000));
-
3902 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{994'981155689671, -12}));
-
3903 BEAST_EXPECT(expectHolding(env, carol, USD(27'000)));
-
3904 // Set fee to 0
-
3905 ammAlice.vote(alice, 0);
-
3906 ammAlice.withdrawAll(carol, USD(0));
-
3907 // Carol gets back less than the original deposit
-
3908 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(29'994'96220068281), -11}));
-
3909 },
-
3910 {{USD(1'000), EUR(1'000)}},
-
3911 0,
- -
3913 {features});
-
3914
-
3915 // Single deposit with EP not exceeding specified:
-
3916 // 100USD with EP not to exceed 0.1 (AssetIn/TokensOut). 1% fee.
-
3917 testAMM(
-
3918 [&](AMM& ammAlice, Env& env) {
-
3919 auto const balance = env.balance(carol, USD);
-
3920 auto tokensFee = ammAlice.deposit(carol, USD(1'000), std::nullopt, STAmount{USD, 1, -1});
-
3921 auto const deposit = balance - env.balance(carol, USD);
-
3922 ammAlice.withdrawAll(carol, USD(0));
-
3923 ammAlice.vote(alice, 0);
-
3924 BEAST_EXPECT(ammAlice.expectTradingFee(0));
-
3925 auto const tokensNoFee = ammAlice.deposit(carol, deposit);
-
3926 // carol pays ~2008 LPTokens in fees or ~0.5% of the no-fee
-
3927 // LPTokens
-
3928 BEAST_EXPECT(tokensFee == IOUAmount(485'636'0611129, -7));
-
3929 BEAST_EXPECT(tokensNoFee == IOUAmount(487'644'85901109, -8));
-
3930 },
- -
3932 1'000,
- -
3934 {features});
-
3935
-
3936 // Single deposit with EP not exceeding specified:
-
3937 // 200USD with EP not to exceed 0.002020 (AssetIn/TokensOut). 1% fee
-
3938 testAMM(
-
3939 [&](AMM& ammAlice, Env& env) {
-
3940 auto const balance = env.balance(carol, USD);
-
3941 auto const tokensFee = ammAlice.deposit(carol, USD(200), std::nullopt, STAmount{USD, 2020, -6});
-
3942 auto const deposit = balance - env.balance(carol, USD);
-
3943 ammAlice.withdrawAll(carol, USD(0));
-
3944 ammAlice.vote(alice, 0);
-
3945 BEAST_EXPECT(ammAlice.expectTradingFee(0));
-
3946 auto const tokensNoFee = ammAlice.deposit(carol, deposit);
-
3947 // carol pays ~475 LPTokens in fees or ~0.5% of the no-fee
-
3948 // LPTokens
-
3949 BEAST_EXPECT(tokensFee == IOUAmount(98'000'00000002, -8));
-
3950 BEAST_EXPECT(tokensNoFee == IOUAmount(98'475'81871545, -8));
-
3951 },
- -
3953 1'000,
- -
3955 {features});
-
3956
-
3957 // Single Withdrawal, 1% fee
-
3958 testAMM(
-
3959 [&](AMM& ammAlice, Env& env) {
-
3960 // No fee
-
3961 ammAlice.deposit(carol, USD(3'000));
-
3962
-
3963 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000}));
-
3964 BEAST_EXPECT(expectHolding(env, carol, USD(27'000)));
-
3965 // Set fee to 1%
-
3966 ammAlice.vote(alice, 1'000);
-
3967 BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
-
3968 // Single withdrawal. Carol gets ~5USD less than deposited.
-
3969 ammAlice.withdrawAll(carol, USD(0));
-
3970 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(29'994'97487437186), -11}));
-
3971 },
-
3972 {{USD(1'000), EUR(1'000)}},
-
3973 0,
- -
3975 {features});
-
3976
-
3977 // Withdraw with EPrice limit, 1% fee.
-
3978 testAMM(
-
3979 [&](AMM& ammAlice, Env& env) {
-
3980 ammAlice.deposit(carol, 1'000'000);
-
3981 auto const tokensFee = ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{520, 0});
-
3982 // carol withdraws ~1,443.44USD
-
3983 auto const balanceAfterWithdraw = [&]() {
-
3984 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
-
3985 return STAmount(USD, UINT64_C(30'443'43891402715), -11);
-
3986 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
-
3987 return STAmount(USD, UINT64_C(30'443'43891402714), -11);
-
3988 else
-
3989 return STAmount(USD, UINT64_C(30'443'43891402713), -11);
-
3990 }();
-
3991 BEAST_EXPECT(env.balance(carol, USD) == balanceAfterWithdraw);
-
3992 // Set to original pool size
-
3993 auto const deposit = balanceAfterWithdraw - USD(29'000);
-
3994 ammAlice.deposit(carol, deposit);
-
3995 // fee 0%
-
3996 ammAlice.vote(alice, 0);
-
3997 BEAST_EXPECT(ammAlice.expectTradingFee(0));
-
3998 auto const tokensNoFee = ammAlice.withdraw(carol, deposit);
-
3999 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
-
4000 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402717), -11));
-
4001 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
-
4002 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402716), -11));
-
4003 else
-
4004 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402713), -11));
-
4005 // carol pays ~4008 LPTokens in fees or ~0.5% of the no-fee
-
4006 // LPTokens
-
4007 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
-
4008 BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779913, -8));
-
4009 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
-
4010 BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779912, -8));
-
4011 else
-
4012 BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779911, -8));
-
4013 BEAST_EXPECT(tokensFee == IOUAmount(750'588'23529411, -8));
-
4014 },
- -
4016 1'000,
- -
4018 {features});
-
4019
-
4020 // Payment, 1% fee
-
4021 testAMM(
-
4022 [&](AMM& ammAlice, Env& env) {
-
4023 fund(env, gw, {bob}, XRP(1'000), {USD(1'000), EUR(1'000)}, Fund::Acct);
-
4024 // Alice contributed 1010EUR and 1000USD to the pool
-
4025 BEAST_EXPECT(expectHolding(env, alice, EUR(28'990)));
-
4026 BEAST_EXPECT(expectHolding(env, alice, USD(29'000)));
-
4027 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
-
4028 // Carol pays to Alice with no fee
-
4029 env(pay(carol, alice, EUR(10)), path(~EUR), sendmax(USD(10)), txflags(tfNoRippleDirect));
-
4030 env.close();
-
4031 // Alice has 10EUR more and Carol has 10USD less
-
4032 BEAST_EXPECT(expectHolding(env, alice, EUR(29'000)));
-
4033 BEAST_EXPECT(expectHolding(env, alice, USD(29'000)));
-
4034 BEAST_EXPECT(expectHolding(env, carol, USD(29'990)));
-
4035
-
4036 // Set fee to 1%
-
4037 ammAlice.vote(alice, 1'000);
-
4038 BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
-
4039 // Bob pays to Carol with 1% fee
-
4040 env(pay(bob, carol, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
-
4041 env.close();
-
4042 // Bob sends 10.1~EUR to pay 10USD
-
4043 BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(989'8989898989899), -13}));
-
4044 // Carol got 10USD
-
4045 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
-
4046 BEAST_EXPECT(ammAlice.expectBalances(
-
4047 USD(1'000), STAmount{EUR, UINT64_C(1'010'10101010101), -11}, ammAlice.tokens()));
-
4048 },
-
4049 {{USD(1'000), EUR(1'010)}},
-
4050 0,
- -
4052 {features});
-
4053
-
4054 // Offer crossing, 0.5% fee
-
4055 testAMM(
-
4056 [&](AMM& ammAlice, Env& env) {
-
4057 // No fee
-
4058 env(offer(carol, EUR(10), USD(10)));
-
4059 env.close();
-
4060 BEAST_EXPECT(expectHolding(env, carol, USD(29'990)));
-
4061 BEAST_EXPECT(expectHolding(env, carol, EUR(30'010)));
-
4062 // Change pool composition back
-
4063 env(offer(carol, USD(10), EUR(10)));
-
4064 env.close();
-
4065 // Set fee to 0.5%
-
4066 ammAlice.vote(alice, 500);
-
4067 BEAST_EXPECT(ammAlice.expectTradingFee(500));
-
4068 env(offer(carol, EUR(10), USD(10)));
-
4069 env.close();
-
4070 // Alice gets fewer ~4.97EUR for ~5.02USD, the difference goes
-
4071 // to the pool
-
4072 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(29'995'02512562814), -11}));
-
4073 BEAST_EXPECT(expectHolding(env, carol, STAmount{EUR, UINT64_C(30'004'97487437186), -11}));
-
4074 BEAST_EXPECT(expectOffers(
-
4075 env,
-
4076 carol,
-
4077 1,
-
4078 {{Amounts{
-
4079 STAmount{EUR, UINT64_C(5'025125628140703), -15},
-
4080 STAmount{USD, UINT64_C(5'025125628140703), -15}}}}));
-
4081 if (!features[fixAMMv1_1])
-
4082 {
-
4083 BEAST_EXPECT(ammAlice.expectBalances(
-
4084 STAmount{USD, UINT64_C(1'004'974874371859), -12},
-
4085 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
-
4086 ammAlice.tokens()));
-
4087 }
-
4088 else
-
4089 {
-
4090 BEAST_EXPECT(ammAlice.expectBalances(
-
4091 STAmount{USD, UINT64_C(1'004'97487437186), -11},
-
4092 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
-
4093 ammAlice.tokens()));
-
4094 }
-
4095 },
-
4096 {{USD(1'000), EUR(1'010)}},
-
4097 0,
- -
4099 {features});
-
4100
-
4101 // Payment with AMM and CLOB offer, 0 fee
-
4102 // AMM liquidity is consumed first up to CLOB offer quality
-
4103 // CLOB offer is fully consumed next
-
4104 // Remaining amount is consumed via AMM liquidity
-
4105 {
-
4106 Env env(*this, features);
-
4107 Account const ed("ed");
-
4108 fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)});
-
4109 env(offer(carol, EUR(5), USD(5)));
-
4110 AMM ammAlice(env, alice, USD(1'005), EUR(1'000));
-
4111 env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
-
4112 BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
-
4113 if (!features[fixAMMv1_1])
-
4114 {
-
4115 BEAST_EXPECT(expectHolding(env, bob, EUR(1'990)));
-
4116 BEAST_EXPECT(ammAlice.expectBalances(USD(1'000), EUR(1'005), ammAlice.tokens()));
-
4117 }
-
4118 else
-
4119 {
-
4120 BEAST_EXPECT(expectHolding(env, bob, STAmount(EUR, UINT64_C(1989'999999999999), -12)));
-
4121 BEAST_EXPECT(ammAlice.expectBalances(
-
4122 USD(1'000), STAmount(EUR, UINT64_C(1005'000000000001), -12), ammAlice.tokens()));
-
4123 }
-
4124 BEAST_EXPECT(expectOffers(env, carol, 0));
-
4125 }
-
4126
-
4127 // Payment with AMM and CLOB offer. Same as above but with 0.25%
-
4128 // fee.
-
4129 {
-
4130 Env env(*this, features);
-
4131 Account const ed("ed");
-
4132 fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)});
-
4133 env(offer(carol, EUR(5), USD(5)));
-
4134 // Set 0.25% fee
-
4135 AMM ammAlice(env, alice, USD(1'005), EUR(1'000), false, 250);
-
4136 env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
-
4137 BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
-
4138 if (!features[fixAMMv1_1])
-
4139 {
-
4140 BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'987453007618), -12}));
-
4141 BEAST_EXPECT(ammAlice.expectBalances(
-
4142 USD(1'000), STAmount{EUR, UINT64_C(1'005'012546992382), -12}, ammAlice.tokens()));
-
4143 }
-
4144 else
-
4145 {
-
4146 BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'987453007628), -12}));
-
4147 BEAST_EXPECT(ammAlice.expectBalances(
-
4148 USD(1'000), STAmount{EUR, UINT64_C(1'005'012546992372), -12}, ammAlice.tokens()));
-
4149 }
-
4150 BEAST_EXPECT(expectOffers(env, carol, 0));
-
4151 }
-
4152
-
4153 // Payment with AMM and CLOB offer. AMM has a better
-
4154 // spot price quality, but 1% fee offsets that. As the result
-
4155 // the entire trade is executed via LOB.
-
4156 {
-
4157 Env env(*this, features);
-
4158 Account const ed("ed");
-
4159 fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)});
-
4160 env(offer(carol, EUR(10), USD(10)));
-
4161 // Set 1% fee
-
4162 AMM ammAlice(env, alice, USD(1'005), EUR(1'000), false, 1'000);
-
4163 env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
-
4164 BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
-
4165 BEAST_EXPECT(expectHolding(env, bob, EUR(1'990)));
-
4166 BEAST_EXPECT(ammAlice.expectBalances(USD(1'005), EUR(1'000), ammAlice.tokens()));
-
4167 BEAST_EXPECT(expectOffers(env, carol, 0));
-
4168 }
-
4169
-
4170 // Payment with AMM and CLOB offer. AMM has a better
-
4171 // spot price quality, but 1% fee offsets that.
-
4172 // The CLOB offer is consumed first and the remaining
-
4173 // amount is consumed via AMM liquidity.
-
4174 {
-
4175 Env env(*this, features);
-
4176 Account const ed("ed");
-
4177 fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)});
-
4178 env(offer(carol, EUR(9), USD(9)));
-
4179 // Set 1% fee
-
4180 AMM ammAlice(env, alice, USD(1'005), EUR(1'000), false, 1'000);
-
4181 env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
-
4182 BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
-
4183 BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'993923296712), -12}));
-
4184 BEAST_EXPECT(ammAlice.expectBalances(
-
4185 USD(1'004), STAmount{EUR, UINT64_C(1'001'006076703288), -12}, ammAlice.tokens()));
-
4186 BEAST_EXPECT(expectOffers(env, carol, 0));
-
4187 }
-
4188 }
+
3892
+
3893 void
+
+ +
3895 {
+
3896 testcase("Trading Fee");
+
3897 using namespace jtx;
+
3898
+
3899 // Single Deposit, 1% fee
+
3900 testAMM(
+
3901 [&](AMM& ammAlice, Env& env) {
+
3902 // No fee
+
3903 ammAlice.deposit(carol, USD(3'000));
+
3904 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000}));
+
3905 ammAlice.withdrawAll(carol, USD(3'000));
+
3906 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{0}));
+
3907 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
+
3908 // Set fee to 1%
+
3909 ammAlice.vote(alice, 1'000);
+
3910 BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
+
3911 // Carol gets fewer LPToken ~994, because of the single deposit
+
3912 // fee
+
3913 ammAlice.deposit(carol, USD(3'000));
+
3914 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{994'981155689671, -12}));
+
3915 BEAST_EXPECT(expectHolding(env, carol, USD(27'000)));
+
3916 // Set fee to 0
+
3917 ammAlice.vote(alice, 0);
+
3918 ammAlice.withdrawAll(carol, USD(0));
+
3919 // Carol gets back less than the original deposit
+
3920 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(29'994'96220068281), -11}));
+
3921 },
+
3922 {{USD(1'000), EUR(1'000)}},
+
3923 0,
+ +
3925 {features});
+
3926
+
3927 // Single deposit with EP not exceeding specified:
+
3928 // 100USD with EP not to exceed 0.1 (AssetIn/TokensOut). 1% fee.
+
3929 testAMM(
+
3930 [&](AMM& ammAlice, Env& env) {
+
3931 auto const balance = env.balance(carol, USD);
+
3932 auto tokensFee = ammAlice.deposit(carol, USD(1'000), std::nullopt, STAmount{USD, 1, -1});
+
3933 auto const deposit = balance - env.balance(carol, USD);
+
3934 ammAlice.withdrawAll(carol, USD(0));
+
3935 ammAlice.vote(alice, 0);
+
3936 BEAST_EXPECT(ammAlice.expectTradingFee(0));
+
3937 auto const tokensNoFee = ammAlice.deposit(carol, deposit);
+
3938 // carol pays ~2008 LPTokens in fees or ~0.5% of the no-fee
+
3939 // LPTokens
+
3940 BEAST_EXPECT(tokensFee == IOUAmount(485'636'0611129, -7));
+
3941 BEAST_EXPECT(tokensNoFee == IOUAmount(487'644'85901109, -8));
+
3942 },
+ +
3944 1'000,
+ +
3946 {features});
+
3947
+
3948 // Single deposit with EP not exceeding specified:
+
3949 // 200USD with EP not to exceed 0.002020 (AssetIn/TokensOut). 1% fee
+
3950 testAMM(
+
3951 [&](AMM& ammAlice, Env& env) {
+
3952 auto const balance = env.balance(carol, USD);
+
3953 auto const tokensFee = ammAlice.deposit(carol, USD(200), std::nullopt, STAmount{USD, 2020, -6});
+
3954 auto const deposit = balance - env.balance(carol, USD);
+
3955 ammAlice.withdrawAll(carol, USD(0));
+
3956 ammAlice.vote(alice, 0);
+
3957 BEAST_EXPECT(ammAlice.expectTradingFee(0));
+
3958 auto const tokensNoFee = ammAlice.deposit(carol, deposit);
+
3959 // carol pays ~475 LPTokens in fees or ~0.5% of the no-fee
+
3960 // LPTokens
+
3961 BEAST_EXPECT(tokensFee == IOUAmount(98'000'00000002, -8));
+
3962 BEAST_EXPECT(tokensNoFee == IOUAmount(98'475'81871545, -8));
+
3963 },
+ +
3965 1'000,
+ +
3967 {features});
+
3968
+
3969 // Single Withdrawal, 1% fee
+
3970 testAMM(
+
3971 [&](AMM& ammAlice, Env& env) {
+
3972 // No fee
+
3973 ammAlice.deposit(carol, USD(3'000));
+
3974
+
3975 BEAST_EXPECT(ammAlice.expectLPTokens(carol, IOUAmount{1'000}));
+
3976 BEAST_EXPECT(expectHolding(env, carol, USD(27'000)));
+
3977 // Set fee to 1%
+
3978 ammAlice.vote(alice, 1'000);
+
3979 BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
+
3980 // Single withdrawal. Carol gets ~5USD less than deposited.
+
3981 ammAlice.withdrawAll(carol, USD(0));
+
3982 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(29'994'97487437186), -11}));
+
3983 },
+
3984 {{USD(1'000), EUR(1'000)}},
+
3985 0,
+ +
3987 {features});
+
3988
+
3989 // Withdraw with EPrice limit, 1% fee.
+
3990 testAMM(
+
3991 [&](AMM& ammAlice, Env& env) {
+
3992 ammAlice.deposit(carol, 1'000'000);
+
3993 auto const tokensFee = ammAlice.withdraw(carol, USD(100), std::nullopt, IOUAmount{520, 0});
+
3994 // carol withdraws ~1,443.44USD
+
3995 auto const balanceAfterWithdraw = [&]() {
+
3996 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
+
3997 return STAmount(USD, UINT64_C(30'443'43891402715), -11);
+
3998 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
+
3999 return STAmount(USD, UINT64_C(30'443'43891402714), -11);
+
4000 else
+
4001 return STAmount(USD, UINT64_C(30'443'43891402713), -11);
+
4002 }();
+
4003 BEAST_EXPECT(env.balance(carol, USD) == balanceAfterWithdraw);
+
4004 // Set to original pool size
+
4005 auto const deposit = balanceAfterWithdraw - USD(29'000);
+
4006 ammAlice.deposit(carol, deposit);
+
4007 // fee 0%
+
4008 ammAlice.vote(alice, 0);
+
4009 BEAST_EXPECT(ammAlice.expectTradingFee(0));
+
4010 auto const tokensNoFee = ammAlice.withdraw(carol, deposit);
+
4011 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
+
4012 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402717), -11));
+
4013 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
+
4014 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402716), -11));
+
4015 else
+
4016 BEAST_EXPECT(env.balance(carol, USD) == STAmount(USD, UINT64_C(30'443'43891402713), -11));
+
4017 // carol pays ~4008 LPTokens in fees or ~0.5% of the no-fee
+
4018 // LPTokens
+
4019 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
+
4020 BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779913, -8));
+
4021 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
+
4022 BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779912, -8));
+
4023 else
+
4024 BEAST_EXPECT(tokensNoFee == IOUAmount(746'579'80779911, -8));
+
4025 BEAST_EXPECT(tokensFee == IOUAmount(750'588'23529411, -8));
+
4026 },
+ +
4028 1'000,
+ +
4030 {features});
+
4031
+
4032 // Payment, 1% fee
+
4033 testAMM(
+
4034 [&](AMM& ammAlice, Env& env) {
+
4035 fund(env, gw, {bob}, XRP(1'000), {USD(1'000), EUR(1'000)}, Fund::Acct);
+
4036 // Alice contributed 1010EUR and 1000USD to the pool
+
4037 BEAST_EXPECT(expectHolding(env, alice, EUR(28'990)));
+
4038 BEAST_EXPECT(expectHolding(env, alice, USD(29'000)));
+
4039 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
+
4040 // Carol pays to Alice with no fee
+
4041 env(pay(carol, alice, EUR(10)), path(~EUR), sendmax(USD(10)), txflags(tfNoRippleDirect));
+
4042 env.close();
+
4043 // Alice has 10EUR more and Carol has 10USD less
+
4044 BEAST_EXPECT(expectHolding(env, alice, EUR(29'000)));
+
4045 BEAST_EXPECT(expectHolding(env, alice, USD(29'000)));
+
4046 BEAST_EXPECT(expectHolding(env, carol, USD(29'990)));
+
4047
+
4048 // Set fee to 1%
+
4049 ammAlice.vote(alice, 1'000);
+
4050 BEAST_EXPECT(ammAlice.expectTradingFee(1'000));
+
4051 // Bob pays to Carol with 1% fee
+
4052 env(pay(bob, carol, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
+
4053 env.close();
+
4054 // Bob sends 10.1~EUR to pay 10USD
+
4055 BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(989'8989898989899), -13}));
+
4056 // Carol got 10USD
+
4057 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
+
4058 BEAST_EXPECT(ammAlice.expectBalances(
+
4059 USD(1'000), STAmount{EUR, UINT64_C(1'010'10101010101), -11}, ammAlice.tokens()));
+
4060 },
+
4061 {{USD(1'000), EUR(1'010)}},
+
4062 0,
+ +
4064 {features});
+
4065
+
4066 // Offer crossing, 0.5% fee
+
4067 testAMM(
+
4068 [&](AMM& ammAlice, Env& env) {
+
4069 // No fee
+
4070 env(offer(carol, EUR(10), USD(10)));
+
4071 env.close();
+
4072 BEAST_EXPECT(expectHolding(env, carol, USD(29'990)));
+
4073 BEAST_EXPECT(expectHolding(env, carol, EUR(30'010)));
+
4074 // Change pool composition back
+
4075 env(offer(carol, USD(10), EUR(10)));
+
4076 env.close();
+
4077 // Set fee to 0.5%
+
4078 ammAlice.vote(alice, 500);
+
4079 BEAST_EXPECT(ammAlice.expectTradingFee(500));
+
4080 env(offer(carol, EUR(10), USD(10)));
+
4081 env.close();
+
4082 // Alice gets fewer ~4.97EUR for ~5.02USD, the difference goes
+
4083 // to the pool
+
4084 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(29'995'02512562814), -11}));
+
4085 BEAST_EXPECT(expectHolding(env, carol, STAmount{EUR, UINT64_C(30'004'97487437186), -11}));
+
4086 BEAST_EXPECT(expectOffers(
+
4087 env,
+
4088 carol,
+
4089 1,
+
4090 {{Amounts{
+
4091 STAmount{EUR, UINT64_C(5'025125628140703), -15},
+
4092 STAmount{USD, UINT64_C(5'025125628140703), -15}}}}));
+
4093 if (!features[fixAMMv1_1])
+
4094 {
+
4095 BEAST_EXPECT(ammAlice.expectBalances(
+
4096 STAmount{USD, UINT64_C(1'004'974874371859), -12},
+
4097 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
+
4098 ammAlice.tokens()));
+
4099 }
+
4100 else
+
4101 {
+
4102 BEAST_EXPECT(ammAlice.expectBalances(
+
4103 STAmount{USD, UINT64_C(1'004'97487437186), -11},
+
4104 STAmount{EUR, UINT64_C(1'005'025125628141), -12},
+
4105 ammAlice.tokens()));
+
4106 }
+
4107 },
+
4108 {{USD(1'000), EUR(1'010)}},
+
4109 0,
+ +
4111 {features});
+
4112
+
4113 // Payment with AMM and CLOB offer, 0 fee
+
4114 // AMM liquidity is consumed first up to CLOB offer quality
+
4115 // CLOB offer is fully consumed next
+
4116 // Remaining amount is consumed via AMM liquidity
+
4117 {
+
4118 Env env(*this, features);
+
4119 Account const ed("ed");
+
4120 fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)});
+
4121 env(offer(carol, EUR(5), USD(5)));
+
4122 AMM ammAlice(env, alice, USD(1'005), EUR(1'000));
+
4123 env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
+
4124 BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
+
4125 if (!features[fixAMMv1_1])
+
4126 {
+
4127 BEAST_EXPECT(expectHolding(env, bob, EUR(1'990)));
+
4128 BEAST_EXPECT(ammAlice.expectBalances(USD(1'000), EUR(1'005), ammAlice.tokens()));
+
4129 }
+
4130 else
+
4131 {
+
4132 BEAST_EXPECT(expectHolding(env, bob, STAmount(EUR, UINT64_C(1989'999999999999), -12)));
+
4133 BEAST_EXPECT(ammAlice.expectBalances(
+
4134 USD(1'000), STAmount(EUR, UINT64_C(1005'000000000001), -12), ammAlice.tokens()));
+
4135 }
+
4136 BEAST_EXPECT(expectOffers(env, carol, 0));
+
4137 }
+
4138
+
4139 // Payment with AMM and CLOB offer. Same as above but with 0.25%
+
4140 // fee.
+
4141 {
+
4142 Env env(*this, features);
+
4143 Account const ed("ed");
+
4144 fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)});
+
4145 env(offer(carol, EUR(5), USD(5)));
+
4146 // Set 0.25% fee
+
4147 AMM ammAlice(env, alice, USD(1'005), EUR(1'000), false, 250);
+
4148 env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
+
4149 BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
+
4150 if (!features[fixAMMv1_1])
+
4151 {
+
4152 BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'987453007618), -12}));
+
4153 BEAST_EXPECT(ammAlice.expectBalances(
+
4154 USD(1'000), STAmount{EUR, UINT64_C(1'005'012546992382), -12}, ammAlice.tokens()));
+
4155 }
+
4156 else
+
4157 {
+
4158 BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'987453007628), -12}));
+
4159 BEAST_EXPECT(ammAlice.expectBalances(
+
4160 USD(1'000), STAmount{EUR, UINT64_C(1'005'012546992372), -12}, ammAlice.tokens()));
+
4161 }
+
4162 BEAST_EXPECT(expectOffers(env, carol, 0));
+
4163 }
+
4164
+
4165 // Payment with AMM and CLOB offer. AMM has a better
+
4166 // spot price quality, but 1% fee offsets that. As the result
+
4167 // the entire trade is executed via LOB.
+
4168 {
+
4169 Env env(*this, features);
+
4170 Account const ed("ed");
+
4171 fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)});
+
4172 env(offer(carol, EUR(10), USD(10)));
+
4173 // Set 1% fee
+
4174 AMM ammAlice(env, alice, USD(1'005), EUR(1'000), false, 1'000);
+
4175 env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
+
4176 BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
+
4177 BEAST_EXPECT(expectHolding(env, bob, EUR(1'990)));
+
4178 BEAST_EXPECT(ammAlice.expectBalances(USD(1'005), EUR(1'000), ammAlice.tokens()));
+
4179 BEAST_EXPECT(expectOffers(env, carol, 0));
+
4180 }
+
4181
+
4182 // Payment with AMM and CLOB offer. AMM has a better
+
4183 // spot price quality, but 1% fee offsets that.
+
4184 // The CLOB offer is consumed first and the remaining
+
4185 // amount is consumed via AMM liquidity.
+
4186 {
+
4187 Env env(*this, features);
+
4188 Account const ed("ed");
+
4189 fund(env, gw, {alice, bob, carol, ed}, XRP(1'000), {USD(2'000), EUR(2'000)});
+
4190 env(offer(carol, EUR(9), USD(9)));
+
4191 // Set 1% fee
+
4192 AMM ammAlice(env, alice, USD(1'005), EUR(1'000), false, 1'000);
+
4193 env(pay(bob, ed, USD(10)), path(~USD), sendmax(EUR(15)), txflags(tfNoRippleDirect));
+
4194 BEAST_EXPECT(expectHolding(env, ed, USD(2'010)));
+
4195 BEAST_EXPECT(expectHolding(env, bob, STAmount{EUR, UINT64_C(1'989'993923296712), -12}));
+
4196 BEAST_EXPECT(ammAlice.expectBalances(
+
4197 USD(1'004), STAmount{EUR, UINT64_C(1'001'006076703288), -12}, ammAlice.tokens()));
+
4198 BEAST_EXPECT(expectOffers(env, carol, 0));
+
4199 }
+
4200 }
-
4189
-
4190 void
-
- -
4192 {
-
4193 testcase("Adjusted Deposit/Withdraw Tokens");
-
4194
-
4195 using namespace jtx;
-
4196
-
4197 // Deposit/Withdraw in USD
-
4198 testAMM(
-
4199 [&](AMM& ammAlice, Env& env) {
-
4200 Account const bob("bob");
-
4201 Account const ed("ed");
-
4202 Account const paul("paul");
-
4203 Account const dan("dan");
-
4204 Account const chris("chris");
-
4205 Account const simon("simon");
-
4206 Account const ben("ben");
-
4207 Account const natalie("natalie");
-
4208 fund(env, gw, {bob, ed, paul, dan, chris, simon, ben, natalie}, {USD(1'500'000)}, Fund::Acct);
-
4209 for (int i = 0; i < 10; ++i)
-
4210 {
-
4211 ammAlice.deposit(ben, STAmount{USD, 1, -10});
-
4212 ammAlice.withdrawAll(ben, USD(0));
-
4213 ammAlice.deposit(simon, USD(0.1));
-
4214 ammAlice.withdrawAll(simon, USD(0));
-
4215 ammAlice.deposit(chris, USD(1));
-
4216 ammAlice.withdrawAll(chris, USD(0));
-
4217 ammAlice.deposit(dan, USD(10));
-
4218 ammAlice.withdrawAll(dan, USD(0));
-
4219 ammAlice.deposit(bob, USD(100));
-
4220 ammAlice.withdrawAll(bob, USD(0));
-
4221 ammAlice.deposit(carol, USD(1'000));
-
4222 ammAlice.withdrawAll(carol, USD(0));
-
4223 ammAlice.deposit(ed, USD(10'000));
-
4224 ammAlice.withdrawAll(ed, USD(0));
-
4225 ammAlice.deposit(paul, USD(100'000));
-
4226 ammAlice.withdrawAll(paul, USD(0));
-
4227 ammAlice.deposit(natalie, USD(1'000'000));
-
4228 ammAlice.withdrawAll(natalie, USD(0));
-
4229 }
-
4230 // Due to round off some accounts have a tiny gain, while
-
4231 // other have a tiny loss. The last account to withdraw
-
4232 // gets everything in the pool.
-
4233 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
-
4234 BEAST_EXPECT(ammAlice.expectBalances(
-
4235 XRP(10'000), STAmount{USD, UINT64_C(10'000'0000000013), -10}, IOUAmount{10'000'000}));
-
4236 else if (features[fixAMMv1_3])
-
4237 BEAST_EXPECT(ammAlice.expectBalances(
-
4238 XRP(10'000), STAmount{USD, UINT64_C(10'000'0000000003), -10}, IOUAmount{10'000'000}));
-
4239 else
-
4240 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000}));
-
4241 BEAST_EXPECT(expectHolding(env, ben, USD(1'500'000)));
-
4242 BEAST_EXPECT(expectHolding(env, simon, USD(1'500'000)));
-
4243 BEAST_EXPECT(expectHolding(env, chris, USD(1'500'000)));
-
4244 BEAST_EXPECT(expectHolding(env, dan, USD(1'500'000)));
+
4201
+
4202 void
+
+ +
4204 {
+
4205 testcase("Adjusted Deposit/Withdraw Tokens");
+
4206
+
4207 using namespace jtx;
+
4208
+
4209 // Deposit/Withdraw in USD
+
4210 testAMM(
+
4211 [&](AMM& ammAlice, Env& env) {
+
4212 Account const bob("bob");
+
4213 Account const ed("ed");
+
4214 Account const paul("paul");
+
4215 Account const dan("dan");
+
4216 Account const chris("chris");
+
4217 Account const simon("simon");
+
4218 Account const ben("ben");
+
4219 Account const natalie("natalie");
+
4220 fund(env, gw, {bob, ed, paul, dan, chris, simon, ben, natalie}, {USD(1'500'000)}, Fund::Acct);
+
4221 for (int i = 0; i < 10; ++i)
+
4222 {
+
4223 ammAlice.deposit(ben, STAmount{USD, 1, -10});
+
4224 ammAlice.withdrawAll(ben, USD(0));
+
4225 ammAlice.deposit(simon, USD(0.1));
+
4226 ammAlice.withdrawAll(simon, USD(0));
+
4227 ammAlice.deposit(chris, USD(1));
+
4228 ammAlice.withdrawAll(chris, USD(0));
+
4229 ammAlice.deposit(dan, USD(10));
+
4230 ammAlice.withdrawAll(dan, USD(0));
+
4231 ammAlice.deposit(bob, USD(100));
+
4232 ammAlice.withdrawAll(bob, USD(0));
+
4233 ammAlice.deposit(carol, USD(1'000));
+
4234 ammAlice.withdrawAll(carol, USD(0));
+
4235 ammAlice.deposit(ed, USD(10'000));
+
4236 ammAlice.withdrawAll(ed, USD(0));
+
4237 ammAlice.deposit(paul, USD(100'000));
+
4238 ammAlice.withdrawAll(paul, USD(0));
+
4239 ammAlice.deposit(natalie, USD(1'000'000));
+
4240 ammAlice.withdrawAll(natalie, USD(0));
+
4241 }
+
4242 // Due to round off some accounts have a tiny gain, while
+
4243 // other have a tiny loss. The last account to withdraw
+
4244 // gets everything in the pool.
4245 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
-
4246 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'000'00000000001), -11}));
-
4247 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
-
4248 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
-
4249 else
-
4250 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
-
4251 BEAST_EXPECT(expectHolding(env, ed, USD(1'500'000)));
-
4252 BEAST_EXPECT(expectHolding(env, paul, USD(1'500'000)));
-
4253 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
-
4254 BEAST_EXPECT(expectHolding(env, natalie, STAmount{USD, UINT64_C(1'500'000'000000002), -9}));
-
4255 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
-
4256 BEAST_EXPECT(expectHolding(env, natalie, STAmount{USD, UINT64_C(1'500'000'000000005), -9}));
-
4257 else
-
4258 BEAST_EXPECT(expectHolding(env, natalie, USD(1'500'000)));
-
4259 ammAlice.withdrawAll(alice);
-
4260 BEAST_EXPECT(!ammAlice.ammExists());
-
4261 if (!features[fixAMMv1_1])
-
4262 BEAST_EXPECT(expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000013), -10}));
-
4263 else if (features[fixAMMv1_3])
-
4264 BEAST_EXPECT(expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000003), -10}));
-
4265 else
-
4266 BEAST_EXPECT(expectHolding(env, alice, USD(30'000)));
-
4267 // alice XRP balance is 30,000 initial - 50 AMMCreate fee -
-
4268 // 10drops fee
-
4269 BEAST_EXPECT(
-
4270 accountBalance(env, alice) == std::to_string(29950000000 - env.current()->fees().base.drops()));
-
4271 },
- -
4273 0,
- -
4275 {features});
-
4276
-
4277 // Same as above but deposit/withdraw in XRP
-
4278 testAMM(
-
4279 [&](AMM& ammAlice, Env& env) {
-
4280 Account const bob("bob");
-
4281 Account const ed("ed");
-
4282 Account const paul("paul");
-
4283 Account const dan("dan");
-
4284 Account const chris("chris");
-
4285 Account const simon("simon");
-
4286 Account const ben("ben");
-
4287 Account const natalie("natalie");
-
4288 fund(env, gw, {bob, ed, paul, dan, chris, simon, ben, natalie}, XRP(2'000'000), {}, Fund::Acct);
-
4289 for (int i = 0; i < 10; ++i)
-
4290 {
-
4291 ammAlice.deposit(ben, XRPAmount{1});
-
4292 ammAlice.withdrawAll(ben, XRP(0));
-
4293 ammAlice.deposit(simon, XRPAmount(1'000));
-
4294 ammAlice.withdrawAll(simon, XRP(0));
-
4295 ammAlice.deposit(chris, XRP(1));
-
4296 ammAlice.withdrawAll(chris, XRP(0));
-
4297 ammAlice.deposit(dan, XRP(10));
-
4298 ammAlice.withdrawAll(dan, XRP(0));
-
4299 ammAlice.deposit(bob, XRP(100));
-
4300 ammAlice.withdrawAll(bob, XRP(0));
-
4301 ammAlice.deposit(carol, XRP(1'000));
-
4302 ammAlice.withdrawAll(carol, XRP(0));
-
4303 ammAlice.deposit(ed, XRP(10'000));
-
4304 ammAlice.withdrawAll(ed, XRP(0));
-
4305 ammAlice.deposit(paul, XRP(100'000));
-
4306 ammAlice.withdrawAll(paul, XRP(0));
-
4307 ammAlice.deposit(natalie, XRP(1'000'000));
-
4308 ammAlice.withdrawAll(natalie, XRP(0));
-
4309 }
-
4310 auto const baseFee = env.current()->fees().base.drops();
-
4311 if (!features[fixAMMv1_3])
-
4312 {
-
4313 // No round off with XRP in this test
-
4314 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000}));
-
4315 ammAlice.withdrawAll(alice);
-
4316 BEAST_EXPECT(!ammAlice.ammExists());
-
4317 // 20,000 initial - (deposit+withdraw) * 10
-
4318 auto const xrpBalance = (XRP(2'000'000) - txfee(env, 20)).getText();
-
4319 BEAST_EXPECT(accountBalance(env, ben) == xrpBalance);
-
4320 BEAST_EXPECT(accountBalance(env, simon) == xrpBalance);
-
4321 BEAST_EXPECT(accountBalance(env, chris) == xrpBalance);
-
4322 BEAST_EXPECT(accountBalance(env, dan) == xrpBalance);
-
4323
-
4324 // 30,000 initial - (deposit+withdraw) * 10
-
4325 BEAST_EXPECT(accountBalance(env, carol) == std::to_string(30'000'000'000 - 20 * baseFee));
-
4326 BEAST_EXPECT(accountBalance(env, ed) == xrpBalance);
-
4327 BEAST_EXPECT(accountBalance(env, paul) == xrpBalance);
-
4328 BEAST_EXPECT(accountBalance(env, natalie) == xrpBalance);
-
4329 // 30,000 initial - 50 AMMCreate fee - 10drops withdraw fee
-
4330 BEAST_EXPECT(accountBalance(env, alice) == std::to_string(29'950'000'000 - baseFee));
-
4331 }
-
4332 else
-
4333 {
-
4334 // post-amendment the rounding takes place to ensure
-
4335 // AMM invariant
-
4336 BEAST_EXPECT(
-
4337 ammAlice.expectBalances(XRPAmount(10'000'000'080), USD(10'000), IOUAmount{10'000'000}));
-
4338 ammAlice.withdrawAll(alice);
-
4339 BEAST_EXPECT(!ammAlice.ammExists());
-
4340 auto const xrpBalance = XRP(2'000'000) - txfee(env, 20) - drops(10);
-
4341 auto const xrpBalanceText = xrpBalance.getText();
-
4342 BEAST_EXPECT(accountBalance(env, ben) == xrpBalanceText);
-
4343 BEAST_EXPECT(accountBalance(env, simon) == xrpBalanceText);
-
4344 BEAST_EXPECT(accountBalance(env, chris) == xrpBalanceText);
-
4345 BEAST_EXPECT(accountBalance(env, dan) == xrpBalanceText);
-
4346 BEAST_EXPECT(accountBalance(env, carol) == std::to_string(30'000'000'000 - 20 * baseFee - 10));
-
4347 BEAST_EXPECT(accountBalance(env, ed) == (xrpBalance + drops(2)).getText());
-
4348 BEAST_EXPECT(accountBalance(env, paul) == (xrpBalance + drops(3)).getText());
-
4349 BEAST_EXPECT(accountBalance(env, natalie) == (xrpBalance + drops(5)).getText());
-
4350 BEAST_EXPECT(accountBalance(env, alice) == std::to_string(29'950'000'000 - baseFee + 80));
-
4351 }
-
4352 },
- -
4354 0,
- -
4356 {features});
-
4357 }
+
4246 BEAST_EXPECT(ammAlice.expectBalances(
+
4247 XRP(10'000), STAmount{USD, UINT64_C(10'000'0000000013), -10}, IOUAmount{10'000'000}));
+
4248 else if (features[fixAMMv1_3])
+
4249 BEAST_EXPECT(ammAlice.expectBalances(
+
4250 XRP(10'000), STAmount{USD, UINT64_C(10'000'0000000003), -10}, IOUAmount{10'000'000}));
+
4251 else
+
4252 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000}));
+
4253 BEAST_EXPECT(expectHolding(env, ben, USD(1'500'000)));
+
4254 BEAST_EXPECT(expectHolding(env, simon, USD(1'500'000)));
+
4255 BEAST_EXPECT(expectHolding(env, chris, USD(1'500'000)));
+
4256 BEAST_EXPECT(expectHolding(env, dan, USD(1'500'000)));
+
4257 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
+
4258 BEAST_EXPECT(expectHolding(env, carol, STAmount{USD, UINT64_C(30'000'00000000001), -11}));
+
4259 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
+
4260 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
+
4261 else
+
4262 BEAST_EXPECT(expectHolding(env, carol, USD(30'000)));
+
4263 BEAST_EXPECT(expectHolding(env, ed, USD(1'500'000)));
+
4264 BEAST_EXPECT(expectHolding(env, paul, USD(1'500'000)));
+
4265 if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
+
4266 BEAST_EXPECT(expectHolding(env, natalie, STAmount{USD, UINT64_C(1'500'000'000000002), -9}));
+
4267 else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
+
4268 BEAST_EXPECT(expectHolding(env, natalie, STAmount{USD, UINT64_C(1'500'000'000000005), -9}));
+
4269 else
+
4270 BEAST_EXPECT(expectHolding(env, natalie, USD(1'500'000)));
+
4271 ammAlice.withdrawAll(alice);
+
4272 BEAST_EXPECT(!ammAlice.ammExists());
+
4273 if (!features[fixAMMv1_1])
+
4274 BEAST_EXPECT(expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000013), -10}));
+
4275 else if (features[fixAMMv1_3])
+
4276 BEAST_EXPECT(expectHolding(env, alice, STAmount{USD, UINT64_C(30'000'0000000003), -10}));
+
4277 else
+
4278 BEAST_EXPECT(expectHolding(env, alice, USD(30'000)));
+
4279 // alice XRP balance is 30,000 initial - 50 AMMCreate fee -
+
4280 // 10drops fee
+
4281 BEAST_EXPECT(
+
4282 accountBalance(env, alice) == std::to_string(29950000000 - env.current()->fees().base.drops()));
+
4283 },
+ +
4285 0,
+ +
4287 {features});
+
4288
+
4289 // Same as above but deposit/withdraw in XRP
+
4290 testAMM(
+
4291 [&](AMM& ammAlice, Env& env) {
+
4292 Account const bob("bob");
+
4293 Account const ed("ed");
+
4294 Account const paul("paul");
+
4295 Account const dan("dan");
+
4296 Account const chris("chris");
+
4297 Account const simon("simon");
+
4298 Account const ben("ben");
+
4299 Account const natalie("natalie");
+
4300 fund(env, gw, {bob, ed, paul, dan, chris, simon, ben, natalie}, XRP(2'000'000), {}, Fund::Acct);
+
4301 for (int i = 0; i < 10; ++i)
+
4302 {
+
4303 ammAlice.deposit(ben, XRPAmount{1});
+
4304 ammAlice.withdrawAll(ben, XRP(0));
+
4305 ammAlice.deposit(simon, XRPAmount(1'000));
+
4306 ammAlice.withdrawAll(simon, XRP(0));
+
4307 ammAlice.deposit(chris, XRP(1));
+
4308 ammAlice.withdrawAll(chris, XRP(0));
+
4309 ammAlice.deposit(dan, XRP(10));
+
4310 ammAlice.withdrawAll(dan, XRP(0));
+
4311 ammAlice.deposit(bob, XRP(100));
+
4312 ammAlice.withdrawAll(bob, XRP(0));
+
4313 ammAlice.deposit(carol, XRP(1'000));
+
4314 ammAlice.withdrawAll(carol, XRP(0));
+
4315 ammAlice.deposit(ed, XRP(10'000));
+
4316 ammAlice.withdrawAll(ed, XRP(0));
+
4317 ammAlice.deposit(paul, XRP(100'000));
+
4318 ammAlice.withdrawAll(paul, XRP(0));
+
4319 ammAlice.deposit(natalie, XRP(1'000'000));
+
4320 ammAlice.withdrawAll(natalie, XRP(0));
+
4321 }
+
4322 auto const baseFee = env.current()->fees().base.drops();
+
4323 if (!features[fixAMMv1_3])
+
4324 {
+
4325 // No round off with XRP in this test
+
4326 BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), IOUAmount{10'000'000}));
+
4327 ammAlice.withdrawAll(alice);
+
4328 BEAST_EXPECT(!ammAlice.ammExists());
+
4329 // 20,000 initial - (deposit+withdraw) * 10
+
4330 auto const xrpBalance = (XRP(2'000'000) - txfee(env, 20)).getText();
+
4331 BEAST_EXPECT(accountBalance(env, ben) == xrpBalance);
+
4332 BEAST_EXPECT(accountBalance(env, simon) == xrpBalance);
+
4333 BEAST_EXPECT(accountBalance(env, chris) == xrpBalance);
+
4334 BEAST_EXPECT(accountBalance(env, dan) == xrpBalance);
+
4335
+
4336 // 30,000 initial - (deposit+withdraw) * 10
+
4337 BEAST_EXPECT(accountBalance(env, carol) == std::to_string(30'000'000'000 - 20 * baseFee));
+
4338 BEAST_EXPECT(accountBalance(env, ed) == xrpBalance);
+
4339 BEAST_EXPECT(accountBalance(env, paul) == xrpBalance);
+
4340 BEAST_EXPECT(accountBalance(env, natalie) == xrpBalance);
+
4341 // 30,000 initial - 50 AMMCreate fee - 10drops withdraw fee
+
4342 BEAST_EXPECT(accountBalance(env, alice) == std::to_string(29'950'000'000 - baseFee));
+
4343 }
+
4344 else
+
4345 {
+
4346 // post-amendment the rounding takes place to ensure
+
4347 // AMM invariant
+
4348 BEAST_EXPECT(
+
4349 ammAlice.expectBalances(XRPAmount(10'000'000'080), USD(10'000), IOUAmount{10'000'000}));
+
4350 ammAlice.withdrawAll(alice);
+
4351 BEAST_EXPECT(!ammAlice.ammExists());
+
4352 auto const xrpBalance = XRP(2'000'000) - txfee(env, 20) - drops(10);
+
4353 auto const xrpBalanceText = xrpBalance.getText();
+
4354 BEAST_EXPECT(accountBalance(env, ben) == xrpBalanceText);
+
4355 BEAST_EXPECT(accountBalance(env, simon) == xrpBalanceText);
+
4356 BEAST_EXPECT(accountBalance(env, chris) == xrpBalanceText);
+
4357 BEAST_EXPECT(accountBalance(env, dan) == xrpBalanceText);
+
4358 BEAST_EXPECT(accountBalance(env, carol) == std::to_string(30'000'000'000 - 20 * baseFee - 10));
+
4359 BEAST_EXPECT(accountBalance(env, ed) == (xrpBalance + drops(2)).getText());
+
4360 BEAST_EXPECT(accountBalance(env, paul) == (xrpBalance + drops(3)).getText());
+
4361 BEAST_EXPECT(accountBalance(env, natalie) == (xrpBalance + drops(5)).getText());
+
4362 BEAST_EXPECT(accountBalance(env, alice) == std::to_string(29'950'000'000 - baseFee + 80));
+
4363 }
+
4364 },
+ +
4366 0,
+ +
4368 {features});
+
4369 }
-
4358
-
4359 void
-
- -
4361 {
-
4362 testcase("Auto Delete");
-
4363
-
4364 using namespace jtx;
- -
4366
-
4367 {
-
4368 Env env(
-
4369 *this,
- -
4371 cfg->FEES.reference_fee = XRPAmount(1);
-
4372 return cfg;
-
4373 }),
-
4374 all);
-
4375 fund(env, gw, {alice}, XRP(20'000), {USD(10'000)});
-
4376 AMM amm(env, gw, XRP(10'000), USD(10'000));
-
4377 for (auto i = 0; i < maxDeletableAMMTrustLines + 10; ++i)
-
4378 {
-
4379 Account const a{std::to_string(i)};
-
4380 env.fund(XRP(1'000), a);
-
4381 env(trust(a, STAmount{amm.lptIssue(), 10'000}));
-
4382 env.close();
-
4383 }
-
4384 // The trustlines are partially deleted,
-
4385 // AMM is set to an empty state.
-
4386 amm.withdrawAll(gw);
-
4387 BEAST_EXPECT(amm.ammExists());
-
4388
-
4389 // Bid,Vote,Deposit,Withdraw,SetTrust failing with
-
4390 // tecAMM_EMPTY. Deposit succeeds with tfTwoAssetIfEmpty option.
-
4391 env(amm.bid({
-
4392 .account = alice,
-
4393 .bidMin = 1000,
-
4394 }),
-
4395 ter(tecAMM_EMPTY));
- -
4397 amm.withdraw(alice, 100, std::nullopt, std::nullopt, ter(tecAMM_EMPTY));
-
4398 amm.deposit(alice, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecAMM_EMPTY));
-
4399 env(trust(alice, STAmount{amm.lptIssue(), 10'000}), ter(tecAMM_EMPTY));
+
4370
+
4371 void
+
+ +
4373 {
+
4374 testcase("Auto Delete");
+
4375
+
4376 using namespace jtx;
+ +
4378
+
4379 {
+
4380 Env env(
+
4381 *this,
+ +
4383 cfg->FEES.reference_fee = XRPAmount(1);
+
4384 return cfg;
+
4385 }),
+
4386 all);
+
4387 fund(env, gw, {alice}, XRP(20'000), {USD(10'000)});
+
4388 AMM amm(env, gw, XRP(10'000), USD(10'000));
+
4389 for (auto i = 0; i < maxDeletableAMMTrustLines + 10; ++i)
+
4390 {
+
4391 Account const a{std::to_string(i)};
+
4392 env.fund(XRP(1'000), a);
+
4393 env(trust(a, STAmount{amm.lptIssue(), 10'000}));
+
4394 env.close();
+
4395 }
+
4396 // The trustlines are partially deleted,
+
4397 // AMM is set to an empty state.
+
4398 amm.withdrawAll(gw);
+
4399 BEAST_EXPECT(amm.ammExists());
4400
-
4401 // Can deposit with tfTwoAssetIfEmpty option
-
4402 amm.deposit(
-
4403 alice,
- -
4405 XRP(10'000),
-
4406 USD(10'000),
- - - - -
4411 1'000);
-
4412 BEAST_EXPECT(amm.expectBalances(XRP(10'000), USD(10'000), amm.tokens()));
-
4413 BEAST_EXPECT(amm.expectTradingFee(1'000));
-
4414 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{0}));
-
4415
-
4416 // Withdrawing all tokens deletes AMM since the number
-
4417 // of remaining trustlines is less than max
-
4418 amm.withdrawAll(alice);
-
4419 BEAST_EXPECT(!amm.ammExists());
-
4420 BEAST_EXPECT(!env.le(keylet::ownerDir(amm.ammAccount())));
-
4421 }
-
4422
-
4423 {
-
4424 Env env(
-
4425 *this,
- -
4427 cfg->FEES.reference_fee = XRPAmount(1);
-
4428 return cfg;
-
4429 }),
-
4430 all);
-
4431 fund(env, gw, {alice}, XRP(20'000), {USD(10'000)});
-
4432 AMM amm(env, gw, XRP(10'000), USD(10'000));
-
4433 for (auto i = 0; i < maxDeletableAMMTrustLines * 2 + 10; ++i)
-
4434 {
-
4435 Account const a{std::to_string(i)};
-
4436 env.fund(XRP(1'000), a);
-
4437 env(trust(a, STAmount{amm.lptIssue(), 10'000}));
-
4438 env.close();
-
4439 }
-
4440 // The trustlines are partially deleted.
-
4441 amm.withdrawAll(gw);
-
4442 BEAST_EXPECT(amm.ammExists());
-
4443
-
4444 // AMMDelete has to be called twice to delete AMM.
-
4445 amm.ammDelete(alice, ter(tecINCOMPLETE));
-
4446 BEAST_EXPECT(amm.ammExists());
-
4447 // Deletes remaining trustlines and deletes AMM.
-
4448 amm.ammDelete(alice);
-
4449 BEAST_EXPECT(!amm.ammExists());
-
4450 BEAST_EXPECT(!env.le(keylet::ownerDir(amm.ammAccount())));
-
4451
-
4452 // Try redundant delete
-
4453 amm.ammDelete(alice, ter(terNO_AMM));
-
4454 }
-
4455 }
-
-
4456
-
4457 void
-
- -
4459 {
-
4460 testcase("Clawback");
-
4461 using namespace jtx;
-
4462 Env env(*this);
-
4463 env.fund(XRP(2'000), gw);
-
4464 env.fund(XRP(2'000), alice);
-
4465 AMM amm(env, gw, XRP(1'000), USD(1'000));
- +
4401 // Bid,Vote,Deposit,Withdraw,SetTrust failing with
+
4402 // tecAMM_EMPTY. Deposit succeeds with tfTwoAssetIfEmpty option.
+
4403 env(amm.bid({
+
4404 .account = alice,
+
4405 .bidMin = 1000,
+
4406 }),
+
4407 ter(tecAMM_EMPTY));
+ +
4409 amm.withdraw(alice, 100, std::nullopt, std::nullopt, ter(tecAMM_EMPTY));
+
4410 amm.deposit(alice, USD(100), std::nullopt, std::nullopt, std::nullopt, ter(tecAMM_EMPTY));
+
4411 env(trust(alice, STAmount{amm.lptIssue(), 10'000}), ter(tecAMM_EMPTY));
+
4412
+
4413 // Can deposit with tfTwoAssetIfEmpty option
+
4414 amm.deposit(
+
4415 alice,
+ +
4417 XRP(10'000),
+
4418 USD(10'000),
+ + + + +
4423 1'000);
+
4424 BEAST_EXPECT(amm.expectBalances(XRP(10'000), USD(10'000), amm.tokens()));
+
4425 BEAST_EXPECT(amm.expectTradingFee(1'000));
+
4426 BEAST_EXPECT(amm.expectAuctionSlot(100, 0, IOUAmount{0}));
+
4427
+
4428 // Withdrawing all tokens deletes AMM since the number
+
4429 // of remaining trustlines is less than max
+
4430 amm.withdrawAll(alice);
+
4431 BEAST_EXPECT(!amm.ammExists());
+
4432 BEAST_EXPECT(!env.le(keylet::ownerDir(amm.ammAccount())));
+
4433 }
+
4434
+
4435 {
+
4436 Env env(
+
4437 *this,
+ +
4439 cfg->FEES.reference_fee = XRPAmount(1);
+
4440 return cfg;
+
4441 }),
+
4442 all);
+
4443 fund(env, gw, {alice}, XRP(20'000), {USD(10'000)});
+
4444 AMM amm(env, gw, XRP(10'000), USD(10'000));
+
4445 for (auto i = 0; i < maxDeletableAMMTrustLines * 2 + 10; ++i)
+
4446 {
+
4447 Account const a{std::to_string(i)};
+
4448 env.fund(XRP(1'000), a);
+
4449 env(trust(a, STAmount{amm.lptIssue(), 10'000}));
+
4450 env.close();
+
4451 }
+
4452 // The trustlines are partially deleted.
+
4453 amm.withdrawAll(gw);
+
4454 BEAST_EXPECT(amm.ammExists());
+
4455
+
4456 // AMMDelete has to be called twice to delete AMM.
+
4457 amm.ammDelete(alice, ter(tecINCOMPLETE));
+
4458 BEAST_EXPECT(amm.ammExists());
+
4459 // Deletes remaining trustlines and deletes AMM.
+
4460 amm.ammDelete(alice);
+
4461 BEAST_EXPECT(!amm.ammExists());
+
4462 BEAST_EXPECT(!env.le(keylet::ownerDir(amm.ammAccount())));
+
4463
+
4464 // Try redundant delete
+
4465 amm.ammDelete(alice, ter(terNO_AMM));
+
4466 }
4467 }
4468
4469 void
- +
4471 {
-
4472 testcase("AMMID");
+
4472 testcase("Clawback");
4473 using namespace jtx;
-
4474 testAMM([&](AMM& amm, Env& env) {
-
4475 amm.setClose(false);
-
4476 auto const info =
-
4477 env.rpc("json", "account_info", std::string("{\"account\": \"" + to_string(amm.ammAccount()) + "\"}"));
-
4478 try
-
4479 {
-
4480 BEAST_EXPECT(info[jss::result][jss::account_data][jss::AMMID].asString() == to_string(amm.ammID()));
-
4481 }
-
4482 catch (...)
-
4483 {
-
4484 fail();
-
4485 }
-
4486 amm.deposit(carol, 1'000);
-
4487 auto affected = env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName];
-
4488 try
-
4489 {
-
4490 bool found = false;
-
4491 for (auto const& node : affected)
-
4492 {
-
4493 if (node.isMember(sfModifiedNode.fieldName) &&
-
4494 node[sfModifiedNode.fieldName][sfLedgerEntryType.fieldName].asString() == "AccountRoot" &&
-
4495 node[sfModifiedNode.fieldName][sfFinalFields.fieldName][jss::Account].asString() ==
-
4496 to_string(amm.ammAccount()))
-
4497 {
-
4498 found = node[sfModifiedNode.fieldName][sfFinalFields.fieldName][jss::AMMID].asString() ==
-
4499 to_string(amm.ammID());
-
4500 break;
-
4501 }
-
4502 }
-
4503 BEAST_EXPECT(found);
-
4504 }
-
4505 catch (...)
-
4506 {
-
4507 fail();
-
4508 }
-
4509 });
-
4510 }
+
4474 Env env(*this);
+
4475 env.fund(XRP(2'000), gw);
+
4476 env.fund(XRP(2'000), alice);
+
4477 AMM amm(env, gw, XRP(1'000), USD(1'000));
+ +
4479 }
-
4511
-
4512 void
-
- -
4514 {
-
4515 testcase("Offer/Strand Selection");
-
4516 using namespace jtx;
-
4517 Account const ed("ed");
-
4518 Account const gw1("gw1");
-
4519 auto const ETH = gw1["ETH"];
-
4520 auto const CAN = gw1["CAN"];
-
4521
-
4522 // These tests are expected to fail if the OwnerPaysFee feature
-
4523 // is ever supported. Updates will need to be made to AMM handling
-
4524 // in the payment engine, and these tests will need to be updated.
-
4525
-
4526 auto prep = [&](Env& env, auto gwRate, auto gw1Rate) {
-
4527 fund(env, gw, {alice, carol, bob, ed}, XRP(2'000), {USD(2'000)});
-
4528 env.fund(XRP(2'000), gw1);
-
4529 fund(env, gw1, {alice, carol, bob, ed}, {ETH(2'000), CAN(2'000)}, Fund::IOUOnly);
-
4530 env(rate(gw, gwRate));
-
4531 env(rate(gw1, gw1Rate));
-
4532 env.close();
-
4533 };
-
4534
-
4535 for (auto const& rates : {std::make_pair(1.5, 1.9), std::make_pair(1.9, 1.5)})
-
4536 {
-
4537 // Offer Selection
-
4538
-
4539 // Cross-currency payment: AMM has the same spot price quality
-
4540 // as CLOB's offer and can't generate a better quality offer.
-
4541 // The transfer fee in this case doesn't change the CLOB quality
-
4542 // because trIn is ignored on adjustment and trOut on payment is
-
4543 // also ignored because ownerPaysTransferFee is false in this
-
4544 // case. Run test for 0) offer, 1) AMM, 2) offer and AMM to
-
4545 // verify that the quality is better in the first case, and CLOB
-
4546 // is selected in the second case.
-
4547 {
- -
4549 for (auto i = 0; i < 3; ++i)
-
4550 {
-
4551 Env env(*this, features);
-
4552 prep(env, rates.first, rates.second);
- -
4554 if (i == 0 || i == 2)
-
4555 {
-
4556 env(offer(ed, ETH(400), USD(400)), txflags(tfPassive));
-
4557 env.close();
-
4558 }
-
4559 if (i > 0)
-
4560 amm.emplace(env, ed, USD(1'000), ETH(1'000));
-
4561 env(pay(carol, bob, USD(100)), path(~USD), sendmax(ETH(500)));
-
4562 env.close();
-
4563 // CLOB and AMM, AMM is not selected
-
4564 if (i == 2)
-
4565 {
-
4566 BEAST_EXPECT(amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
-
4567 }
-
4568 BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
-
4569 q[i] = Quality(Amounts{ETH(2'000) - env.balance(carol, ETH), env.balance(bob, USD) - USD(2'000)});
-
4570 }
-
4571 // CLOB is better quality than AMM
-
4572 BEAST_EXPECT(q[0] > q[1]);
-
4573 // AMM is not selected with CLOB
-
4574 BEAST_EXPECT(q[0] == q[2]);
-
4575 }
-
4576 // Offer crossing: AMM has the same spot price quality
-
4577 // as CLOB's offer and can't generate a better quality offer.
-
4578 // The transfer fee in this case doesn't change the CLOB quality
-
4579 // because the quality adjustment is ignored for the offer
-
4580 // crossing.
-
4581 for (auto i = 0; i < 3; ++i)
-
4582 {
-
4583 Env env(*this, features);
-
4584 prep(env, rates.first, rates.second);
- -
4586 if (i == 0 || i == 2)
-
4587 {
-
4588 env(offer(ed, ETH(400), USD(400)), txflags(tfPassive));
-
4589 env.close();
-
4590 }
-
4591 if (i > 0)
-
4592 amm.emplace(env, ed, USD(1'000), ETH(1'000));
-
4593 env(offer(alice, USD(400), ETH(400)));
-
4594 env.close();
-
4595 // AMM is not selected
-
4596 if (i > 0)
-
4597 {
-
4598 BEAST_EXPECT(amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
-
4599 }
-
4600 if (i == 0 || i == 2)
-
4601 {
-
4602 // Fully crosses
-
4603 BEAST_EXPECT(expectOffers(env, alice, 0));
-
4604 }
-
4605 // Fails to cross because AMM is not selected
-
4606 else
-
4607 {
-
4608 BEAST_EXPECT(expectOffers(env, alice, 1, {Amounts{USD(400), ETH(400)}}));
-
4609 }
-
4610 BEAST_EXPECT(expectOffers(env, ed, 0));
-
4611 }
-
4612
-
4613 // Show that the CLOB quality reduction
-
4614 // results in AMM offer selection.
-
4615
-
4616 // Same as the payment but reduced offer quality
-
4617 {
- -
4619 for (auto i = 0; i < 3; ++i)
-
4620 {
-
4621 Env env(*this, features);
-
4622 prep(env, rates.first, rates.second);
- -
4624 if (i == 0 || i == 2)
-
4625 {
-
4626 env(offer(ed, ETH(400), USD(300)), txflags(tfPassive));
-
4627 env.close();
-
4628 }
-
4629 if (i > 0)
-
4630 amm.emplace(env, ed, USD(1'000), ETH(1'000));
-
4631 env(pay(carol, bob, USD(100)), path(~USD), sendmax(ETH(500)));
-
4632 env.close();
-
4633 // AMM and CLOB are selected
-
4634 if (i > 0)
-
4635 {
-
4636 BEAST_EXPECT(!amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
-
4637 }
-
4638 if (i == 2 && !features[fixAMMv1_1])
-
4639 {
-
4640 if (rates.first == 1.5)
-
4641 {
-
4642 if (!features[fixAMMv1_1])
-
4643 BEAST_EXPECT(expectOffers(
-
4644 env,
-
4645 ed,
-
4646 1,
-
4647 {{Amounts{
-
4648 STAmount{ETH, UINT64_C(378'6327949540823), -13},
-
4649 STAmount{USD, UINT64_C(283'9745962155617), -13}}}}));
-
4650 else
-
4651 BEAST_EXPECT(expectOffers(
-
4652 env,
-
4653 ed,
-
4654 1,
-
4655 {{Amounts{
-
4656 STAmount{ETH, UINT64_C(378'6327949540813), -13},
-
4657 STAmount{USD, UINT64_C(283'974596215561), -12}}}}));
-
4658 }
-
4659 else
-
4660 {
-
4661 if (!features[fixAMMv1_1])
-
4662 BEAST_EXPECT(expectOffers(
-
4663 env,
-
4664 ed,
-
4665 1,
-
4666 {{Amounts{
-
4667 STAmount{ETH, UINT64_C(325'299461620749), -12},
-
4668 STAmount{USD, UINT64_C(243'9745962155617), -13}}}}));
-
4669 else
-
4670 BEAST_EXPECT(expectOffers(
-
4671 env,
-
4672 ed,
-
4673 1,
-
4674 {{Amounts{
-
4675 STAmount{ETH, UINT64_C(325'299461620748), -12},
-
4676 STAmount{USD, UINT64_C(243'974596215561), -12}}}}));
-
4677 }
-
4678 }
-
4679 else if (i == 2)
-
4680 {
-
4681 if (rates.first == 1.5)
-
4682 {
-
4683 BEAST_EXPECT(expectOffers(
-
4684 env,
-
4685 ed,
-
4686 1,
-
4687 {{Amounts{
-
4688 STAmount{ETH, UINT64_C(378'6327949540812), -13},
-
4689 STAmount{USD, UINT64_C(283'9745962155609), -13}}}}));
-
4690 }
-
4691 else
-
4692 {
-
4693 BEAST_EXPECT(expectOffers(
-
4694 env,
-
4695 ed,
-
4696 1,
-
4697 {{Amounts{
-
4698 STAmount{ETH, UINT64_C(325'2994616207479), -13},
-
4699 STAmount{USD, UINT64_C(243'9745962155609), -13}}}}));
-
4700 }
-
4701 }
-
4702 BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
-
4703 q[i] = Quality(Amounts{ETH(2'000) - env.balance(carol, ETH), env.balance(bob, USD) - USD(2'000)});
-
4704 }
-
4705 // AMM is better quality
-
4706 BEAST_EXPECT(q[1] > q[0]);
-
4707 // AMM and CLOB produce better quality
-
4708 BEAST_EXPECT(q[2] > q[1]);
-
4709 }
-
4710
-
4711 // Same as the offer-crossing but reduced offer quality
-
4712 for (auto i = 0; i < 3; ++i)
-
4713 {
-
4714 Env env(*this, features);
-
4715 prep(env, rates.first, rates.second);
- -
4717 if (i == 0 || i == 2)
-
4718 {
-
4719 env(offer(ed, ETH(400), USD(250)), txflags(tfPassive));
-
4720 env.close();
-
4721 }
-
4722 if (i > 0)
-
4723 amm.emplace(env, ed, USD(1'000), ETH(1'000));
-
4724 env(offer(alice, USD(250), ETH(400)));
-
4725 env.close();
-
4726 // AMM is selected in both cases
-
4727 if (i > 0)
-
4728 {
-
4729 BEAST_EXPECT(!amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
-
4730 }
-
4731 // Partially crosses, AMM is selected, CLOB fails
-
4732 // limitQuality
-
4733 if (i == 2)
-
4734 {
-
4735 if (rates.first == 1.5)
-
4736 {
-
4737 if (!features[fixAMMv1_1])
-
4738 {
-
4739 BEAST_EXPECT(expectOffers(env, ed, 1, {{Amounts{ETH(400), USD(250)}}}));
-
4740 BEAST_EXPECT(expectOffers(
-
4741 env,
-
4742 alice,
-
4743 1,
-
4744 {{Amounts{
-
4745 STAmount{USD, UINT64_C(40'5694150420947), -13},
-
4746 STAmount{ETH, UINT64_C(64'91106406735152), -14},
-
4747 }}}));
-
4748 }
-
4749 else
+
4480
+
4481 void
+
+ +
4483 {
+
4484 testcase("AMMID");
+
4485 using namespace jtx;
+
4486 testAMM([&](AMM& amm, Env& env) {
+
4487 amm.setClose(false);
+
4488 auto const info =
+
4489 env.rpc("json", "account_info", std::string("{\"account\": \"" + to_string(amm.ammAccount()) + "\"}"));
+
4490 try
+
4491 {
+
4492 BEAST_EXPECT(info[jss::result][jss::account_data][jss::AMMID].asString() == to_string(amm.ammID()));
+
4493 }
+
4494 catch (...)
+
4495 {
+
4496 fail();
+
4497 }
+
4498 amm.deposit(carol, 1'000);
+
4499 auto affected = env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName];
+
4500 try
+
4501 {
+
4502 bool found = false;
+
4503 for (auto const& node : affected)
+
4504 {
+
4505 if (node.isMember(sfModifiedNode.fieldName) &&
+
4506 node[sfModifiedNode.fieldName][sfLedgerEntryType.fieldName].asString() == "AccountRoot" &&
+
4507 node[sfModifiedNode.fieldName][sfFinalFields.fieldName][jss::Account].asString() ==
+
4508 to_string(amm.ammAccount()))
+
4509 {
+
4510 found = node[sfModifiedNode.fieldName][sfFinalFields.fieldName][jss::AMMID].asString() ==
+
4511 to_string(amm.ammID());
+
4512 break;
+
4513 }
+
4514 }
+
4515 BEAST_EXPECT(found);
+
4516 }
+
4517 catch (...)
+
4518 {
+
4519 fail();
+
4520 }
+
4521 });
+
4522 }
+
+
4523
+
4524 void
+
+ +
4526 {
+
4527 testcase("Offer/Strand Selection");
+
4528 using namespace jtx;
+
4529 Account const ed("ed");
+
4530 Account const gw1("gw1");
+
4531 auto const ETH = gw1["ETH"];
+
4532 auto const CAN = gw1["CAN"];
+
4533
+
4534 // These tests are expected to fail if the OwnerPaysFee feature
+
4535 // is ever supported. Updates will need to be made to AMM handling
+
4536 // in the payment engine, and these tests will need to be updated.
+
4537
+
4538 auto prep = [&](Env& env, auto gwRate, auto gw1Rate) {
+
4539 fund(env, gw, {alice, carol, bob, ed}, XRP(2'000), {USD(2'000)});
+
4540 env.fund(XRP(2'000), gw1);
+
4541 fund(env, gw1, {alice, carol, bob, ed}, {ETH(2'000), CAN(2'000)}, Fund::IOUOnly);
+
4542 env(rate(gw, gwRate));
+
4543 env(rate(gw1, gw1Rate));
+
4544 env.close();
+
4545 };
+
4546
+
4547 for (auto const& rates : {std::make_pair(1.5, 1.9), std::make_pair(1.9, 1.5)})
+
4548 {
+
4549 // Offer Selection
+
4550
+
4551 // Cross-currency payment: AMM has the same spot price quality
+
4552 // as CLOB's offer and can't generate a better quality offer.
+
4553 // The transfer fee in this case doesn't change the CLOB quality
+
4554 // because trIn is ignored on adjustment and trOut on payment is
+
4555 // also ignored because ownerPaysTransferFee is false in this
+
4556 // case. Run test for 0) offer, 1) AMM, 2) offer and AMM to
+
4557 // verify that the quality is better in the first case, and CLOB
+
4558 // is selected in the second case.
+
4559 {
+ +
4561 for (auto i = 0; i < 3; ++i)
+
4562 {
+
4563 Env env(*this, features);
+
4564 prep(env, rates.first, rates.second);
+ +
4566 if (i == 0 || i == 2)
+
4567 {
+
4568 env(offer(ed, ETH(400), USD(400)), txflags(tfPassive));
+
4569 env.close();
+
4570 }
+
4571 if (i > 0)
+
4572 amm.emplace(env, ed, USD(1'000), ETH(1'000));
+
4573 env(pay(carol, bob, USD(100)), path(~USD), sendmax(ETH(500)));
+
4574 env.close();
+
4575 // CLOB and AMM, AMM is not selected
+
4576 if (i == 2)
+
4577 {
+
4578 BEAST_EXPECT(amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
+
4579 }
+
4580 BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
+
4581 q[i] = Quality(Amounts{ETH(2'000) - env.balance(carol, ETH), env.balance(bob, USD) - USD(2'000)});
+
4582 }
+
4583 // CLOB is better quality than AMM
+
4584 BEAST_EXPECT(q[0] > q[1]);
+
4585 // AMM is not selected with CLOB
+
4586 BEAST_EXPECT(q[0] == q[2]);
+
4587 }
+
4588 // Offer crossing: AMM has the same spot price quality
+
4589 // as CLOB's offer and can't generate a better quality offer.
+
4590 // The transfer fee in this case doesn't change the CLOB quality
+
4591 // because the quality adjustment is ignored for the offer
+
4592 // crossing.
+
4593 for (auto i = 0; i < 3; ++i)
+
4594 {
+
4595 Env env(*this, features);
+
4596 prep(env, rates.first, rates.second);
+ +
4598 if (i == 0 || i == 2)
+
4599 {
+
4600 env(offer(ed, ETH(400), USD(400)), txflags(tfPassive));
+
4601 env.close();
+
4602 }
+
4603 if (i > 0)
+
4604 amm.emplace(env, ed, USD(1'000), ETH(1'000));
+
4605 env(offer(alice, USD(400), ETH(400)));
+
4606 env.close();
+
4607 // AMM is not selected
+
4608 if (i > 0)
+
4609 {
+
4610 BEAST_EXPECT(amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
+
4611 }
+
4612 if (i == 0 || i == 2)
+
4613 {
+
4614 // Fully crosses
+
4615 BEAST_EXPECT(expectOffers(env, alice, 0));
+
4616 }
+
4617 // Fails to cross because AMM is not selected
+
4618 else
+
4619 {
+
4620 BEAST_EXPECT(expectOffers(env, alice, 1, {Amounts{USD(400), ETH(400)}}));
+
4621 }
+
4622 BEAST_EXPECT(expectOffers(env, ed, 0));
+
4623 }
+
4624
+
4625 // Show that the CLOB quality reduction
+
4626 // results in AMM offer selection.
+
4627
+
4628 // Same as the payment but reduced offer quality
+
4629 {
+ +
4631 for (auto i = 0; i < 3; ++i)
+
4632 {
+
4633 Env env(*this, features);
+
4634 prep(env, rates.first, rates.second);
+ +
4636 if (i == 0 || i == 2)
+
4637 {
+
4638 env(offer(ed, ETH(400), USD(300)), txflags(tfPassive));
+
4639 env.close();
+
4640 }
+
4641 if (i > 0)
+
4642 amm.emplace(env, ed, USD(1'000), ETH(1'000));
+
4643 env(pay(carol, bob, USD(100)), path(~USD), sendmax(ETH(500)));
+
4644 env.close();
+
4645 // AMM and CLOB are selected
+
4646 if (i > 0)
+
4647 {
+
4648 BEAST_EXPECT(!amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
+
4649 }
+
4650 if (i == 2 && !features[fixAMMv1_1])
+
4651 {
+
4652 if (rates.first == 1.5)
+
4653 {
+
4654 if (!features[fixAMMv1_1])
+
4655 BEAST_EXPECT(expectOffers(
+
4656 env,
+
4657 ed,
+
4658 1,
+
4659 {{Amounts{
+
4660 STAmount{ETH, UINT64_C(378'6327949540823), -13},
+
4661 STAmount{USD, UINT64_C(283'9745962155617), -13}}}}));
+
4662 else
+
4663 BEAST_EXPECT(expectOffers(
+
4664 env,
+
4665 ed,
+
4666 1,
+
4667 {{Amounts{
+
4668 STAmount{ETH, UINT64_C(378'6327949540813), -13},
+
4669 STAmount{USD, UINT64_C(283'974596215561), -12}}}}));
+
4670 }
+
4671 else
+
4672 {
+
4673 if (!features[fixAMMv1_1])
+
4674 BEAST_EXPECT(expectOffers(
+
4675 env,
+
4676 ed,
+
4677 1,
+
4678 {{Amounts{
+
4679 STAmount{ETH, UINT64_C(325'299461620749), -12},
+
4680 STAmount{USD, UINT64_C(243'9745962155617), -13}}}}));
+
4681 else
+
4682 BEAST_EXPECT(expectOffers(
+
4683 env,
+
4684 ed,
+
4685 1,
+
4686 {{Amounts{
+
4687 STAmount{ETH, UINT64_C(325'299461620748), -12},
+
4688 STAmount{USD, UINT64_C(243'974596215561), -12}}}}));
+
4689 }
+
4690 }
+
4691 else if (i == 2)
+
4692 {
+
4693 if (rates.first == 1.5)
+
4694 {
+
4695 BEAST_EXPECT(expectOffers(
+
4696 env,
+
4697 ed,
+
4698 1,
+
4699 {{Amounts{
+
4700 STAmount{ETH, UINT64_C(378'6327949540812), -13},
+
4701 STAmount{USD, UINT64_C(283'9745962155609), -13}}}}));
+
4702 }
+
4703 else
+
4704 {
+
4705 BEAST_EXPECT(expectOffers(
+
4706 env,
+
4707 ed,
+
4708 1,
+
4709 {{Amounts{
+
4710 STAmount{ETH, UINT64_C(325'2994616207479), -13},
+
4711 STAmount{USD, UINT64_C(243'9745962155609), -13}}}}));
+
4712 }
+
4713 }
+
4714 BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
+
4715 q[i] = Quality(Amounts{ETH(2'000) - env.balance(carol, ETH), env.balance(bob, USD) - USD(2'000)});
+
4716 }
+
4717 // AMM is better quality
+
4718 BEAST_EXPECT(q[1] > q[0]);
+
4719 // AMM and CLOB produce better quality
+
4720 BEAST_EXPECT(q[2] > q[1]);
+
4721 }
+
4722
+
4723 // Same as the offer-crossing but reduced offer quality
+
4724 for (auto i = 0; i < 3; ++i)
+
4725 {
+
4726 Env env(*this, features);
+
4727 prep(env, rates.first, rates.second);
+ +
4729 if (i == 0 || i == 2)
+
4730 {
+
4731 env(offer(ed, ETH(400), USD(250)), txflags(tfPassive));
+
4732 env.close();
+
4733 }
+
4734 if (i > 0)
+
4735 amm.emplace(env, ed, USD(1'000), ETH(1'000));
+
4736 env(offer(alice, USD(250), ETH(400)));
+
4737 env.close();
+
4738 // AMM is selected in both cases
+
4739 if (i > 0)
+
4740 {
+
4741 BEAST_EXPECT(!amm->expectBalances(USD(1'000), ETH(1'000), amm->tokens()));
+
4742 }
+
4743 // Partially crosses, AMM is selected, CLOB fails
+
4744 // limitQuality
+
4745 if (i == 2)
+
4746 {
+
4747 if (rates.first == 1.5)
+
4748 {
+
4749 if (!features[fixAMMv1_1])
4750 {
-
4751 // Ed offer is partially crossed.
-
4752 // The updated rounding makes limitQuality
-
4753 // work if both amendments are enabled
-
4754 BEAST_EXPECT(expectOffers(
-
4755 env,
-
4756 ed,
-
4757 1,
-
4758 {{Amounts{
-
4759 STAmount{ETH, UINT64_C(335'0889359326475), -13},
-
4760 STAmount{USD, UINT64_C(209'4305849579047), -13},
-
4761 }}}));
-
4762 BEAST_EXPECT(expectOffers(env, alice, 0));
-
4763 }
-
4764 }
-
4765 else
-
4766 {
-
4767 if (!features[fixAMMv1_1])
-
4768 {
-
4769 // Ed offer is partially crossed.
-
4770 BEAST_EXPECT(expectOffers(
-
4771 env,
-
4772 ed,
-
4773 1,
-
4774 {{Amounts{
-
4775 STAmount{ETH, UINT64_C(335'0889359326485), -13},
-
4776 STAmount{USD, UINT64_C(209'4305849579053), -13},
-
4777 }}}));
-
4778 BEAST_EXPECT(expectOffers(env, alice, 0));
-
4779 }
-
4780 else
-
4781 {
-
4782 // Ed offer is partially crossed.
-
4783 BEAST_EXPECT(expectOffers(
-
4784 env,
-
4785 ed,
-
4786 1,
-
4787 {{Amounts{
-
4788 STAmount{ETH, UINT64_C(335'0889359326475), -13},
-
4789 STAmount{USD, UINT64_C(209'4305849579047), -13},
-
4790 }}}));
-
4791 BEAST_EXPECT(expectOffers(env, alice, 0));
-
4792 }
-
4793 }
-
4794 }
-
4795 }
-
4796
-
4797 // Strand selection
-
4798
-
4799 // Two book steps strand quality is 1.
-
4800 // AMM strand's best quality is equal to AMM's spot price
-
4801 // quality, which is 1. Both strands (steps) are adjusted
-
4802 // for the transfer fee in qualityUpperBound. In case
-
4803 // of two strands, AMM offers have better quality and are
-
4804 // consumed first, remaining liquidity is generated by CLOB
-
4805 // offers. Liquidity from two strands is better in this case
-
4806 // than in case of one strand with two book steps. Liquidity
-
4807 // from one strand with AMM has better quality than either one
-
4808 // strand with two book steps or two strands. It may appear
-
4809 // unintuitive, but one strand with AMM is optimized and
-
4810 // generates one AMM offer, while in case of two strands,
-
4811 // multiple AMM offers are generated, which results in slightly
-
4812 // worse overall quality.
-
4813 {
- -
4815 for (auto i = 0; i < 3; ++i)
-
4816 {
-
4817 Env env(*this, features);
-
4818 prep(env, rates.first, rates.second);
- -
4820
-
4821 if (i == 0 || i == 2)
-
4822 {
-
4823 env(offer(ed, ETH(400), CAN(400)), txflags(tfPassive));
-
4824 env(offer(ed, CAN(400), USD(400))), txflags(tfPassive);
-
4825 env.close();
-
4826 }
-
4827
-
4828 if (i > 0)
-
4829 amm.emplace(env, ed, ETH(1'000), USD(1'000));
-
4830
-
4831 env(pay(carol, bob, USD(100)), path(~USD), path(~CAN, ~USD), sendmax(ETH(600)));
-
4832 env.close();
-
4833
-
4834 BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
-
4835
-
4836 if (i == 2 && !features[fixAMMv1_1])
-
4837 {
-
4838 if (rates.first == 1.5)
-
4839 {
-
4840 // Liquidity is consumed from AMM strand only
-
4841 BEAST_EXPECT(amm->expectBalances(
-
4842 STAmount{ETH, UINT64_C(1'176'66038955758), -11}, USD(850), amm->tokens()));
-
4843 }
-
4844 else
-
4845 {
-
4846 BEAST_EXPECT(amm->expectBalances(
-
4847 STAmount{ETH, UINT64_C(1'179'540094339627), -12},
-
4848 STAmount{USD, UINT64_C(847'7880529867501), -13},
-
4849 amm->tokens()));
-
4850 BEAST_EXPECT(expectOffers(
-
4851 env,
-
4852 ed,
-
4853 2,
-
4854 {{Amounts{
-
4855 STAmount{ETH, UINT64_C(343'3179205198749), -13},
-
4856 STAmount{CAN, UINT64_C(343'3179205198749), -13},
-
4857 },
-
4858 Amounts{
-
4859 STAmount{CAN, UINT64_C(362'2119470132499), -13},
-
4860 STAmount{USD, UINT64_C(362'2119470132499), -13},
-
4861 }}}));
-
4862 }
-
4863 }
-
4864 else if (i == 2)
-
4865 {
-
4866 if (rates.first == 1.5)
-
4867 {
-
4868 // Liquidity is consumed from AMM strand only
-
4869 BEAST_EXPECT(amm->expectBalances(
-
4870 STAmount{ETH, UINT64_C(1'176'660389557593), -12}, USD(850), amm->tokens()));
-
4871 }
-
4872 else
-
4873 {
-
4874 BEAST_EXPECT(amm->expectBalances(
-
4875 STAmount{ETH, UINT64_C(1'179'54009433964), -11},
-
4876 STAmount{USD, UINT64_C(847'7880529867501), -13},
-
4877 amm->tokens()));
-
4878 BEAST_EXPECT(expectOffers(
-
4879 env,
-
4880 ed,
-
4881 2,
-
4882 {{Amounts{
-
4883 STAmount{ETH, UINT64_C(343'3179205198749), -13},
-
4884 STAmount{CAN, UINT64_C(343'3179205198749), -13},
-
4885 },
-
4886 Amounts{
-
4887 STAmount{CAN, UINT64_C(362'2119470132499), -13},
-
4888 STAmount{USD, UINT64_C(362'2119470132499), -13},
-
4889 }}}));
-
4890 }
-
4891 }
-
4892 q[i] = Quality(Amounts{ETH(2'000) - env.balance(carol, ETH), env.balance(bob, USD) - USD(2'000)});
-
4893 }
-
4894 BEAST_EXPECT(q[1] > q[0]);
-
4895 BEAST_EXPECT(q[2] > q[0] && q[2] < q[1]);
-
4896 }
-
4897 }
-
4898 }
+
4751 BEAST_EXPECT(expectOffers(env, ed, 1, {{Amounts{ETH(400), USD(250)}}}));
+
4752 BEAST_EXPECT(expectOffers(
+
4753 env,
+
4754 alice,
+
4755 1,
+
4756 {{Amounts{
+
4757 STAmount{USD, UINT64_C(40'5694150420947), -13},
+
4758 STAmount{ETH, UINT64_C(64'91106406735152), -14},
+
4759 }}}));
+
4760 }
+
4761 else
+
4762 {
+
4763 // Ed offer is partially crossed.
+
4764 // The updated rounding makes limitQuality
+
4765 // work if both amendments are enabled
+
4766 BEAST_EXPECT(expectOffers(
+
4767 env,
+
4768 ed,
+
4769 1,
+
4770 {{Amounts{
+
4771 STAmount{ETH, UINT64_C(335'0889359326475), -13},
+
4772 STAmount{USD, UINT64_C(209'4305849579047), -13},
+
4773 }}}));
+
4774 BEAST_EXPECT(expectOffers(env, alice, 0));
+
4775 }
+
4776 }
+
4777 else
+
4778 {
+
4779 if (!features[fixAMMv1_1])
+
4780 {
+
4781 // Ed offer is partially crossed.
+
4782 BEAST_EXPECT(expectOffers(
+
4783 env,
+
4784 ed,
+
4785 1,
+
4786 {{Amounts{
+
4787 STAmount{ETH, UINT64_C(335'0889359326485), -13},
+
4788 STAmount{USD, UINT64_C(209'4305849579053), -13},
+
4789 }}}));
+
4790 BEAST_EXPECT(expectOffers(env, alice, 0));
+
4791 }
+
4792 else
+
4793 {
+
4794 // Ed offer is partially crossed.
+
4795 BEAST_EXPECT(expectOffers(
+
4796 env,
+
4797 ed,
+
4798 1,
+
4799 {{Amounts{
+
4800 STAmount{ETH, UINT64_C(335'0889359326475), -13},
+
4801 STAmount{USD, UINT64_C(209'4305849579047), -13},
+
4802 }}}));
+
4803 BEAST_EXPECT(expectOffers(env, alice, 0));
+
4804 }
+
4805 }
+
4806 }
+
4807 }
+
4808
+
4809 // Strand selection
+
4810
+
4811 // Two book steps strand quality is 1.
+
4812 // AMM strand's best quality is equal to AMM's spot price
+
4813 // quality, which is 1. Both strands (steps) are adjusted
+
4814 // for the transfer fee in qualityUpperBound. In case
+
4815 // of two strands, AMM offers have better quality and are
+
4816 // consumed first, remaining liquidity is generated by CLOB
+
4817 // offers. Liquidity from two strands is better in this case
+
4818 // than in case of one strand with two book steps. Liquidity
+
4819 // from one strand with AMM has better quality than either one
+
4820 // strand with two book steps or two strands. It may appear
+
4821 // unintuitive, but one strand with AMM is optimized and
+
4822 // generates one AMM offer, while in case of two strands,
+
4823 // multiple AMM offers are generated, which results in slightly
+
4824 // worse overall quality.
+
4825 {
+ +
4827 for (auto i = 0; i < 3; ++i)
+
4828 {
+
4829 Env env(*this, features);
+
4830 prep(env, rates.first, rates.second);
+ +
4832
+
4833 if (i == 0 || i == 2)
+
4834 {
+
4835 env(offer(ed, ETH(400), CAN(400)), txflags(tfPassive));
+
4836 env(offer(ed, CAN(400), USD(400))), txflags(tfPassive);
+
4837 env.close();
+
4838 }
+
4839
+
4840 if (i > 0)
+
4841 amm.emplace(env, ed, ETH(1'000), USD(1'000));
+
4842
+
4843 env(pay(carol, bob, USD(100)), path(~USD), path(~CAN, ~USD), sendmax(ETH(600)));
+
4844 env.close();
+
4845
+
4846 BEAST_EXPECT(expectHolding(env, bob, USD(2'100)));
+
4847
+
4848 if (i == 2 && !features[fixAMMv1_1])
+
4849 {
+
4850 if (rates.first == 1.5)
+
4851 {
+
4852 // Liquidity is consumed from AMM strand only
+
4853 BEAST_EXPECT(amm->expectBalances(
+
4854 STAmount{ETH, UINT64_C(1'176'66038955758), -11}, USD(850), amm->tokens()));
+
4855 }
+
4856 else
+
4857 {
+
4858 BEAST_EXPECT(amm->expectBalances(
+
4859 STAmount{ETH, UINT64_C(1'179'540094339627), -12},
+
4860 STAmount{USD, UINT64_C(847'7880529867501), -13},
+
4861 amm->tokens()));
+
4862 BEAST_EXPECT(expectOffers(
+
4863 env,
+
4864 ed,
+
4865 2,
+
4866 {{Amounts{
+
4867 STAmount{ETH, UINT64_C(343'3179205198749), -13},
+
4868 STAmount{CAN, UINT64_C(343'3179205198749), -13},
+
4869 },
+
4870 Amounts{
+
4871 STAmount{CAN, UINT64_C(362'2119470132499), -13},
+
4872 STAmount{USD, UINT64_C(362'2119470132499), -13},
+
4873 }}}));
+
4874 }
+
4875 }
+
4876 else if (i == 2)
+
4877 {
+
4878 if (rates.first == 1.5)
+
4879 {
+
4880 // Liquidity is consumed from AMM strand only
+
4881 BEAST_EXPECT(amm->expectBalances(
+
4882 STAmount{ETH, UINT64_C(1'176'660389557593), -12}, USD(850), amm->tokens()));
+
4883 }
+
4884 else
+
4885 {
+
4886 BEAST_EXPECT(amm->expectBalances(
+
4887 STAmount{ETH, UINT64_C(1'179'54009433964), -11},
+
4888 STAmount{USD, UINT64_C(847'7880529867501), -13},
+
4889 amm->tokens()));
+
4890 BEAST_EXPECT(expectOffers(
+
4891 env,
+
4892 ed,
+
4893 2,
+
4894 {{Amounts{
+
4895 STAmount{ETH, UINT64_C(343'3179205198749), -13},
+
4896 STAmount{CAN, UINT64_C(343'3179205198749), -13},
+
4897 },
+
4898 Amounts{
+
4899 STAmount{CAN, UINT64_C(362'2119470132499), -13},
+
4900 STAmount{USD, UINT64_C(362'2119470132499), -13},
+
4901 }}}));
+
4902 }
+
4903 }
+
4904 q[i] = Quality(Amounts{ETH(2'000) - env.balance(carol, ETH), env.balance(bob, USD) - USD(2'000)});
+
4905 }
+
4906 BEAST_EXPECT(q[1] > q[0]);
+
4907 BEAST_EXPECT(q[2] > q[0] && q[2] < q[1]);
+
4908 }
+
4909 }
+
4910 }
-
4899
-
4900 void
-
- -
4902 {
-
4903 testcase("Fix Default Inner Object");
-
4904 using namespace jtx;
- -
4906
-
4907 auto test = [&](FeatureBitset features,
-
4908 TER const& err1,
-
4909 TER const& err2,
-
4910 TER const& err3,
-
4911 TER const& err4,
-
4912 std::uint16_t tfee,
-
4913 bool closeLedger,
- -
4915 Env env(*this, features);
-
4916 fund(env, gw, {alice}, XRP(1'000), {USD(10)});
-
4917 AMM amm(env, gw, XRP(10), USD(10), {.tfee = tfee, .close = closeLedger});
-
4918 amm.deposit(alice, USD(10), XRP(10));
-
4919 amm.vote(VoteArg{.account = alice, .tfee = tfee, .err = ter(err1)});
-
4920 amm.withdraw(WithdrawArg{.account = gw, .asset1Out = USD(1), .err = ter(err2)});
-
4921 // with the amendment disabled and ledger not closed,
-
4922 // second vote succeeds if the first vote sets the trading fee
-
4923 // to non-zero; if the first vote sets the trading fee to >0 &&
-
4924 // <9 then the second withdraw succeeds if the second vote sets
-
4925 // the trading fee so that the discounted fee is non-zero
-
4926 amm.vote(VoteArg{.account = alice, .tfee = 20, .err = ter(err3)});
-
4927 amm.withdraw(WithdrawArg{.account = gw, .asset1Out = USD(2), .err = ter(err4)});
-
4928 };
-
4929
-
4930 // ledger is closed after each transaction, vote/withdraw don't fail
-
4931 // regardless whether the amendment is enabled or not
-
4932 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 0, true);
-
4933 test(all - fixInnerObjTemplate, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 0, true);
-
4934 // ledger is not closed after each transaction
-
4935 // vote/withdraw don't fail if the amendment is enabled
-
4936 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 0, false);
-
4937 // vote/withdraw fail if the amendment is not enabled
-
4938 // second vote/withdraw still fail: second vote fails because
-
4939 // the initial trading fee is 0, consequently second withdraw fails
-
4940 // because the second vote fails
-
4941 test(all - fixInnerObjTemplate, tefEXCEPTION, tefEXCEPTION, tefEXCEPTION, tefEXCEPTION, 0, false);
-
4942 // if non-zero trading/discounted fee then vote/withdraw
-
4943 // don't fail whether the ledger is closed or not and
-
4944 // the amendment is enabled or not
-
4945 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, true);
-
4946 test(all - fixInnerObjTemplate, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, true);
-
4947 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, false);
-
4948 test(all - fixInnerObjTemplate, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, false);
-
4949 // non-zero trading fee but discounted fee is 0, vote doesn't fail
-
4950 // but withdraw fails
-
4951 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 9, false);
-
4952 // second vote sets the trading fee to non-zero, consequently
-
4953 // second withdraw doesn't fail even if the amendment is not
-
4954 // enabled and the ledger is not closed
-
4955 test(all - fixInnerObjTemplate, tesSUCCESS, tefEXCEPTION, tesSUCCESS, tesSUCCESS, 9, false);
-
4956 }
+
4911
+
4912 void
+
+ +
4914 {
+
4915 testcase("Fix Default Inner Object");
+
4916 using namespace jtx;
+ +
4918
+
4919 auto test = [&](FeatureBitset features,
+
4920 TER const& err1,
+
4921 TER const& err2,
+
4922 TER const& err3,
+
4923 TER const& err4,
+
4924 std::uint16_t tfee,
+
4925 bool closeLedger,
+ +
4927 Env env(*this, features);
+
4928 fund(env, gw, {alice}, XRP(1'000), {USD(10)});
+
4929 AMM amm(env, gw, XRP(10), USD(10), {.tfee = tfee, .close = closeLedger});
+
4930 amm.deposit(alice, USD(10), XRP(10));
+
4931 amm.vote(VoteArg{.account = alice, .tfee = tfee, .err = ter(err1)});
+
4932 amm.withdraw(WithdrawArg{.account = gw, .asset1Out = USD(1), .err = ter(err2)});
+
4933 // with the amendment disabled and ledger not closed,
+
4934 // second vote succeeds if the first vote sets the trading fee
+
4935 // to non-zero; if the first vote sets the trading fee to >0 &&
+
4936 // <9 then the second withdraw succeeds if the second vote sets
+
4937 // the trading fee so that the discounted fee is non-zero
+
4938 amm.vote(VoteArg{.account = alice, .tfee = 20, .err = ter(err3)});
+
4939 amm.withdraw(WithdrawArg{.account = gw, .asset1Out = USD(2), .err = ter(err4)});
+
4940 };
+
4941
+
4942 // ledger is closed after each transaction, vote/withdraw don't fail
+
4943 // regardless whether the amendment is enabled or not
+
4944 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 0, true);
+
4945 test(all - fixInnerObjTemplate, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 0, true);
+
4946 // ledger is not closed after each transaction
+
4947 // vote/withdraw don't fail if the amendment is enabled
+
4948 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 0, false);
+
4949 // vote/withdraw fail if the amendment is not enabled
+
4950 // second vote/withdraw still fail: second vote fails because
+
4951 // the initial trading fee is 0, consequently second withdraw fails
+
4952 // because the second vote fails
+
4953 test(all - fixInnerObjTemplate, tefEXCEPTION, tefEXCEPTION, tefEXCEPTION, tefEXCEPTION, 0, false);
+
4954 // if non-zero trading/discounted fee then vote/withdraw
+
4955 // don't fail whether the ledger is closed or not and
+
4956 // the amendment is enabled or not
+
4957 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, true);
+
4958 test(all - fixInnerObjTemplate, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, true);
+
4959 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, false);
+
4960 test(all - fixInnerObjTemplate, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 10, false);
+
4961 // non-zero trading fee but discounted fee is 0, vote doesn't fail
+
4962 // but withdraw fails
+
4963 test(all, tesSUCCESS, tesSUCCESS, tesSUCCESS, tesSUCCESS, 9, false);
+
4964 // second vote sets the trading fee to non-zero, consequently
+
4965 // second withdraw doesn't fail even if the amendment is not
+
4966 // enabled and the ledger is not closed
+
4967 test(all - fixInnerObjTemplate, tesSUCCESS, tefEXCEPTION, tesSUCCESS, tesSUCCESS, 9, false);
+
4968 }
-
4957
-
4958 void
-
- -
4960 {
-
4961 testcase("Fix changeSpotPriceQuality");
-
4962 using namespace jtx;
-
4963
-
4964 std::string logs;
-
4965
-
4966 enum class Status {
-
4967 SucceedShouldSucceedResize, // Succeed in pre-fix because
-
4968 // error allowance, succeed post-fix
-
4969 // because of offer resizing
-
4970 FailShouldSucceed, // Fail in pre-fix due to rounding,
-
4971 // succeed after fix because of XRP
-
4972 // side is generated first
-
4973 SucceedShouldFail, // Succeed in pre-fix, fail after fix
-
4974 // due to small quality difference
-
4975 Fail, // Both fail because the quality can't be matched
-
4976 Succeed // Both succeed
-
4977 };
-
4978 using enum Status;
-
4979 auto const xrpIouAmounts10_100 = TAmounts{XRPAmount{10}, IOUAmount{100}};
-
4980 auto const iouXrpAmounts10_100 = TAmounts{IOUAmount{10}, XRPAmount{100}};
-
4981 // clang-format off
- -
4983 //Pool In , Pool Out, Quality , Fee, Status
-
4984 {"0.001519763260828713", "1558701", Quality{5414253689393440221}, 1000, FailShouldSucceed},
-
4985 {"0.01099814367603737", "1892611", Quality{5482264816516900274}, 1000, FailShouldSucceed},
-
4986 {"0.78", "796599", Quality{5630392334958379008}, 1000, FailShouldSucceed},
-
4987 {"105439.2955578965", "49398693", Quality{5910869983721805038}, 400, FailShouldSucceed},
-
4988 {"12408293.23445213", "4340810521", Quality{5911611095910090752}, 997, FailShouldSucceed},
-
4989 {"1892611", "0.01099814367603737", Quality{6703103457950430139}, 1000, FailShouldSucceed},
-
4990 {"423028.8508101858", "3392804520", Quality{5837920340654162816}, 600, FailShouldSucceed},
-
4991 {"44565388.41001027", "73890647", Quality{6058976634606450001}, 1000, FailShouldSucceed},
-
4992 {"66831.68494832662", "16", Quality{6346111134641742975}, 0, FailShouldSucceed},
-
4993 {"675.9287302203422", "1242632304", Quality{5625960929244093294}, 300, FailShouldSucceed},
-
4994 {"7047.112186735699", "1649845866", Quality{5696855348026306945}, 504, FailShouldSucceed},
-
4995 {"840236.4402981238", "47419053", Quality{5982561601648018688}, 499, FailShouldSucceed},
-
4996 {"992715.618909774", "189445631733", Quality{5697835648288106944}, 815, SucceedShouldSucceedResize},
-
4997 {"504636667521", "185545883.9506651", Quality{6343802275337659280}, 503, SucceedShouldSucceedResize},
-
4998 {"992706.7218636649", "189447316000", Quality{5697835648288106944}, 797, SucceedShouldSucceedResize},
-
4999 {"1.068737911388205", "127860278877", Quality{5268604356368739396}, 293, SucceedShouldSucceedResize},
-
5000 {"17932506.56880419", "189308.6043676173", Quality{6206460598195440068}, 311, SucceedShouldSucceedResize},
-
5001 {"1.066379294658174", "128042251493", Quality{5268559341368739328}, 270, SucceedShouldSucceedResize},
-
5002 {"350131413924", "1576879.110907892", Quality{6487411636539049449}, 650, Fail},
-
5003 {"422093460", "2.731797662057464", Quality{6702911108534394924}, 1000, Fail},
-
5004 {"76128132223", "367172.7148422662", Quality{6487263463413514240}, 548, Fail},
-
5005 {"132701839250", "280703770.7695443", Quality{6273750681188885075}, 562, Fail},
-
5006 {"994165.7604612011", "189551302411", Quality{5697835592690668727}, 815, Fail},
-
5007 {"45053.33303227917", "86612695359", Quality{5625695218943638190}, 500, Fail},
-
5008 {"199649.077043865", "14017933007", Quality{5766034667318524880}, 324, Fail},
-
5009 {"27751824831.70903", "78896950", Quality{6272538159621630432}, 500, Fail},
-
5010 {"225.3731275781907", "156431793648", Quality{5477818047604078924}, 989, Fail},
-
5011 {"199649.077043865", "14017933007", Quality{5766036094462806309}, 324, Fail},
-
5012 {"3.590272027140361", "20677643641", Quality{5406056147042156356}, 808, Fail},
-
5013 {"1.070884664490231", "127604712776", Quality{5268620608623825741}, 293, Fail},
-
5014 {"3272.448829820197", "6275124076", Quality{5625710328924117902}, 81, Fail},
-
5015 {"0.009059512633902926", "7994028", Quality{5477511954775533172}, 1000, Fail},
-
5016 {"1", "1.0", Quality{0}, 100, Fail},
-
5017 {"1.0", "1", Quality{0}, 100, Fail},
-
5018 {"10", "10.0", Quality{xrpIouAmounts10_100}, 100, Fail},
-
5019 {"10.0", "10", Quality{iouXrpAmounts10_100}, 100, Fail},
-
5020 {"69864389131", "287631.4543025075", Quality{6487623473313516078}, 451, Succeed},
-
5021 {"4328342973", "12453825.99247381", Quality{6272522264364865181}, 997, Succeed},
-
5022 {"32347017", "7003.93031579449", Quality{6347261126087916670}, 1000, Succeed},
-
5023 {"61697206161", "36631.4583206413", Quality{6558965195382476659}, 500, Succeed},
-
5024 {"1654524979", "7028.659825511603", Quality{6487551345110052981}, 504, Succeed},
-
5025 {"88621.22277293179", "5128418948", Quality{5766347291552869205}, 380, Succeed},
-
5026 {"1892611", "0.01099814367603737", Quality{6703102780512015436}, 1000, Succeed},
-
5027 {"4542.639373338766", "24554809", Quality{5838994982188783710}, 0, Succeed},
-
5028 {"5132932546", "88542.99750172683", Quality{6419203342950054537}, 380, Succeed},
-
5029 {"78929964.1549083", "1506494795", Quality{5986890029845558688}, 589, Succeed},
-
5030 {"10096561906", "44727.72453735605", Quality{6487455290284644551}, 250, Succeed},
-
5031 {"5092.219565514988", "8768257694", Quality{5626349534958379008}, 503, Succeed},
-
5032 {"1819778294", "8305.084302902864", Quality{6487429398998540860}, 415, Succeed},
-
5033 {"6970462.633911943", "57359281", Quality{6054087899185946624}, 850, Succeed},
-
5034 {"3983448845", "2347.543644281467", Quality{6558965195382476659}, 856, Succeed},
-
5035 // This is a tiny offer 12drops/19321952e-15 it succeeds pre-amendment because of the error allowance.
-
5036 // Post amendment it is resized to 11drops/17711789e-15 but the quality is still less than
-
5037 // the target quality and the offer fails.
-
5038 {"771493171", "1.243473020567508", Quality{6707566798038544272}, 100, SucceedShouldFail},
-
5039 };
-
5040 // clang-format on
-
5041
-
5042 boost::regex rx("^\\d+$");
-
5043 boost::smatch match;
-
5044 // tests that succeed should have the same amounts pre-fix and post-fix
- -
5046 Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
-
5047 auto rules = env.current()->rules();
- -
5049 NumberMantissaScaleGuard sg(MantissaRange::small);
-
5050
-
5051 for (auto const& t : tests)
-
5052 {
-
5053 auto getPool = [&](std::string const& v, bool isXRP) {
-
5054 if (isXRP)
-
5055 return amountFromString(xrpIssue(), v);
-
5056 return amountFromString(noIssue(), v);
-
5057 };
-
5058 auto const& quality = std::get<Quality>(t);
-
5059 auto const tfee = std::get<std::uint16_t>(t);
-
5060 auto const status = std::get<Status>(t);
-
5061 auto const poolInIsXRP = boost::regex_search(std::get<0>(t), match, rx);
-
5062 auto const poolOutIsXRP = boost::regex_search(std::get<1>(t), match, rx);
-
5063 assert(!(poolInIsXRP && poolOutIsXRP));
-
5064 auto const poolIn = getPool(std::get<0>(t), poolInIsXRP);
-
5065 auto const poolOut = getPool(std::get<1>(t), poolOutIsXRP);
-
5066 try
-
5067 {
-
5068 auto const amounts = changeSpotPriceQuality(
-
5069 Amounts{poolIn, poolOut}, quality, tfee, env.current()->rules(), env.journal);
-
5070 if (amounts)
-
5071 {
-
5072 if (status == SucceedShouldSucceedResize)
-
5073 {
-
5074 if (!features[fixAMMv1_1])
-
5075 BEAST_EXPECT(Quality{*amounts} < quality);
-
5076 else
-
5077 BEAST_EXPECT(Quality{*amounts} >= quality);
-
5078 }
-
5079 else if (status == Succeed)
-
5080 {
-
5081 if (!features[fixAMMv1_1])
-
5082 BEAST_EXPECT(
-
5083 Quality{*amounts} >= quality ||
-
5084 withinRelativeDistance(Quality{*amounts}, quality, Number{1, -7}));
-
5085 else
-
5086 BEAST_EXPECT(Quality{*amounts} >= quality);
-
5087 }
-
5088 else if (status == FailShouldSucceed)
-
5089 {
-
5090 BEAST_EXPECT(features[fixAMMv1_1] && Quality{*amounts} >= quality);
-
5091 }
-
5092 else if (status == SucceedShouldFail)
-
5093 {
-
5094 BEAST_EXPECT(
-
5095 !features[fixAMMv1_1] && Quality{*amounts} < quality &&
-
5096 withinRelativeDistance(Quality{*amounts}, quality, Number{1, -7}));
-
5097 }
-
5098 }
-
5099 else
-
5100 {
-
5101 // Fails pre- and post-amendment because the quality can't
-
5102 // be matched. Verify by generating a tiny offer, which
-
5103 // doesn't match the quality. Exclude zero quality since
-
5104 // no offer is generated in this case.
-
5105 if (status == Fail && quality != Quality{0})
-
5106 {
-
5107 auto tinyOffer = [&]() {
-
5108 if (isXRP(poolIn))
-
5109 {
-
5110 auto const takerPays = STAmount{xrpIssue(), 1};
-
5111 return Amounts{takerPays, swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)};
-
5112 }
-
5113 else if (isXRP(poolOut))
-
5114 {
-
5115 auto const takerGets = STAmount{xrpIssue(), 1};
-
5116 return Amounts{swapAssetOut(Amounts{poolIn, poolOut}, takerGets, tfee), takerGets};
-
5117 }
-
5118 auto const takerPays = toAmount<STAmount>(getIssue(poolIn), Number{1, -10} * poolIn);
-
5119 return Amounts{takerPays, swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)};
-
5120 }();
-
5121 BEAST_EXPECT(Quality(tinyOffer) < quality);
-
5122 }
-
5123 else if (status == FailShouldSucceed)
-
5124 {
-
5125 BEAST_EXPECT(!features[fixAMMv1_1]);
-
5126 }
-
5127 else if (status == SucceedShouldFail)
-
5128 {
-
5129 BEAST_EXPECT(features[fixAMMv1_1]);
-
5130 }
-
5131 }
-
5132 }
-
5133 catch (std::runtime_error const& e)
-
5134 {
-
5135 BEAST_EXPECT(!strcmp(e.what(), "changeSpotPriceQuality failed"));
-
5136 BEAST_EXPECT(!features[fixAMMv1_1] && status == FailShouldSucceed);
-
5137 }
-
5138 }
-
5139
-
5140 // Test negative discriminant
-
5141 {
-
5142 // b**2 - 4 * a * c -> 1 * 1 - 4 * 1 * 1 = -3
-
5143 auto const res = solveQuadraticEqSmallest(Number{1}, Number{1}, Number{1});
-
5144 BEAST_EXPECT(!res.has_value());
-
5145 }
-
5146 }
-
-
5147
-
5148 void
-
- -
5150 {
-
5151 using namespace jtx;
-
5152
-
5153 testAMM([&](AMM& ammAlice, Env& env) {
-
5154 WithdrawArg args{
- -
5156 .err = ter(temMALFORMED),
-
5157 };
-
5158 ammAlice.withdraw(args);
-
5159 });
-
5160
-
5161 testAMM([&](AMM& ammAlice, Env& env) {
-
5162 WithdrawArg args{
- -
5164 .err = ter(temMALFORMED),
-
5165 };
-
5166 ammAlice.withdraw(args);
-
5167 });
-
5168
-
5169 testAMM([&](AMM& ammAlice, Env& env) {
-
5170 WithdrawArg args{
- -
5172 .err = ter(temMALFORMED),
-
5173 };
-
5174 ammAlice.withdraw(args);
-
5175 });
-
5176
-
5177 testAMM([&](AMM& ammAlice, Env& env) {
-
5178 WithdrawArg args{
-
5179 .asset1Out = XRP(100),
-
5180 .asset2Out = XRP(100),
-
5181 .err = ter(temBAD_AMM_TOKENS),
-
5182 };
-
5183 ammAlice.withdraw(args);
-
5184 });
-
5185
-
5186 testAMM([&](AMM& ammAlice, Env& env) {
-
5187 WithdrawArg args{
-
5188 .asset1Out = XRP(100),
-
5189 .asset2Out = BAD(100),
-
5190 .err = ter(temBAD_CURRENCY),
-
5191 };
-
5192 ammAlice.withdraw(args);
-
5193 });
-
5194
-
5195 testAMM([&](AMM& ammAlice, Env& env) {
-
5196 Json::Value jv;
-
5197 jv[jss::TransactionType] = jss::AMMWithdraw;
-
5198 jv[jss::Flags] = tfLimitLPToken;
-
5199 jv[jss::Account] = alice.human();
-
5200 ammAlice.setTokens(jv);
-
5201 XRP(100).value().setJson(jv[jss::Amount]);
-
5202 USD(100).value().setJson(jv[jss::EPrice]);
-
5203 env(jv, ter(temBAD_AMM_TOKENS));
-
5204 });
-
5205 }
+
4969
+
4970 void
+
+ +
4972 {
+
4973 testcase("Fix changeSpotPriceQuality");
+
4974 using namespace jtx;
+
4975
+
4976 std::string logs;
+
4977
+
4978 enum class Status {
+
4979 SucceedShouldSucceedResize, // Succeed in pre-fix because
+
4980 // error allowance, succeed post-fix
+
4981 // because of offer resizing
+
4982 FailShouldSucceed, // Fail in pre-fix due to rounding,
+
4983 // succeed after fix because of XRP
+
4984 // side is generated first
+
4985 SucceedShouldFail, // Succeed in pre-fix, fail after fix
+
4986 // due to small quality difference
+
4987 Fail, // Both fail because the quality can't be matched
+
4988 Succeed // Both succeed
+
4989 };
+
4990 using enum Status;
+
4991 auto const xrpIouAmounts10_100 = TAmounts{XRPAmount{10}, IOUAmount{100}};
+
4992 auto const iouXrpAmounts10_100 = TAmounts{IOUAmount{10}, XRPAmount{100}};
+
4993 // clang-format off
+ +
4995 //Pool In , Pool Out, Quality , Fee, Status
+
4996 {"0.001519763260828713", "1558701", Quality{5414253689393440221}, 1000, FailShouldSucceed},
+
4997 {"0.01099814367603737", "1892611", Quality{5482264816516900274}, 1000, FailShouldSucceed},
+
4998 {"0.78", "796599", Quality{5630392334958379008}, 1000, FailShouldSucceed},
+
4999 {"105439.2955578965", "49398693", Quality{5910869983721805038}, 400, FailShouldSucceed},
+
5000 {"12408293.23445213", "4340810521", Quality{5911611095910090752}, 997, FailShouldSucceed},
+
5001 {"1892611", "0.01099814367603737", Quality{6703103457950430139}, 1000, FailShouldSucceed},
+
5002 {"423028.8508101858", "3392804520", Quality{5837920340654162816}, 600, FailShouldSucceed},
+
5003 {"44565388.41001027", "73890647", Quality{6058976634606450001}, 1000, FailShouldSucceed},
+
5004 {"66831.68494832662", "16", Quality{6346111134641742975}, 0, FailShouldSucceed},
+
5005 {"675.9287302203422", "1242632304", Quality{5625960929244093294}, 300, FailShouldSucceed},
+
5006 {"7047.112186735699", "1649845866", Quality{5696855348026306945}, 504, FailShouldSucceed},
+
5007 {"840236.4402981238", "47419053", Quality{5982561601648018688}, 499, FailShouldSucceed},
+
5008 {"992715.618909774", "189445631733", Quality{5697835648288106944}, 815, SucceedShouldSucceedResize},
+
5009 {"504636667521", "185545883.9506651", Quality{6343802275337659280}, 503, SucceedShouldSucceedResize},
+
5010 {"992706.7218636649", "189447316000", Quality{5697835648288106944}, 797, SucceedShouldSucceedResize},
+
5011 {"1.068737911388205", "127860278877", Quality{5268604356368739396}, 293, SucceedShouldSucceedResize},
+
5012 {"17932506.56880419", "189308.6043676173", Quality{6206460598195440068}, 311, SucceedShouldSucceedResize},
+
5013 {"1.066379294658174", "128042251493", Quality{5268559341368739328}, 270, SucceedShouldSucceedResize},
+
5014 {"350131413924", "1576879.110907892", Quality{6487411636539049449}, 650, Fail},
+
5015 {"422093460", "2.731797662057464", Quality{6702911108534394924}, 1000, Fail},
+
5016 {"76128132223", "367172.7148422662", Quality{6487263463413514240}, 548, Fail},
+
5017 {"132701839250", "280703770.7695443", Quality{6273750681188885075}, 562, Fail},
+
5018 {"994165.7604612011", "189551302411", Quality{5697835592690668727}, 815, Fail},
+
5019 {"45053.33303227917", "86612695359", Quality{5625695218943638190}, 500, Fail},
+
5020 {"199649.077043865", "14017933007", Quality{5766034667318524880}, 324, Fail},
+
5021 {"27751824831.70903", "78896950", Quality{6272538159621630432}, 500, Fail},
+
5022 {"225.3731275781907", "156431793648", Quality{5477818047604078924}, 989, Fail},
+
5023 {"199649.077043865", "14017933007", Quality{5766036094462806309}, 324, Fail},
+
5024 {"3.590272027140361", "20677643641", Quality{5406056147042156356}, 808, Fail},
+
5025 {"1.070884664490231", "127604712776", Quality{5268620608623825741}, 293, Fail},
+
5026 {"3272.448829820197", "6275124076", Quality{5625710328924117902}, 81, Fail},
+
5027 {"0.009059512633902926", "7994028", Quality{5477511954775533172}, 1000, Fail},
+
5028 {"1", "1.0", Quality{0}, 100, Fail},
+
5029 {"1.0", "1", Quality{0}, 100, Fail},
+
5030 {"10", "10.0", Quality{xrpIouAmounts10_100}, 100, Fail},
+
5031 {"10.0", "10", Quality{iouXrpAmounts10_100}, 100, Fail},
+
5032 {"69864389131", "287631.4543025075", Quality{6487623473313516078}, 451, Succeed},
+
5033 {"4328342973", "12453825.99247381", Quality{6272522264364865181}, 997, Succeed},
+
5034 {"32347017", "7003.93031579449", Quality{6347261126087916670}, 1000, Succeed},
+
5035 {"61697206161", "36631.4583206413", Quality{6558965195382476659}, 500, Succeed},
+
5036 {"1654524979", "7028.659825511603", Quality{6487551345110052981}, 504, Succeed},
+
5037 {"88621.22277293179", "5128418948", Quality{5766347291552869205}, 380, Succeed},
+
5038 {"1892611", "0.01099814367603737", Quality{6703102780512015436}, 1000, Succeed},
+
5039 {"4542.639373338766", "24554809", Quality{5838994982188783710}, 0, Succeed},
+
5040 {"5132932546", "88542.99750172683", Quality{6419203342950054537}, 380, Succeed},
+
5041 {"78929964.1549083", "1506494795", Quality{5986890029845558688}, 589, Succeed},
+
5042 {"10096561906", "44727.72453735605", Quality{6487455290284644551}, 250, Succeed},
+
5043 {"5092.219565514988", "8768257694", Quality{5626349534958379008}, 503, Succeed},
+
5044 {"1819778294", "8305.084302902864", Quality{6487429398998540860}, 415, Succeed},
+
5045 {"6970462.633911943", "57359281", Quality{6054087899185946624}, 850, Succeed},
+
5046 {"3983448845", "2347.543644281467", Quality{6558965195382476659}, 856, Succeed},
+
5047 // This is a tiny offer 12drops/19321952e-15 it succeeds pre-amendment because of the error allowance.
+
5048 // Post amendment it is resized to 11drops/17711789e-15 but the quality is still less than
+
5049 // the target quality and the offer fails.
+
5050 {"771493171", "1.243473020567508", Quality{6707566798038544272}, 100, SucceedShouldFail},
+
5051 };
+
5052 // clang-format on
+
5053
+
5054 boost::regex rx("^\\d+$");
+
5055 boost::smatch match;
+
5056 // tests that succeed should have the same amounts pre-fix and post-fix
+ +
5058 Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
+
5059 auto rules = env.current()->rules();
+ +
5061 NumberMantissaScaleGuard sg(MantissaRange::small);
+
5062
+
5063 for (auto const& t : tests)
+
5064 {
+
5065 auto getPool = [&](std::string const& v, bool isXRP) {
+
5066 if (isXRP)
+
5067 return amountFromString(xrpIssue(), v);
+
5068 return amountFromString(noIssue(), v);
+
5069 };
+
5070 auto const& quality = std::get<Quality>(t);
+
5071 auto const tfee = std::get<std::uint16_t>(t);
+
5072 auto const status = std::get<Status>(t);
+
5073 auto const poolInIsXRP = boost::regex_search(std::get<0>(t), match, rx);
+
5074 auto const poolOutIsXRP = boost::regex_search(std::get<1>(t), match, rx);
+
5075 assert(!(poolInIsXRP && poolOutIsXRP));
+
5076 auto const poolIn = getPool(std::get<0>(t), poolInIsXRP);
+
5077 auto const poolOut = getPool(std::get<1>(t), poolOutIsXRP);
+
5078 try
+
5079 {
+
5080 auto const amounts = changeSpotPriceQuality(
+
5081 Amounts{poolIn, poolOut}, quality, tfee, env.current()->rules(), env.journal);
+
5082 if (amounts)
+
5083 {
+
5084 if (status == SucceedShouldSucceedResize)
+
5085 {
+
5086 if (!features[fixAMMv1_1])
+
5087 BEAST_EXPECT(Quality{*amounts} < quality);
+
5088 else
+
5089 BEAST_EXPECT(Quality{*amounts} >= quality);
+
5090 }
+
5091 else if (status == Succeed)
+
5092 {
+
5093 if (!features[fixAMMv1_1])
+
5094 BEAST_EXPECT(
+
5095 Quality{*amounts} >= quality ||
+
5096 withinRelativeDistance(Quality{*amounts}, quality, Number{1, -7}));
+
5097 else
+
5098 BEAST_EXPECT(Quality{*amounts} >= quality);
+
5099 }
+
5100 else if (status == FailShouldSucceed)
+
5101 {
+
5102 BEAST_EXPECT(features[fixAMMv1_1] && Quality{*amounts} >= quality);
+
5103 }
+
5104 else if (status == SucceedShouldFail)
+
5105 {
+
5106 BEAST_EXPECT(
+
5107 !features[fixAMMv1_1] && Quality{*amounts} < quality &&
+
5108 withinRelativeDistance(Quality{*amounts}, quality, Number{1, -7}));
+
5109 }
+
5110 }
+
5111 else
+
5112 {
+
5113 // Fails pre- and post-amendment because the quality can't
+
5114 // be matched. Verify by generating a tiny offer, which
+
5115 // doesn't match the quality. Exclude zero quality since
+
5116 // no offer is generated in this case.
+
5117 if (status == Fail && quality != Quality{0})
+
5118 {
+
5119 auto tinyOffer = [&]() {
+
5120 if (isXRP(poolIn))
+
5121 {
+
5122 auto const takerPays = STAmount{xrpIssue(), 1};
+
5123 return Amounts{takerPays, swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)};
+
5124 }
+
5125 else if (isXRP(poolOut))
+
5126 {
+
5127 auto const takerGets = STAmount{xrpIssue(), 1};
+
5128 return Amounts{swapAssetOut(Amounts{poolIn, poolOut}, takerGets, tfee), takerGets};
+
5129 }
+
5130 auto const takerPays = toAmount<STAmount>(getIssue(poolIn), Number{1, -10} * poolIn);
+
5131 return Amounts{takerPays, swapAssetIn(Amounts{poolIn, poolOut}, takerPays, tfee)};
+
5132 }();
+
5133 BEAST_EXPECT(Quality(tinyOffer) < quality);
+
5134 }
+
5135 else if (status == FailShouldSucceed)
+
5136 {
+
5137 BEAST_EXPECT(!features[fixAMMv1_1]);
+
5138 }
+
5139 else if (status == SucceedShouldFail)
+
5140 {
+
5141 BEAST_EXPECT(features[fixAMMv1_1]);
+
5142 }
+
5143 }
+
5144 }
+
5145 catch (std::runtime_error const& e)
+
5146 {
+
5147 BEAST_EXPECT(!strcmp(e.what(), "changeSpotPriceQuality failed"));
+
5148 BEAST_EXPECT(!features[fixAMMv1_1] && status == FailShouldSucceed);
+
5149 }
+
5150 }
+
5151
+
5152 // Test negative discriminant
+
5153 {
+
5154 // b**2 - 4 * a * c -> 1 * 1 - 4 * 1 * 1 = -3
+
5155 auto const res = solveQuadraticEqSmallest(Number{1}, Number{1}, Number{1});
+
5156 BEAST_EXPECT(!res.has_value());
+
5157 }
+
5158 }
+
5159
+
5160 void
+
+ +
5162 {
+
5163 using namespace jtx;
+
5164
+
5165 testAMM([&](AMM& ammAlice, Env& env) {
+
5166 WithdrawArg args{
+ +
5168 .err = ter(temMALFORMED),
+
5169 };
+
5170 ammAlice.withdraw(args);
+
5171 });
+
5172
+
5173 testAMM([&](AMM& ammAlice, Env& env) {
+
5174 WithdrawArg args{
+ +
5176 .err = ter(temMALFORMED),
+
5177 };
+
5178 ammAlice.withdraw(args);
+
5179 });
+
5180
+
5181 testAMM([&](AMM& ammAlice, Env& env) {
+
5182 WithdrawArg args{
+ +
5184 .err = ter(temMALFORMED),
+
5185 };
+
5186 ammAlice.withdraw(args);
+
5187 });
+
5188
+
5189 testAMM([&](AMM& ammAlice, Env& env) {
+
5190 WithdrawArg args{
+
5191 .asset1Out = XRP(100),
+
5192 .asset2Out = XRP(100),
+
5193 .err = ter(temBAD_AMM_TOKENS),
+
5194 };
+
5195 ammAlice.withdraw(args);
+
5196 });
+
5197
+
5198 testAMM([&](AMM& ammAlice, Env& env) {
+
5199 WithdrawArg args{
+
5200 .asset1Out = XRP(100),
+
5201 .asset2Out = BAD(100),
+
5202 .err = ter(temBAD_CURRENCY),
+
5203 };
+
5204 ammAlice.withdraw(args);
+
5205 });
5206
-
5207 void
-
- -
5209 {
-
5210 using namespace jtx;
-
5211 using namespace std::chrono;
-
5212 FeatureBitset const all{featuresInitial};
-
5213
-
5214 std::string logs;
-
5215
-
5216 Account const gatehub{"gatehub"};
-
5217 Account const bitstamp{"bitstamp"};
-
5218 Account const trader{"trader"};
-
5219 auto const usdGH = gatehub["USD"];
-
5220 auto const btcGH = gatehub["BTC"];
-
5221 auto const usdBIT = bitstamp["USD"];
-
5222
-
5223 struct InputSet
-
5224 {
-
5225 char const* testCase;
-
5226 double const poolUsdBIT;
-
5227 double const poolUsdGH;
-
5228 sendmax const sendMaxUsdBIT;
-
5229 STAmount const sendUsdGH;
-
5230 STAmount const failUsdGH;
-
5231 STAmount const failUsdGHr;
-
5232 STAmount const failUsdBIT;
-
5233 STAmount const failUsdBITr;
-
5234 STAmount const goodUsdGH;
-
5235 STAmount const goodUsdGHr;
-
5236 STAmount const goodUsdBIT;
-
5237 STAmount const goodUsdBITr;
-
5238 IOUAmount const lpTokenBalance;
-
5239 std::optional<IOUAmount> const lpTokenBalanceAlt = {};
-
5240 double const offer1BtcGH = 0.1;
-
5241 double const offer2BtcGH = 0.1;
-
5242 double const offer2UsdGH = 1;
-
5243 double const rateBIT = 0.0;
-
5244 double const rateGH = 0.0;
-
5245 };
-
5246
-
5247 using uint64_t = std::uint64_t;
-
5248
-
5249 for (auto const& input : {
-
5250 InputSet{
-
5251 .testCase = "Test Fix Overflow Offer", //
-
5252 .poolUsdBIT = 3, //
-
5253 .poolUsdGH = 273, //
-
5254 .sendMaxUsdBIT{usdBIT(50)}, //
-
5255 .sendUsdGH{usdGH, uint64_t(272'455089820359), -12}, //
-
5256 .failUsdGH = STAmount{0}, //
-
5257 .failUsdGHr = STAmount{0}, //
-
5258 .failUsdBIT{usdBIT, uint64_t(46'47826086956522), -14}, //
-
5259 .failUsdBITr{usdBIT, uint64_t(46'47826086956521), -14}, //
-
5260 .goodUsdGH{usdGH, uint64_t(96'7543114220382), -13}, //
-
5261 .goodUsdGHr{usdGH, uint64_t(96'7543114222965), -13}, //
-
5262 .goodUsdBIT{usdBIT, uint64_t(8'464739069120721), -15}, //
-
5263 .goodUsdBITr{usdBIT, uint64_t(8'464739069098152), -15}, //
-
5264 .lpTokenBalance = {28'61817604250837, -14}, //
-
5265 .lpTokenBalanceAlt = IOUAmount{28'61817604250836, -14}, //
-
5266 .offer1BtcGH = 0.1, //
-
5267 .offer2BtcGH = 0.1, //
-
5268 .offer2UsdGH = 1, //
-
5269 .rateBIT = 1.15, //
-
5270 .rateGH = 1.2, //
-
5271 },
-
5272 InputSet{
-
5273 .testCase = "Overflow test {1, 100, 0.111}", //
-
5274 .poolUsdBIT = 1, //
-
5275 .poolUsdGH = 100, //
-
5276 .sendMaxUsdBIT{usdBIT(0.111)}, //
-
5277 .sendUsdGH{usdGH, 100}, //
-
5278 .failUsdGH = STAmount{0}, //
-
5279 .failUsdGHr = STAmount{0}, //
-
5280 .failUsdBIT{usdBIT, uint64_t(1'111), -3}, //
-
5281 .failUsdBITr{usdBIT, uint64_t(1'111), -3}, //
-
5282 .goodUsdGH{usdGH, uint64_t(90'04347888284115), -14}, //
-
5283 .goodUsdGHr{usdGH, uint64_t(90'04347888284201), -14}, //
-
5284 .goodUsdBIT{usdBIT, uint64_t(1'111), -3}, //
-
5285 .goodUsdBITr{usdBIT, uint64_t(1'111), -3}, //
-
5286 .lpTokenBalance{10, 0}, //
-
5287 .offer1BtcGH = 1e-5, //
-
5288 .offer2BtcGH = 1, //
-
5289 .offer2UsdGH = 1e-5, //
-
5290 .rateBIT = 0, //
-
5291 .rateGH = 0, //
-
5292 },
-
5293 InputSet{
-
5294 .testCase = "Overflow test {1, 100, 1.00}", //
-
5295 .poolUsdBIT = 1, //
-
5296 .poolUsdGH = 100, //
-
5297 .sendMaxUsdBIT{usdBIT(1.00)}, //
-
5298 .sendUsdGH{usdGH, 100}, //
-
5299 .failUsdGH = STAmount{0}, //
-
5300 .failUsdGHr = STAmount{0}, //
-
5301 .failUsdBIT{usdBIT, uint64_t(2), 0}, //
-
5302 .failUsdBITr{usdBIT, uint64_t(2), 0}, //
-
5303 .goodUsdGH{usdGH, uint64_t(52'94379354424079), -14}, //
-
5304 .goodUsdGHr{usdGH, uint64_t(52'94379354424135), -14}, //
-
5305 .goodUsdBIT{usdBIT, uint64_t(2), 0}, //
-
5306 .goodUsdBITr{usdBIT, uint64_t(2), 0}, //
-
5307 .lpTokenBalance{10, 0}, //
-
5308 .offer1BtcGH = 1e-5, //
-
5309 .offer2BtcGH = 1, //
-
5310 .offer2UsdGH = 1e-5, //
-
5311 .rateBIT = 0, //
-
5312 .rateGH = 0, //
-
5313 },
-
5314 InputSet{
-
5315 .testCase = "Overflow test {1, 100, 4.6432}", //
-
5316 .poolUsdBIT = 1, //
-
5317 .poolUsdGH = 100, //
-
5318 .sendMaxUsdBIT{usdBIT(4.6432)}, //
-
5319 .sendUsdGH{usdGH, 100}, //
-
5320 .failUsdGH = STAmount{0}, //
-
5321 .failUsdGHr = STAmount{0}, //
-
5322 .failUsdBIT{usdBIT, uint64_t(5'6432), -4}, //
-
5323 .failUsdBITr{usdBIT, uint64_t(5'6432), -4}, //
-
5324 .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
-
5325 .goodUsdGHr{usdGH, uint64_t(35'44113971506987), -14}, //
-
5326 .goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15}, //
-
5327 .goodUsdBITr{usdBIT, uint64_t(2'821579689703954), -15}, //
-
5328 .lpTokenBalance{10, 0}, //
-
5329 .offer1BtcGH = 1e-5, //
-
5330 .offer2BtcGH = 1, //
-
5331 .offer2UsdGH = 1e-5, //
-
5332 .rateBIT = 0, //
-
5333 .rateGH = 0, //
-
5334 },
-
5335 InputSet{
-
5336 .testCase = "Overflow test {1, 100, 10}", //
-
5337 .poolUsdBIT = 1, //
-
5338 .poolUsdGH = 100, //
-
5339 .sendMaxUsdBIT{usdBIT(10)}, //
-
5340 .sendUsdGH{usdGH, 100}, //
-
5341 .failUsdGH = STAmount{0}, //
-
5342 .failUsdGHr = STAmount{0}, //
-
5343 .failUsdBIT{usdBIT, uint64_t(11), 0}, //
-
5344 .failUsdBITr{usdBIT, uint64_t(11), 0}, //
-
5345 .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
-
5346 .goodUsdGHr{usdGH, uint64_t(35'44113971506987), -14}, //
-
5347 .goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15}, //
-
5348 .goodUsdBITr{usdBIT, uint64_t(2'821579689703954), -15}, //
-
5349 .lpTokenBalance{10, 0}, //
-
5350 .offer1BtcGH = 1e-5, //
-
5351 .offer2BtcGH = 1, //
-
5352 .offer2UsdGH = 1e-5, //
-
5353 .rateBIT = 0, //
-
5354 .rateGH = 0, //
-
5355 },
-
5356 InputSet{
-
5357 .testCase = "Overflow test {50, 100, 5.55}", //
-
5358 .poolUsdBIT = 50, //
-
5359 .poolUsdGH = 100, //
-
5360 .sendMaxUsdBIT{usdBIT(5.55)}, //
-
5361 .sendUsdGH{usdGH, 100}, //
-
5362 .failUsdGH = STAmount{0}, //
-
5363 .failUsdGHr = STAmount{0}, //
-
5364 .failUsdBIT{usdBIT, uint64_t(55'55), -2}, //
-
5365 .failUsdBITr{usdBIT, uint64_t(55'55), -2}, //
-
5366 .goodUsdGH{usdGH, uint64_t(90'04347888284113), -14}, //
-
5367 .goodUsdGHr{usdGH, uint64_t(90'0434788828413), -13}, //
-
5368 .goodUsdBIT{usdBIT, uint64_t(55'55), -2}, //
-
5369 .goodUsdBITr{usdBIT, uint64_t(55'55), -2}, //
-
5370 .lpTokenBalance{uint64_t(70'71067811865475), -14}, //
-
5371 .offer1BtcGH = 1e-5, //
-
5372 .offer2BtcGH = 1, //
-
5373 .offer2UsdGH = 1e-5, //
-
5374 .rateBIT = 0, //
-
5375 .rateGH = 0, //
-
5376 },
-
5377 InputSet{
-
5378 .testCase = "Overflow test {50, 100, 50.00}", //
-
5379 .poolUsdBIT = 50, //
-
5380 .poolUsdGH = 100, //
-
5381 .sendMaxUsdBIT{usdBIT(50.00)}, //
-
5382 .sendUsdGH{usdGH, 100}, //
-
5383 .failUsdGH{usdGH, uint64_t(52'94379354424081), -14}, //
-
5384 .failUsdGHr{usdGH, uint64_t(52'94379354424092), -14}, //
-
5385 .failUsdBIT{usdBIT, uint64_t(100), 0}, //
-
5386 .failUsdBITr{usdBIT, uint64_t(100), 0}, //
-
5387 .goodUsdGH{usdGH, uint64_t(52'94379354424081), -14}, //
-
5388 .goodUsdGHr{usdGH, uint64_t(52'94379354424092), -14}, //
-
5389 .goodUsdBIT{usdBIT, uint64_t(100), 0}, //
-
5390 .goodUsdBITr{usdBIT, uint64_t(100), 0}, //
-
5391 .lpTokenBalance{uint64_t(70'71067811865475), -14}, //
-
5392 .offer1BtcGH = 1e-5, //
-
5393 .offer2BtcGH = 1, //
-
5394 .offer2UsdGH = 1e-5, //
-
5395 .rateBIT = 0, //
-
5396 .rateGH = 0, //
-
5397 },
-
5398 InputSet{
-
5399 .testCase = "Overflow test {50, 100, 232.16}", //
-
5400 .poolUsdBIT = 50, //
-
5401 .poolUsdGH = 100, //
-
5402 .sendMaxUsdBIT{usdBIT(232.16)}, //
-
5403 .sendUsdGH{usdGH, 100}, //
-
5404 .failUsdGH = STAmount{0}, //
-
5405 .failUsdGHr = STAmount{0}, //
-
5406 .failUsdBIT{usdBIT, uint64_t(282'16), -2}, //
-
5407 .failUsdBITr{usdBIT, uint64_t(282'16), -2}, //
-
5408 .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
-
5409 .goodUsdGHr{usdGH, uint64_t(35'44113971506987), -14}, //
-
5410 .goodUsdBIT{usdBIT, uint64_t(141'0789844851958), -13}, //
-
5411 .goodUsdBITr{usdBIT, uint64_t(141'0789844851962), -13}, //
-
5412 .lpTokenBalance{70'71067811865475, -14}, //
-
5413 .offer1BtcGH = 1e-5, //
-
5414 .offer2BtcGH = 1, //
-
5415 .offer2UsdGH = 1e-5, //
-
5416 .rateBIT = 0, //
-
5417 .rateGH = 0, //
-
5418 },
-
5419 InputSet{
-
5420 .testCase = "Overflow test {50, 100, 500}", //
-
5421 .poolUsdBIT = 50, //
-
5422 .poolUsdGH = 100, //
-
5423 .sendMaxUsdBIT{usdBIT(500)}, //
-
5424 .sendUsdGH{usdGH, 100}, //
-
5425 .failUsdGH = STAmount{0}, //
-
5426 .failUsdGHr = STAmount{0}, //
-
5427 .failUsdBIT{usdBIT, uint64_t(550), 0}, //
-
5428 .failUsdBITr{usdBIT, uint64_t(550), 0}, //
-
5429 .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
-
5430 .goodUsdGHr{usdGH, uint64_t(35'44113971506987), -14}, //
-
5431 .goodUsdBIT{usdBIT, uint64_t(141'0789844851958), -13}, //
-
5432 .goodUsdBITr{usdBIT, uint64_t(141'0789844851962), -13}, //
-
5433 .lpTokenBalance{70'71067811865475, -14}, //
-
5434 .offer1BtcGH = 1e-5, //
-
5435 .offer2BtcGH = 1, //
-
5436 .offer2UsdGH = 1e-5, //
-
5437 .rateBIT = 0, //
-
5438 .rateGH = 0, //
-
5439 },
-
5440 })
-
5441 {
-
5442 testcase(input.testCase);
-
5443 for (auto const& features : {all - fixAMMOverflowOffer - fixAMMv1_1 - fixAMMv1_3, all})
-
5444 {
-
5445 Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
-
5446
-
5447 env.fund(XRP(5'000), gatehub, bitstamp, trader);
-
5448 env.close();
-
5449
-
5450 if (input.rateGH != 0.0)
-
5451 env(rate(gatehub, input.rateGH));
-
5452 if (input.rateBIT != 0.0)
-
5453 env(rate(bitstamp, input.rateBIT));
-
5454
-
5455 env(trust(trader, usdGH(10'000'000)));
-
5456 env(trust(trader, usdBIT(10'000'000)));
-
5457 env(trust(trader, btcGH(10'000'000)));
-
5458 env.close();
-
5459
-
5460 env(pay(gatehub, trader, usdGH(100'000)));
-
5461 env(pay(gatehub, trader, btcGH(100'000)));
-
5462 env(pay(bitstamp, trader, usdBIT(100'000)));
-
5463 env.close();
-
5464
-
5465 AMM amm{env, trader, usdGH(input.poolUsdGH), usdBIT(input.poolUsdBIT)};
-
5466 env.close();
-
5467
-
5468 IOUAmount const preSwapLPTokenBalance = amm.getLPTokensBalance();
-
5469
-
5470 env(offer(trader, usdBIT(1), btcGH(input.offer1BtcGH)));
-
5471 env(offer(trader, btcGH(input.offer2BtcGH), usdGH(input.offer2UsdGH)));
-
5472 env.close();
-
5473
-
5474 env(pay(trader, trader, input.sendUsdGH),
-
5475 path(~usdGH),
-
5476 path(~btcGH, ~usdGH),
-
5477 sendmax(input.sendMaxUsdBIT),
- -
5479 env.close();
-
5480
-
5481 auto const failUsdGH = features[fixAMMv1_1] ? input.failUsdGHr : input.failUsdGH;
-
5482 auto const failUsdBIT = features[fixAMMv1_1] ? input.failUsdBITr : input.failUsdBIT;
-
5483 auto const goodUsdGH = features[fixAMMv1_1] ? input.goodUsdGHr : input.goodUsdGH;
-
5484 auto const goodUsdBIT = features[fixAMMv1_1] ? input.goodUsdBITr : input.goodUsdBIT;
-
5485 auto const lpTokenBalance = env.enabled(fixAMMv1_3) && input.lpTokenBalanceAlt
-
5486 ? *input.lpTokenBalanceAlt
-
5487 : input.lpTokenBalance;
-
5488 if (!features[fixAMMOverflowOffer])
-
5489 {
-
5490 BEAST_EXPECT(amm.expectBalances(failUsdGH, failUsdBIT, lpTokenBalance));
-
5491 }
-
5492 else
-
5493 {
-
5494 BEAST_EXPECT(amm.expectBalances(goodUsdGH, goodUsdBIT, lpTokenBalance));
-
5495
-
5496 // Invariant: LPToken balance must not change in a
-
5497 // payment or a swap transaction
-
5498 BEAST_EXPECT(amm.getLPTokensBalance() == preSwapLPTokenBalance);
-
5499
-
5500 // Invariant: The square root of (product of the pool
-
5501 // balances) must be at least the LPTokenBalance
-
5502 Number const sqrtPoolProduct = root2(goodUsdGH * goodUsdBIT);
-
5503
-
5504 // Include a tiny tolerance for the test cases using
-
5505 // .goodUsdGH{usdGH, uint64_t(35'44113971506987),
-
5506 // -14}, .goodUsdBIT{usdBIT,
-
5507 // uint64_t(2'821579689703915), -15},
-
5508 // These two values multiply
-
5509 // to 99.99999999999994227040383754105 which gets
-
5510 // internally rounded to 100, due to representation
-
5511 // error.
-
5512 BEAST_EXPECT((sqrtPoolProduct + Number{1, -14} >= input.lpTokenBalance));
-
5513 }
-
5514 }
-
5515 }
-
5516 }
+
5207 testAMM([&](AMM& ammAlice, Env& env) {
+
5208 Json::Value jv;
+
5209 jv[jss::TransactionType] = jss::AMMWithdraw;
+
5210 jv[jss::Flags] = tfLimitLPToken;
+
5211 jv[jss::Account] = alice.human();
+
5212 ammAlice.setTokens(jv);
+
5213 XRP(100).value().setJson(jv[jss::Amount]);
+
5214 USD(100).value().setJson(jv[jss::EPrice]);
+
5215 env(jv, ter(temBAD_AMM_TOKENS));
+
5216 });
+
5217 }
+
+
5218
+
5219 void
+
+ +
5221 {
+
5222 using namespace jtx;
+
5223 using namespace std::chrono;
+
5224 FeatureBitset const all{featuresInitial};
+
5225
+
5226 std::string logs;
+
5227
+
5228 Account const gatehub{"gatehub"};
+
5229 Account const bitstamp{"bitstamp"};
+
5230 Account const trader{"trader"};
+
5231 auto const usdGH = gatehub["USD"];
+
5232 auto const btcGH = gatehub["BTC"];
+
5233 auto const usdBIT = bitstamp["USD"];
+
5234
+
5235 struct InputSet
+
5236 {
+
5237 char const* testCase;
+
5238 double const poolUsdBIT;
+
5239 double const poolUsdGH;
+
5240 sendmax const sendMaxUsdBIT;
+
5241 STAmount const sendUsdGH;
+
5242 STAmount const failUsdGH;
+
5243 STAmount const failUsdGHr;
+
5244 STAmount const failUsdBIT;
+
5245 STAmount const failUsdBITr;
+
5246 STAmount const goodUsdGH;
+
5247 STAmount const goodUsdGHr;
+
5248 STAmount const goodUsdBIT;
+
5249 STAmount const goodUsdBITr;
+
5250 IOUAmount const lpTokenBalance;
+
5251 std::optional<IOUAmount> const lpTokenBalanceAlt = {};
+
5252 double const offer1BtcGH = 0.1;
+
5253 double const offer2BtcGH = 0.1;
+
5254 double const offer2UsdGH = 1;
+
5255 double const rateBIT = 0.0;
+
5256 double const rateGH = 0.0;
+
5257 };
+
5258
+
5259 using uint64_t = std::uint64_t;
+
5260
+
5261 for (auto const& input : {
+
5262 InputSet{
+
5263 .testCase = "Test Fix Overflow Offer", //
+
5264 .poolUsdBIT = 3, //
+
5265 .poolUsdGH = 273, //
+
5266 .sendMaxUsdBIT{usdBIT(50)}, //
+
5267 .sendUsdGH{usdGH, uint64_t(272'455089820359), -12}, //
+
5268 .failUsdGH = STAmount{0}, //
+
5269 .failUsdGHr = STAmount{0}, //
+
5270 .failUsdBIT{usdBIT, uint64_t(46'47826086956522), -14}, //
+
5271 .failUsdBITr{usdBIT, uint64_t(46'47826086956521), -14}, //
+
5272 .goodUsdGH{usdGH, uint64_t(96'7543114220382), -13}, //
+
5273 .goodUsdGHr{usdGH, uint64_t(96'7543114222965), -13}, //
+
5274 .goodUsdBIT{usdBIT, uint64_t(8'464739069120721), -15}, //
+
5275 .goodUsdBITr{usdBIT, uint64_t(8'464739069098152), -15}, //
+
5276 .lpTokenBalance = {28'61817604250837, -14}, //
+
5277 .lpTokenBalanceAlt = IOUAmount{28'61817604250836, -14}, //
+
5278 .offer1BtcGH = 0.1, //
+
5279 .offer2BtcGH = 0.1, //
+
5280 .offer2UsdGH = 1, //
+
5281 .rateBIT = 1.15, //
+
5282 .rateGH = 1.2, //
+
5283 },
+
5284 InputSet{
+
5285 .testCase = "Overflow test {1, 100, 0.111}", //
+
5286 .poolUsdBIT = 1, //
+
5287 .poolUsdGH = 100, //
+
5288 .sendMaxUsdBIT{usdBIT(0.111)}, //
+
5289 .sendUsdGH{usdGH, 100}, //
+
5290 .failUsdGH = STAmount{0}, //
+
5291 .failUsdGHr = STAmount{0}, //
+
5292 .failUsdBIT{usdBIT, uint64_t(1'111), -3}, //
+
5293 .failUsdBITr{usdBIT, uint64_t(1'111), -3}, //
+
5294 .goodUsdGH{usdGH, uint64_t(90'04347888284115), -14}, //
+
5295 .goodUsdGHr{usdGH, uint64_t(90'04347888284201), -14}, //
+
5296 .goodUsdBIT{usdBIT, uint64_t(1'111), -3}, //
+
5297 .goodUsdBITr{usdBIT, uint64_t(1'111), -3}, //
+
5298 .lpTokenBalance{10, 0}, //
+
5299 .offer1BtcGH = 1e-5, //
+
5300 .offer2BtcGH = 1, //
+
5301 .offer2UsdGH = 1e-5, //
+
5302 .rateBIT = 0, //
+
5303 .rateGH = 0, //
+
5304 },
+
5305 InputSet{
+
5306 .testCase = "Overflow test {1, 100, 1.00}", //
+
5307 .poolUsdBIT = 1, //
+
5308 .poolUsdGH = 100, //
+
5309 .sendMaxUsdBIT{usdBIT(1.00)}, //
+
5310 .sendUsdGH{usdGH, 100}, //
+
5311 .failUsdGH = STAmount{0}, //
+
5312 .failUsdGHr = STAmount{0}, //
+
5313 .failUsdBIT{usdBIT, uint64_t(2), 0}, //
+
5314 .failUsdBITr{usdBIT, uint64_t(2), 0}, //
+
5315 .goodUsdGH{usdGH, uint64_t(52'94379354424079), -14}, //
+
5316 .goodUsdGHr{usdGH, uint64_t(52'94379354424135), -14}, //
+
5317 .goodUsdBIT{usdBIT, uint64_t(2), 0}, //
+
5318 .goodUsdBITr{usdBIT, uint64_t(2), 0}, //
+
5319 .lpTokenBalance{10, 0}, //
+
5320 .offer1BtcGH = 1e-5, //
+
5321 .offer2BtcGH = 1, //
+
5322 .offer2UsdGH = 1e-5, //
+
5323 .rateBIT = 0, //
+
5324 .rateGH = 0, //
+
5325 },
+
5326 InputSet{
+
5327 .testCase = "Overflow test {1, 100, 4.6432}", //
+
5328 .poolUsdBIT = 1, //
+
5329 .poolUsdGH = 100, //
+
5330 .sendMaxUsdBIT{usdBIT(4.6432)}, //
+
5331 .sendUsdGH{usdGH, 100}, //
+
5332 .failUsdGH = STAmount{0}, //
+
5333 .failUsdGHr = STAmount{0}, //
+
5334 .failUsdBIT{usdBIT, uint64_t(5'6432), -4}, //
+
5335 .failUsdBITr{usdBIT, uint64_t(5'6432), -4}, //
+
5336 .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
+
5337 .goodUsdGHr{usdGH, uint64_t(35'44113971506987), -14}, //
+
5338 .goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15}, //
+
5339 .goodUsdBITr{usdBIT, uint64_t(2'821579689703954), -15}, //
+
5340 .lpTokenBalance{10, 0}, //
+
5341 .offer1BtcGH = 1e-5, //
+
5342 .offer2BtcGH = 1, //
+
5343 .offer2UsdGH = 1e-5, //
+
5344 .rateBIT = 0, //
+
5345 .rateGH = 0, //
+
5346 },
+
5347 InputSet{
+
5348 .testCase = "Overflow test {1, 100, 10}", //
+
5349 .poolUsdBIT = 1, //
+
5350 .poolUsdGH = 100, //
+
5351 .sendMaxUsdBIT{usdBIT(10)}, //
+
5352 .sendUsdGH{usdGH, 100}, //
+
5353 .failUsdGH = STAmount{0}, //
+
5354 .failUsdGHr = STAmount{0}, //
+
5355 .failUsdBIT{usdBIT, uint64_t(11), 0}, //
+
5356 .failUsdBITr{usdBIT, uint64_t(11), 0}, //
+
5357 .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
+
5358 .goodUsdGHr{usdGH, uint64_t(35'44113971506987), -14}, //
+
5359 .goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15}, //
+
5360 .goodUsdBITr{usdBIT, uint64_t(2'821579689703954), -15}, //
+
5361 .lpTokenBalance{10, 0}, //
+
5362 .offer1BtcGH = 1e-5, //
+
5363 .offer2BtcGH = 1, //
+
5364 .offer2UsdGH = 1e-5, //
+
5365 .rateBIT = 0, //
+
5366 .rateGH = 0, //
+
5367 },
+
5368 InputSet{
+
5369 .testCase = "Overflow test {50, 100, 5.55}", //
+
5370 .poolUsdBIT = 50, //
+
5371 .poolUsdGH = 100, //
+
5372 .sendMaxUsdBIT{usdBIT(5.55)}, //
+
5373 .sendUsdGH{usdGH, 100}, //
+
5374 .failUsdGH = STAmount{0}, //
+
5375 .failUsdGHr = STAmount{0}, //
+
5376 .failUsdBIT{usdBIT, uint64_t(55'55), -2}, //
+
5377 .failUsdBITr{usdBIT, uint64_t(55'55), -2}, //
+
5378 .goodUsdGH{usdGH, uint64_t(90'04347888284113), -14}, //
+
5379 .goodUsdGHr{usdGH, uint64_t(90'0434788828413), -13}, //
+
5380 .goodUsdBIT{usdBIT, uint64_t(55'55), -2}, //
+
5381 .goodUsdBITr{usdBIT, uint64_t(55'55), -2}, //
+
5382 .lpTokenBalance{uint64_t(70'71067811865475), -14}, //
+
5383 .offer1BtcGH = 1e-5, //
+
5384 .offer2BtcGH = 1, //
+
5385 .offer2UsdGH = 1e-5, //
+
5386 .rateBIT = 0, //
+
5387 .rateGH = 0, //
+
5388 },
+
5389 InputSet{
+
5390 .testCase = "Overflow test {50, 100, 50.00}", //
+
5391 .poolUsdBIT = 50, //
+
5392 .poolUsdGH = 100, //
+
5393 .sendMaxUsdBIT{usdBIT(50.00)}, //
+
5394 .sendUsdGH{usdGH, 100}, //
+
5395 .failUsdGH{usdGH, uint64_t(52'94379354424081), -14}, //
+
5396 .failUsdGHr{usdGH, uint64_t(52'94379354424092), -14}, //
+
5397 .failUsdBIT{usdBIT, uint64_t(100), 0}, //
+
5398 .failUsdBITr{usdBIT, uint64_t(100), 0}, //
+
5399 .goodUsdGH{usdGH, uint64_t(52'94379354424081), -14}, //
+
5400 .goodUsdGHr{usdGH, uint64_t(52'94379354424092), -14}, //
+
5401 .goodUsdBIT{usdBIT, uint64_t(100), 0}, //
+
5402 .goodUsdBITr{usdBIT, uint64_t(100), 0}, //
+
5403 .lpTokenBalance{uint64_t(70'71067811865475), -14}, //
+
5404 .offer1BtcGH = 1e-5, //
+
5405 .offer2BtcGH = 1, //
+
5406 .offer2UsdGH = 1e-5, //
+
5407 .rateBIT = 0, //
+
5408 .rateGH = 0, //
+
5409 },
+
5410 InputSet{
+
5411 .testCase = "Overflow test {50, 100, 232.16}", //
+
5412 .poolUsdBIT = 50, //
+
5413 .poolUsdGH = 100, //
+
5414 .sendMaxUsdBIT{usdBIT(232.16)}, //
+
5415 .sendUsdGH{usdGH, 100}, //
+
5416 .failUsdGH = STAmount{0}, //
+
5417 .failUsdGHr = STAmount{0}, //
+
5418 .failUsdBIT{usdBIT, uint64_t(282'16), -2}, //
+
5419 .failUsdBITr{usdBIT, uint64_t(282'16), -2}, //
+
5420 .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
+
5421 .goodUsdGHr{usdGH, uint64_t(35'44113971506987), -14}, //
+
5422 .goodUsdBIT{usdBIT, uint64_t(141'0789844851958), -13}, //
+
5423 .goodUsdBITr{usdBIT, uint64_t(141'0789844851962), -13}, //
+
5424 .lpTokenBalance{70'71067811865475, -14}, //
+
5425 .offer1BtcGH = 1e-5, //
+
5426 .offer2BtcGH = 1, //
+
5427 .offer2UsdGH = 1e-5, //
+
5428 .rateBIT = 0, //
+
5429 .rateGH = 0, //
+
5430 },
+
5431 InputSet{
+
5432 .testCase = "Overflow test {50, 100, 500}", //
+
5433 .poolUsdBIT = 50, //
+
5434 .poolUsdGH = 100, //
+
5435 .sendMaxUsdBIT{usdBIT(500)}, //
+
5436 .sendUsdGH{usdGH, 100}, //
+
5437 .failUsdGH = STAmount{0}, //
+
5438 .failUsdGHr = STAmount{0}, //
+
5439 .failUsdBIT{usdBIT, uint64_t(550), 0}, //
+
5440 .failUsdBITr{usdBIT, uint64_t(550), 0}, //
+
5441 .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
+
5442 .goodUsdGHr{usdGH, uint64_t(35'44113971506987), -14}, //
+
5443 .goodUsdBIT{usdBIT, uint64_t(141'0789844851958), -13}, //
+
5444 .goodUsdBITr{usdBIT, uint64_t(141'0789844851962), -13}, //
+
5445 .lpTokenBalance{70'71067811865475, -14}, //
+
5446 .offer1BtcGH = 1e-5, //
+
5447 .offer2BtcGH = 1, //
+
5448 .offer2UsdGH = 1e-5, //
+
5449 .rateBIT = 0, //
+
5450 .rateGH = 0, //
+
5451 },
+
5452 })
+
5453 {
+
5454 testcase(input.testCase);
+
5455 for (auto const& features : {all - fixAMMOverflowOffer - fixAMMv1_1 - fixAMMv1_3, all})
+
5456 {
+
5457 Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
+
5458
+
5459 env.fund(XRP(5'000), gatehub, bitstamp, trader);
+
5460 env.close();
+
5461
+
5462 if (input.rateGH != 0.0)
+
5463 env(rate(gatehub, input.rateGH));
+
5464 if (input.rateBIT != 0.0)
+
5465 env(rate(bitstamp, input.rateBIT));
+
5466
+
5467 env(trust(trader, usdGH(10'000'000)));
+
5468 env(trust(trader, usdBIT(10'000'000)));
+
5469 env(trust(trader, btcGH(10'000'000)));
+
5470 env.close();
+
5471
+
5472 env(pay(gatehub, trader, usdGH(100'000)));
+
5473 env(pay(gatehub, trader, btcGH(100'000)));
+
5474 env(pay(bitstamp, trader, usdBIT(100'000)));
+
5475 env.close();
+
5476
+
5477 AMM amm{env, trader, usdGH(input.poolUsdGH), usdBIT(input.poolUsdBIT)};
+
5478 env.close();
+
5479
+
5480 IOUAmount const preSwapLPTokenBalance = amm.getLPTokensBalance();
+
5481
+
5482 env(offer(trader, usdBIT(1), btcGH(input.offer1BtcGH)));
+
5483 env(offer(trader, btcGH(input.offer2BtcGH), usdGH(input.offer2UsdGH)));
+
5484 env.close();
+
5485
+
5486 env(pay(trader, trader, input.sendUsdGH),
+
5487 path(~usdGH),
+
5488 path(~btcGH, ~usdGH),
+
5489 sendmax(input.sendMaxUsdBIT),
+ +
5491 env.close();
+
5492
+
5493 auto const failUsdGH = features[fixAMMv1_1] ? input.failUsdGHr : input.failUsdGH;
+
5494 auto const failUsdBIT = features[fixAMMv1_1] ? input.failUsdBITr : input.failUsdBIT;
+
5495 auto const goodUsdGH = features[fixAMMv1_1] ? input.goodUsdGHr : input.goodUsdGH;
+
5496 auto const goodUsdBIT = features[fixAMMv1_1] ? input.goodUsdBITr : input.goodUsdBIT;
+
5497 auto const lpTokenBalance = env.enabled(fixAMMv1_3) && input.lpTokenBalanceAlt
+
5498 ? *input.lpTokenBalanceAlt
+
5499 : input.lpTokenBalance;
+
5500 if (!features[fixAMMOverflowOffer])
+
5501 {
+
5502 BEAST_EXPECT(amm.expectBalances(failUsdGH, failUsdBIT, lpTokenBalance));
+
5503 }
+
5504 else
+
5505 {
+
5506 BEAST_EXPECT(amm.expectBalances(goodUsdGH, goodUsdBIT, lpTokenBalance));
+
5507
+
5508 // Invariant: LPToken balance must not change in a
+
5509 // payment or a swap transaction
+
5510 BEAST_EXPECT(amm.getLPTokensBalance() == preSwapLPTokenBalance);
+
5511
+
5512 // Invariant: The square root of (product of the pool
+
5513 // balances) must be at least the LPTokenBalance
+
5514 Number const sqrtPoolProduct = root2(goodUsdGH * goodUsdBIT);
+
5515
+
5516 // Include a tiny tolerance for the test cases using
+
5517 // .goodUsdGH{usdGH, uint64_t(35'44113971506987),
+
5518 // -14}, .goodUsdBIT{usdBIT,
+
5519 // uint64_t(2'821579689703915), -15},
+
5520 // These two values multiply
+
5521 // to 99.99999999999994227040383754105 which gets
+
5522 // internally rounded to 100, due to representation
+
5523 // error.
+
5524 BEAST_EXPECT((sqrtPoolProduct + Number{1, -14} >= input.lpTokenBalance));
+
5525 }
+
5526 }
+
5527 }
+
5528 }
-
5517
-
5518 void
-
- -
5520 {
-
5521 testcase("swapRounding");
-
5522 using namespace jtx;
-
5523
-
5524 STAmount const xrpPool{XRP, UINT64_C(51600'000981)};
-
5525 STAmount const iouPool{USD, UINT64_C(803040'9987141784), -10};
-
5526
-
5527 STAmount const xrpBob{XRP, UINT64_C(1092'878933)};
-
5528 STAmount const iouBob{USD, UINT64_C(3'988035892323031), -28}; // 3.9...e-13
5529
-
5530 testAMM(
-
5531 [&](AMM& amm, Env& env) {
-
5532 // Check our AMM starting conditions.
-
5533 auto [xrpBegin, iouBegin, lptBegin] = amm.balances(XRP, USD);
-
5534
-
5535 // Set Bob's starting conditions.
-
5536 env.fund(xrpBob, bob);
-
5537 env.trust(USD(1'000'000), bob);
-
5538 env(pay(gw, bob, iouBob));
-
5539 env.close();
-
5540
-
5541 env(offer(bob, XRP(6300), USD(100'000)));
-
5542 env.close();
-
5543
-
5544 // Assert that AMM is unchanged.
-
5545 BEAST_EXPECT(amm.expectBalances(xrpBegin, iouBegin, amm.tokens()));
-
5546 },
-
5547 {{xrpPool, iouPool}},
-
5548 889,
- -
5550 {testable_amendments() | fixAMMv1_1});
-
5551 }
-
+
5530 void
+
+ +
5532 {
+
5533 testcase("swapRounding");
+
5534 using namespace jtx;
+
5535
+
5536 STAmount const xrpPool{XRP, UINT64_C(51600'000981)};
+
5537 STAmount const iouPool{USD, UINT64_C(803040'9987141784), -10};
+
5538
+
5539 STAmount const xrpBob{XRP, UINT64_C(1092'878933)};
+
5540 STAmount const iouBob{USD, UINT64_C(3'988035892323031), -28}; // 3.9...e-13
+
5541
+
5542 testAMM(
+
5543 [&](AMM& amm, Env& env) {
+
5544 // Check our AMM starting conditions.
+
5545 auto [xrpBegin, iouBegin, lptBegin] = amm.balances(XRP, USD);
+
5546
+
5547 // Set Bob's starting conditions.
+
5548 env.fund(xrpBob, bob);
+
5549 env.trust(USD(1'000'000), bob);
+
5550 env(pay(gw, bob, iouBob));
+
5551 env.close();
5552
-
5553 void
-
- -
5555 {
-
5556 testcase("AMM Offer Blocked By LOB");
-
5557 using namespace jtx;
-
5558
-
5559 // Low quality LOB offer blocks AMM liquidity
-
5560
-
5561 // USD/XRP crosses AMM
-
5562 {
-
5563 Env env(*this, features);
+
5553 env(offer(bob, XRP(6300), USD(100'000)));
+
5554 env.close();
+
5555
+
5556 // Assert that AMM is unchanged.
+
5557 BEAST_EXPECT(amm.expectBalances(xrpBegin, iouBegin, amm.tokens()));
+
5558 },
+
5559 {{xrpPool, iouPool}},
+
5560 889,
+ +
5562 {testable_amendments() | fixAMMv1_1});
+
5563 }
+
5564
-
5565 fund(env, gw, {alice, carol}, XRP(1'000'000), {USD(1'000'000)});
-
5566 // This offer blocks AMM offer in pre-amendment
-
5567 env(offer(alice, XRP(1), USD(0.01)));
-
5568 env.close();
-
5569
-
5570 AMM amm(env, gw, XRP(200'000), USD(100'000));
-
5571
-
5572 // The offer doesn't cross AMM in pre-amendment code
-
5573 // It crosses AMM in post-amendment code
-
5574 env(offer(carol, USD(0.49), XRP(1)));
-
5575 env.close();
+
5565 void
+
+ +
5567 {
+
5568 testcase("AMM Offer Blocked By LOB");
+
5569 using namespace jtx;
+
5570
+
5571 // Low quality LOB offer blocks AMM liquidity
+
5572
+
5573 // USD/XRP crosses AMM
+
5574 {
+
5575 Env env(*this, features);
5576
-
5577 if (!features[fixAMMv1_1])
-
5578 {
-
5579 BEAST_EXPECT(amm.expectBalances(XRP(200'000), USD(100'000), amm.tokens()));
-
5580 BEAST_EXPECT(expectOffers(env, alice, 1, {{Amounts{XRP(1), USD(0.01)}}}));
-
5581 // Carol's offer is blocked by alice's offer
-
5582 BEAST_EXPECT(expectOffers(env, carol, 1, {{Amounts{USD(0.49), XRP(1)}}}));
-
5583 }
-
5584 else
-
5585 {
-
5586 BEAST_EXPECT(amm.expectBalances(XRPAmount(200'000'980'005), USD(99'999.51), amm.tokens()));
-
5587 BEAST_EXPECT(expectOffers(env, alice, 1, {{Amounts{XRP(1), USD(0.01)}}}));
-
5588 // Carol's offer crosses AMM
-
5589 BEAST_EXPECT(expectOffers(env, carol, 0));
-
5590 }
-
5591 }
-
5592
-
5593 // There is no blocking offer, the same AMM liquidity is consumed
-
5594 // pre- and post-amendment.
-
5595 {
-
5596 Env env(*this, features);
-
5597
-
5598 fund(env, gw, {alice, carol}, XRP(1'000'000), {USD(1'000'000)});
-
5599 // There is no blocking offer
-
5600 // env(offer(alice, XRP(1), USD(0.01)));
-
5601
-
5602 AMM amm(env, gw, XRP(200'000), USD(100'000));
-
5603
-
5604 // The offer crosses AMM
-
5605 env(offer(carol, USD(0.49), XRP(1)));
-
5606 env.close();
-
5607
-
5608 // The same result as with the blocking offer
-
5609 BEAST_EXPECT(amm.expectBalances(XRPAmount(200'000'980'005), USD(99'999.51), amm.tokens()));
-
5610 // Carol's offer crosses AMM
-
5611 BEAST_EXPECT(expectOffers(env, carol, 0));
-
5612 }
+
5577 fund(env, gw, {alice, carol}, XRP(1'000'000), {USD(1'000'000)});
+
5578 // This offer blocks AMM offer in pre-amendment
+
5579 env(offer(alice, XRP(1), USD(0.01)));
+
5580 env.close();
+
5581
+
5582 AMM amm(env, gw, XRP(200'000), USD(100'000));
+
5583
+
5584 // The offer doesn't cross AMM in pre-amendment code
+
5585 // It crosses AMM in post-amendment code
+
5586 env(offer(carol, USD(0.49), XRP(1)));
+
5587 env.close();
+
5588
+
5589 if (!features[fixAMMv1_1])
+
5590 {
+
5591 BEAST_EXPECT(amm.expectBalances(XRP(200'000), USD(100'000), amm.tokens()));
+
5592 BEAST_EXPECT(expectOffers(env, alice, 1, {{Amounts{XRP(1), USD(0.01)}}}));
+
5593 // Carol's offer is blocked by alice's offer
+
5594 BEAST_EXPECT(expectOffers(env, carol, 1, {{Amounts{USD(0.49), XRP(1)}}}));
+
5595 }
+
5596 else
+
5597 {
+
5598 BEAST_EXPECT(amm.expectBalances(XRPAmount(200'000'980'005), USD(99'999.51), amm.tokens()));
+
5599 BEAST_EXPECT(expectOffers(env, alice, 1, {{Amounts{XRP(1), USD(0.01)}}}));
+
5600 // Carol's offer crosses AMM
+
5601 BEAST_EXPECT(expectOffers(env, carol, 0));
+
5602 }
+
5603 }
+
5604
+
5605 // There is no blocking offer, the same AMM liquidity is consumed
+
5606 // pre- and post-amendment.
+
5607 {
+
5608 Env env(*this, features);
+
5609
+
5610 fund(env, gw, {alice, carol}, XRP(1'000'000), {USD(1'000'000)});
+
5611 // There is no blocking offer
+
5612 // env(offer(alice, XRP(1), USD(0.01)));
5613
-
5614 // XRP/USD crosses AMM
-
5615 {
-
5616 Env env(*this, features);
-
5617 fund(env, gw, {alice, carol, bob}, XRP(10'000), {USD(1'000)});
-
5618
-
5619 // This offer blocks AMM offer in pre-amendment
-
5620 // It crosses AMM in post-amendment code
-
5621 env(offer(bob, USD(1), XRPAmount(500)));
-
5622 env.close();
-
5623 AMM amm(env, alice, XRP(1'000), USD(500));
-
5624 env(offer(carol, XRP(100), USD(55)));
-
5625 env.close();
-
5626 if (!features[fixAMMv1_1])
-
5627 {
-
5628 BEAST_EXPECT(amm.expectBalances(XRP(1'000), USD(500), amm.tokens()));
-
5629 BEAST_EXPECT(expectOffers(env, bob, 1, {{Amounts{USD(1), XRPAmount(500)}}}));
-
5630 BEAST_EXPECT(expectOffers(env, carol, 1, {{Amounts{XRP(100), USD(55)}}}));
-
5631 }
-
5632 else
-
5633 {
-
5634 BEAST_EXPECT(amm.expectBalances(
-
5635 XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens()));
-
5636 BEAST_EXPECT(
-
5637 expectOffers(env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}}));
-
5638 BEAST_EXPECT(expectOffers(env, bob, 1, {{Amounts{USD(1), XRPAmount(500)}}}));
-
5639 }
-
5640 }
-
5641
-
5642 // There is no blocking offer, the same AMM liquidity is consumed
-
5643 // pre- and post-amendment.
-
5644 {
-
5645 Env env(*this, features);
-
5646 fund(env, gw, {alice, carol, bob}, XRP(10'000), {USD(1'000)});
-
5647
-
5648 AMM amm(env, alice, XRP(1'000), USD(500));
-
5649 env(offer(carol, XRP(100), USD(55)));
-
5650 env.close();
-
5651 BEAST_EXPECT(
-
5652 amm.expectBalances(XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens()));
-
5653 BEAST_EXPECT(expectOffers(env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}}));
-
5654 }
-
5655 }
+
5614 AMM amm(env, gw, XRP(200'000), USD(100'000));
+
5615
+
5616 // The offer crosses AMM
+
5617 env(offer(carol, USD(0.49), XRP(1)));
+
5618 env.close();
+
5619
+
5620 // The same result as with the blocking offer
+
5621 BEAST_EXPECT(amm.expectBalances(XRPAmount(200'000'980'005), USD(99'999.51), amm.tokens()));
+
5622 // Carol's offer crosses AMM
+
5623 BEAST_EXPECT(expectOffers(env, carol, 0));
+
5624 }
+
5625
+
5626 // XRP/USD crosses AMM
+
5627 {
+
5628 Env env(*this, features);
+
5629 fund(env, gw, {alice, carol, bob}, XRP(10'000), {USD(1'000)});
+
5630
+
5631 // This offer blocks AMM offer in pre-amendment
+
5632 // It crosses AMM in post-amendment code
+
5633 env(offer(bob, USD(1), XRPAmount(500)));
+
5634 env.close();
+
5635 AMM amm(env, alice, XRP(1'000), USD(500));
+
5636 env(offer(carol, XRP(100), USD(55)));
+
5637 env.close();
+
5638 if (!features[fixAMMv1_1])
+
5639 {
+
5640 BEAST_EXPECT(amm.expectBalances(XRP(1'000), USD(500), amm.tokens()));
+
5641 BEAST_EXPECT(expectOffers(env, bob, 1, {{Amounts{USD(1), XRPAmount(500)}}}));
+
5642 BEAST_EXPECT(expectOffers(env, carol, 1, {{Amounts{XRP(100), USD(55)}}}));
+
5643 }
+
5644 else
+
5645 {
+
5646 BEAST_EXPECT(amm.expectBalances(
+
5647 XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens()));
+
5648 BEAST_EXPECT(
+
5649 expectOffers(env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}}));
+
5650 BEAST_EXPECT(expectOffers(env, bob, 1, {{Amounts{USD(1), XRPAmount(500)}}}));
+
5651 }
+
5652 }
+
5653
+
5654 // There is no blocking offer, the same AMM liquidity is consumed
+
5655 // pre- and post-amendment.
+
5656 {
+
5657 Env env(*this, features);
+
5658 fund(env, gw, {alice, carol, bob}, XRP(10'000), {USD(1'000)});
+
5659
+
5660 AMM amm(env, alice, XRP(1'000), USD(500));
+
5661 env(offer(carol, XRP(100), USD(55)));
+
5662 env.close();
+
5663 BEAST_EXPECT(
+
5664 amm.expectBalances(XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens()));
+
5665 BEAST_EXPECT(expectOffers(env, carol, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}}));
+
5666 }
+
5667 }
-
5656
-
5657 void
-
- -
5659 {
-
5660 testcase("LPToken Balance");
-
5661 using namespace jtx;
-
5662
-
5663 // Last Liquidity Provider is the issuer of one token
-
5664 {
-
5665 std::string logs;
-
5666 Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
-
5667 fund(env, gw, {alice, carol}, XRP(1'000'000'000), {USD(1'000'000'000)});
-
5668 AMM amm(env, gw, XRP(2), USD(1));
-
5669 amm.deposit(alice, IOUAmount{1'876123487565916, -15});
-
5670 amm.deposit(carol, IOUAmount{1'000'000});
-
5671 amm.withdrawAll(alice);
-
5672 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{0}));
-
5673 amm.withdrawAll(carol);
-
5674 BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{0}));
-
5675 auto const lpToken = getAccountLines(env, gw, amm.lptIssue())[jss::lines][0u][jss::balance];
-
5676 auto const lpTokenBalance = amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value];
-
5677 BEAST_EXPECT(lpToken == "1414.213562373095" && lpTokenBalance == "1414.213562373");
-
5678 if (!features[fixAMMv1_1])
-
5679 {
-
5680 amm.withdrawAll(gw, std::nullopt, ter(tecAMM_BALANCE));
-
5681 BEAST_EXPECT(amm.ammExists());
-
5682 }
-
5683 else
-
5684 {
-
5685 amm.withdrawAll(gw);
-
5686 BEAST_EXPECT(!amm.ammExists());
-
5687 }
-
5688 }
-
5689
-
5690 // Last Liquidity Provider is the issuer of two tokens, or not
-
5691 // the issuer
-
5692 for (auto const& lp : {gw, bob})
-
5693 {
-
5694 Env env(*this, features);
-
5695 auto const ABC = gw["ABC"];
-
5696 fund(env, gw, {alice, carol, bob}, XRP(1'000), {USD(1'000'000'000), ABC(1'000'000'000'000)});
-
5697 AMM amm(env, lp, ABC(2'000'000), USD(1));
-
5698 amm.deposit(alice, IOUAmount{1'876123487565916, -15});
-
5699 amm.deposit(carol, IOUAmount{1'000'000});
-
5700 amm.withdrawAll(alice);
-
5701 amm.withdrawAll(carol);
-
5702 auto const lpToken = getAccountLines(env, lp, amm.lptIssue())[jss::lines][0u][jss::balance];
-
5703 auto const lpTokenBalance = amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value];
-
5704 BEAST_EXPECT(lpToken == "1414.213562373095" && lpTokenBalance == "1414.213562373");
-
5705 if (!features[fixAMMv1_1])
-
5706 {
-
5707 amm.withdrawAll(lp, std::nullopt, ter(tecAMM_BALANCE));
-
5708 BEAST_EXPECT(amm.ammExists());
-
5709 }
-
5710 else
-
5711 {
-
5712 amm.withdrawAll(lp);
-
5713 BEAST_EXPECT(!amm.ammExists());
-
5714 }
-
5715 }
-
5716
-
5717 // More than one Liquidity Provider
-
5718 // XRP/IOU
-
5719 {
-
5720 Env env(*this, features);
-
5721 fund(env, gw, {alice}, XRP(1'000), {USD(1'000)});
-
5722 AMM amm(env, gw, XRP(10), USD(10));
-
5723 amm.deposit(alice, 1'000);
-
5724 auto res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), gw);
-
5725 BEAST_EXPECT(res && !res.value());
-
5726 res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), alice);
-
5727 BEAST_EXPECT(res && !res.value());
-
5728 }
-
5729 // IOU/IOU, issuer of both IOU
-
5730 {
-
5731 Env env(*this, features);
-
5732 fund(env, gw, {alice}, XRP(1'000), {USD(1'000), EUR(1'000)});
-
5733 AMM amm(env, gw, EUR(10), USD(10));
-
5734 amm.deposit(alice, 1'000);
-
5735 auto res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), gw);
-
5736 BEAST_EXPECT(res && !res.value());
-
5737 res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), alice);
-
5738 BEAST_EXPECT(res && !res.value());
-
5739 }
-
5740 // IOU/IOU, issuer of one IOU
-
5741 {
-
5742 Env env(*this, features);
-
5743 Account const gw1("gw1");
-
5744 auto const YAN = gw1["YAN"];
-
5745 fund(env, gw, {gw1}, XRP(1'000), {USD(1'000)});
-
5746 fund(env, gw1, {gw}, XRP(1'000), {YAN(1'000)}, Fund::IOUOnly);
-
5747 AMM amm(env, gw1, YAN(10), USD(10));
-
5748 amm.deposit(gw, 1'000);
-
5749 auto res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), gw);
+
5668
+
5669 void
+
+ +
5671 {
+
5672 testcase("LPToken Balance");
+
5673 using namespace jtx;
+
5674
+
5675 // Last Liquidity Provider is the issuer of one token
+
5676 {
+
5677 std::string logs;
+
5678 Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
+
5679 fund(env, gw, {alice, carol}, XRP(1'000'000'000), {USD(1'000'000'000)});
+
5680 AMM amm(env, gw, XRP(2), USD(1));
+
5681 amm.deposit(alice, IOUAmount{1'876123487565916, -15});
+
5682 amm.deposit(carol, IOUAmount{1'000'000});
+
5683 amm.withdrawAll(alice);
+
5684 BEAST_EXPECT(amm.expectLPTokens(alice, IOUAmount{0}));
+
5685 amm.withdrawAll(carol);
+
5686 BEAST_EXPECT(amm.expectLPTokens(carol, IOUAmount{0}));
+
5687 auto const lpToken = getAccountLines(env, gw, amm.lptIssue())[jss::lines][0u][jss::balance];
+
5688 auto const lpTokenBalance = amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value];
+
5689 BEAST_EXPECT(lpToken == "1414.213562373095" && lpTokenBalance == "1414.213562373");
+
5690 if (!features[fixAMMv1_1])
+
5691 {
+
5692 amm.withdrawAll(gw, std::nullopt, ter(tecAMM_BALANCE));
+
5693 BEAST_EXPECT(amm.ammExists());
+
5694 }
+
5695 else
+
5696 {
+
5697 amm.withdrawAll(gw);
+
5698 BEAST_EXPECT(!amm.ammExists());
+
5699 }
+
5700 }
+
5701
+
5702 // Last Liquidity Provider is the issuer of two tokens, or not
+
5703 // the issuer
+
5704 for (auto const& lp : {gw, bob})
+
5705 {
+
5706 Env env(*this, features);
+
5707 auto const ABC = gw["ABC"];
+
5708 fund(env, gw, {alice, carol, bob}, XRP(1'000), {USD(1'000'000'000), ABC(1'000'000'000'000)});
+
5709 AMM amm(env, lp, ABC(2'000'000), USD(1));
+
5710 amm.deposit(alice, IOUAmount{1'876123487565916, -15});
+
5711 amm.deposit(carol, IOUAmount{1'000'000});
+
5712 amm.withdrawAll(alice);
+
5713 amm.withdrawAll(carol);
+
5714 auto const lpToken = getAccountLines(env, lp, amm.lptIssue())[jss::lines][0u][jss::balance];
+
5715 auto const lpTokenBalance = amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value];
+
5716 BEAST_EXPECT(lpToken == "1414.213562373095" && lpTokenBalance == "1414.213562373");
+
5717 if (!features[fixAMMv1_1])
+
5718 {
+
5719 amm.withdrawAll(lp, std::nullopt, ter(tecAMM_BALANCE));
+
5720 BEAST_EXPECT(amm.ammExists());
+
5721 }
+
5722 else
+
5723 {
+
5724 amm.withdrawAll(lp);
+
5725 BEAST_EXPECT(!amm.ammExists());
+
5726 }
+
5727 }
+
5728
+
5729 // More than one Liquidity Provider
+
5730 // XRP/IOU
+
5731 {
+
5732 Env env(*this, features);
+
5733 fund(env, gw, {alice}, XRP(1'000), {USD(1'000)});
+
5734 AMM amm(env, gw, XRP(10), USD(10));
+
5735 amm.deposit(alice, 1'000);
+
5736 auto res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), gw);
+
5737 BEAST_EXPECT(res && !res.value());
+
5738 res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), alice);
+
5739 BEAST_EXPECT(res && !res.value());
+
5740 }
+
5741 // IOU/IOU, issuer of both IOU
+
5742 {
+
5743 Env env(*this, features);
+
5744 fund(env, gw, {alice}, XRP(1'000), {USD(1'000), EUR(1'000)});
+
5745 AMM amm(env, gw, EUR(10), USD(10));
+
5746 amm.deposit(alice, 1'000);
+
5747 auto res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), gw);
+
5748 BEAST_EXPECT(res && !res.value());
+
5749 res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), alice);
5750 BEAST_EXPECT(res && !res.value());
-
5751 res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), gw1);
-
5752 BEAST_EXPECT(res && !res.value());
-
5753 }
-
5754 }
+
5751 }
+
5752 // IOU/IOU, issuer of one IOU
+
5753 {
+
5754 Env env(*this, features);
+
5755 Account const gw1("gw1");
+
5756 auto const YAN = gw1["YAN"];
+
5757 fund(env, gw, {gw1}, XRP(1'000), {USD(1'000)});
+
5758 fund(env, gw1, {gw}, XRP(1'000), {YAN(1'000)}, Fund::IOUOnly);
+
5759 AMM amm(env, gw1, YAN(10), USD(10));
+
5760 amm.deposit(gw, 1'000);
+
5761 auto res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), gw);
+
5762 BEAST_EXPECT(res && !res.value());
+
5763 res = isOnlyLiquidityProvider(*env.current(), amm.lptIssue(), gw1);
+
5764 BEAST_EXPECT(res && !res.value());
+
5765 }
+
5766 }
-
5755
-
5756 void
-
- -
5758 {
-
5759 testcase("test clawback from AMM account");
-
5760 using namespace jtx;
-
5761
-
5762 // Issuer has clawback enabled
-
5763 Env env(*this, features);
-
5764 env.fund(XRP(1'000), gw);
- -
5766 fund(env, gw, {alice}, XRP(1'000), {USD(1'000)}, Fund::Acct);
-
5767 env.close();
-
5768
-
5769 // If featureAMMClawback is not enabled, AMMCreate is not allowed for
-
5770 // clawback-enabled issuer
-
5771 if (!features[featureAMMClawback])
-
5772 {
-
5773 AMM amm(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION));
-
5774 AMM amm1(env, alice, USD(100), XRP(100), ter(tecNO_PERMISSION));
- -
5776 env.close();
-
5777 // Can't be cleared
-
5778 AMM amm2(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION));
-
5779 }
-
5780 // If featureAMMClawback is enabled, AMMCreate is allowed for
-
5781 // clawback-enabled issuer. Clawback from the AMM Account is not
-
5782 // allowed, which will return tecAMM_ACCOUNT or tecPSEUDO_ACCOUNT,
-
5783 // depending on whether SingleAssetVault is enabled. We can only use
-
5784 // AMMClawback transaction to claw back from AMM Account.
-
5785 else
-
5786 {
-
5787 AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
-
5788 AMM amm1(env, alice, USD(100), XRP(200), ter(tecDUPLICATE));
-
5789
-
5790 // Construct the amount being clawed back using AMM account.
-
5791 // By doing this, we make the clawback transaction's Amount field's
-
5792 // subfield `issuer` to be the AMM account, which means
-
5793 // we are clawing back from an AMM account. This should return an
-
5794 // error because regular Clawback transaction is not
-
5795 // allowed for clawing back from an AMM account. Please notice the
-
5796 // `issuer` subfield represents the account being clawed back, which
-
5797 // is confusing.
-
5798 auto const error = features[featureSingleAssetVault] ? ter{tecPSEUDO_ACCOUNT} : ter{tecAMM_ACCOUNT};
-
5799 Issue usd(USD.issue().currency, amm.ammAccount());
-
5800 auto amount = amountFromString(usd, "10");
-
5801 env(claw(gw, amount), error);
-
5802 }
-
5803 }
+
5767
+
5768 void
+
+ +
5770 {
+
5771 testcase("test clawback from AMM account");
+
5772 using namespace jtx;
+
5773
+
5774 // Issuer has clawback enabled
+
5775 Env env(*this, features);
+
5776 env.fund(XRP(1'000), gw);
+ +
5778 fund(env, gw, {alice}, XRP(1'000), {USD(1'000)}, Fund::Acct);
+
5779 env.close();
+
5780
+
5781 // If featureAMMClawback is not enabled, AMMCreate is not allowed for
+
5782 // clawback-enabled issuer
+
5783 if (!features[featureAMMClawback])
+
5784 {
+
5785 AMM amm(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION));
+
5786 AMM amm1(env, alice, USD(100), XRP(100), ter(tecNO_PERMISSION));
+ +
5788 env.close();
+
5789 // Can't be cleared
+
5790 AMM amm2(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION));
+
5791 }
+
5792 // If featureAMMClawback is enabled, AMMCreate is allowed for
+
5793 // clawback-enabled issuer. Clawback from the AMM Account is not
+
5794 // allowed, which will return tecAMM_ACCOUNT or tecPSEUDO_ACCOUNT,
+
5795 // depending on whether SingleAssetVault is enabled. We can only use
+
5796 // AMMClawback transaction to claw back from AMM Account.
+
5797 else
+
5798 {
+
5799 AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
+
5800 AMM amm1(env, alice, USD(100), XRP(200), ter(tecDUPLICATE));
+
5801
+
5802 // Construct the amount being clawed back using AMM account.
+
5803 // By doing this, we make the clawback transaction's Amount field's
+
5804 // subfield `issuer` to be the AMM account, which means
+
5805 // we are clawing back from an AMM account. This should return an
+
5806 // error because regular Clawback transaction is not
+
5807 // allowed for clawing back from an AMM account. Please notice the
+
5808 // `issuer` subfield represents the account being clawed back, which
+
5809 // is confusing.
+
5810 auto const error = features[featureSingleAssetVault] ? ter{tecPSEUDO_ACCOUNT} : ter{tecAMM_ACCOUNT};
+
5811 Issue usd(USD.issue().currency, amm.ammAccount());
+
5812 auto amount = amountFromString(usd, "10");
+
5813 env(claw(gw, amount), error);
+
5814 }
+
5815 }
-
5804
-
5805 void
-
- -
5807 {
-
5808 testcase("test AMMDeposit with frozen assets");
-
5809 using namespace jtx;
-
5810
-
5811 // This lambda function is used to create trustlines
-
5812 // between gw and alice, and create an AMM account.
-
5813 // And also test the callback function.
-
5814 auto testAMMDeposit = [&](Env& env, std::function<void(AMM & amm)> cb) {
-
5815 env.fund(XRP(1'000), gw);
-
5816 fund(env, gw, {alice}, XRP(1'000), {USD(1'000)}, Fund::Acct);
-
5817 env.close();
-
5818 AMM amm(env, alice, XRP(100), USD(100), ter(tesSUCCESS));
-
5819 env(trust(gw, alice["USD"](0), tfSetFreeze));
-
5820 cb(amm);
-
5821 };
+
5816
+
5817 void
+
+ +
5819 {
+
5820 testcase("test AMMDeposit with frozen assets");
+
5821 using namespace jtx;
5822
-
5823 // Deposit two assets, one of which is frozen,
-
5824 // then we should get tecFROZEN error.
-
5825 {
-
5826 Env env(*this, features);
-
5827 testAMMDeposit(env, [&](AMM& amm) {
-
5828 amm.deposit(alice, USD(100), XRP(100), std::nullopt, tfTwoAsset, ter(tecFROZEN));
-
5829 });
-
5830 }
-
5831
-
5832 // Deposit one asset, which is the frozen token,
-
5833 // then we should get tecFROZEN error.
-
5834 {
-
5835 Env env(*this, features);
-
5836 testAMMDeposit(env, [&](AMM& amm) {
-
5837 amm.deposit(alice, USD(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN));
-
5838 });
-
5839 }
-
5840
-
5841 if (features[featureAMMClawback])
-
5842 {
-
5843 // Deposit one asset which is not the frozen token,
-
5844 // but the other asset is frozen. We should get tecFROZEN error
-
5845 // when feature AMMClawback is enabled.
-
5846 Env env(*this, features);
-
5847 testAMMDeposit(env, [&](AMM& amm) {
-
5848 amm.deposit(alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN));
-
5849 });
-
5850 }
-
5851 else
-
5852 {
-
5853 // Deposit one asset which is not the frozen token,
-
5854 // but the other asset is frozen. We will get tecSUCCESS
-
5855 // when feature AMMClawback is not enabled.
-
5856 Env env(*this, features);
-
5857 testAMMDeposit(env, [&](AMM& amm) {
-
5858 amm.deposit(alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tesSUCCESS));
-
5859 });
-
5860 }
-
5861 }
+
5823 // This lambda function is used to create trustlines
+
5824 // between gw and alice, and create an AMM account.
+
5825 // And also test the callback function.
+
5826 auto testAMMDeposit = [&](Env& env, std::function<void(AMM & amm)> cb) {
+
5827 env.fund(XRP(1'000), gw);
+
5828 fund(env, gw, {alice}, XRP(1'000), {USD(1'000)}, Fund::Acct);
+
5829 env.close();
+
5830 AMM amm(env, alice, XRP(100), USD(100), ter(tesSUCCESS));
+
5831 env(trust(gw, alice["USD"](0), tfSetFreeze));
+
5832 cb(amm);
+
5833 };
+
5834
+
5835 // Deposit two assets, one of which is frozen,
+
5836 // then we should get tecFROZEN error.
+
5837 {
+
5838 Env env(*this, features);
+
5839 testAMMDeposit(env, [&](AMM& amm) {
+
5840 amm.deposit(alice, USD(100), XRP(100), std::nullopt, tfTwoAsset, ter(tecFROZEN));
+
5841 });
+
5842 }
+
5843
+
5844 // Deposit one asset, which is the frozen token,
+
5845 // then we should get tecFROZEN error.
+
5846 {
+
5847 Env env(*this, features);
+
5848 testAMMDeposit(env, [&](AMM& amm) {
+
5849 amm.deposit(alice, USD(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN));
+
5850 });
+
5851 }
+
5852
+
5853 if (features[featureAMMClawback])
+
5854 {
+
5855 // Deposit one asset which is not the frozen token,
+
5856 // but the other asset is frozen. We should get tecFROZEN error
+
5857 // when feature AMMClawback is enabled.
+
5858 Env env(*this, features);
+
5859 testAMMDeposit(env, [&](AMM& amm) {
+
5860 amm.deposit(alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tecFROZEN));
+
5861 });
+
5862 }
+
5863 else
+
5864 {
+
5865 // Deposit one asset which is not the frozen token,
+
5866 // but the other asset is frozen. We will get tecSUCCESS
+
5867 // when feature AMMClawback is not enabled.
+
5868 Env env(*this, features);
+
5869 testAMMDeposit(env, [&](AMM& amm) {
+
5870 amm.deposit(alice, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, ter(tesSUCCESS));
+
5871 });
+
5872 }
+
5873 }
-
5862
-
5863 void
-
- -
5865 {
-
5866 testcase("Fix Reserve Check On Withdrawal");
-
5867 using namespace jtx;
-
5868
-
5869 auto const err = features[fixAMMv1_2] ? ter(tecINSUFFICIENT_RESERVE) : ter(tesSUCCESS);
-
5870
-
5871 auto test = [&](auto&& cb) {
-
5872 Env env(*this, features);
-
5873 auto const starting_xrp = reserve(env, 2) + env.current()->fees().base * 5;
-
5874 env.fund(starting_xrp, gw);
-
5875 env.fund(starting_xrp, alice);
-
5876 env.trust(USD(2'000), alice);
-
5877 env.close();
-
5878 env(pay(gw, alice, USD(2'000)));
-
5879 env.close();
-
5880 AMM amm(env, gw, EUR(1'000), USD(1'000));
-
5881 amm.deposit(alice, USD(1));
-
5882 cb(amm);
-
5883 };
-
5884
-
5885 // Equal withdraw
-
5886 test([&](AMM& amm) { amm.withdrawAll(alice, std::nullopt, err); });
-
5887
-
5888 // Equal withdraw with a limit
-
5889 test([&](AMM& amm) {
-
5890 amm.withdraw(WithdrawArg{.account = alice, .asset1Out = EUR(0.1), .asset2Out = USD(0.1), .err = err});
-
5891 amm.withdraw(WithdrawArg{.account = alice, .asset1Out = USD(0.1), .asset2Out = EUR(0.1), .err = err});
-
5892 });
-
5893
-
5894 // Single withdraw
-
5895 test([&](AMM& amm) {
-
5896 amm.withdraw(WithdrawArg{.account = alice, .asset1Out = EUR(0.1), .err = err});
-
5897 amm.withdraw(WithdrawArg{.account = alice, .asset1Out = USD(0.1)});
-
5898 });
-
5899 }
-
-
5900
-
5901 void
-
- -
5903 {
-
5904 using namespace test::jtx;
+
5874
+
5875 void
+
+ +
5877 {
+
5878 testcase("Fix Reserve Check On Withdrawal");
+
5879 using namespace jtx;
+
5880
+
5881 auto const err = features[fixAMMv1_2] ? ter(tecINSUFFICIENT_RESERVE) : ter(tesSUCCESS);
+
5882
+
5883 auto test = [&](auto&& cb) {
+
5884 Env env(*this, features);
+
5885 auto const starting_xrp = reserve(env, 2) + env.current()->fees().base * 5;
+
5886 env.fund(starting_xrp, gw);
+
5887 env.fund(starting_xrp, alice);
+
5888 env.trust(USD(2'000), alice);
+
5889 env.close();
+
5890 env(pay(gw, alice, USD(2'000)));
+
5891 env.close();
+
5892 AMM amm(env, gw, EUR(1'000), USD(1'000));
+
5893 amm.deposit(alice, USD(1));
+
5894 cb(amm);
+
5895 };
+
5896
+
5897 // Equal withdraw
+
5898 test([&](AMM& amm) { amm.withdrawAll(alice, std::nullopt, err); });
+
5899
+
5900 // Equal withdraw with a limit
+
5901 test([&](AMM& amm) {
+
5902 amm.withdraw(WithdrawArg{.account = alice, .asset1Out = EUR(0.1), .asset2Out = USD(0.1), .err = err});
+
5903 amm.withdraw(WithdrawArg{.account = alice, .asset1Out = USD(0.1), .asset2Out = EUR(0.1), .err = err});
+
5904 });
5905
-
5906 auto const testCase = [&](std::string suffix, FeatureBitset features) {
-
5907 testcase("Fail pseudo-account allocation " + suffix);
-
5908 std::string logs;
-
5909 Env env{*this, features, std::make_unique<CaptureLogs>(&logs)};
-
5910 env.fund(XRP(30'000), gw, alice);
-
5911 env.close();
-
5912 env(trust(alice, gw["USD"](30'000), 0));
-
5913 env(pay(gw, alice, USD(10'000)));
-
5914 env.close();
-
5915
-
5916 STAmount amount = XRP(10'000);
-
5917 STAmount amount2 = USD(10'000);
-
5918 auto const keylet = keylet::amm(amount.issue(), amount2.issue());
-
5919 for (int i = 0; i < 256; ++i)
-
5920 {
-
5921 AccountID const accountId = xrpl::pseudoAccountAddress(*env.current(), keylet.key);
-
5922
-
5923 env(pay(env.master.id(), accountId, XRP(1000)), seq(autofill), fee(autofill), sig(autofill));
-
5924 }
-
5925
-
5926 AMM ammAlice(
-
5927 env,
-
5928 alice,
-
5929 amount,
-
5930 amount2,
-
5931 features[featureSingleAssetVault] ? ter{terADDRESS_COLLISION} : ter{tecDUPLICATE});
-
5932 };
-
5933
-
5934 testCase("tecDUPLICATE", testable_amendments() - featureSingleAssetVault);
-
5935 testCase("terADDRESS_COLLISION", testable_amendments() | featureSingleAssetVault);
-
5936 }
+
5906 // Single withdraw
+
5907 test([&](AMM& amm) {
+
5908 amm.withdraw(WithdrawArg{.account = alice, .asset1Out = EUR(0.1), .err = err});
+
5909 amm.withdraw(WithdrawArg{.account = alice, .asset1Out = USD(0.1)});
+
5910 });
+
5911 }
+
5912
+
5913 void
+
+ +
5915 {
+
5916 using namespace test::jtx;
+
5917
+
5918 auto const testCase = [&](std::string suffix, FeatureBitset features) {
+
5919 testcase("Fail pseudo-account allocation " + suffix);
+
5920 std::string logs;
+
5921 Env env{*this, features, std::make_unique<CaptureLogs>(&logs)};
+
5922 env.fund(XRP(30'000), gw, alice);
+
5923 env.close();
+
5924 env(trust(alice, gw["USD"](30'000), 0));
+
5925 env(pay(gw, alice, USD(10'000)));
+
5926 env.close();
+
5927
+
5928 STAmount amount = XRP(10'000);
+
5929 STAmount amount2 = USD(10'000);
+
5930 auto const keylet = keylet::amm(amount.issue(), amount2.issue());
+
5931 for (int i = 0; i < 256; ++i)
+
5932 {
+
5933 AccountID const accountId = xrpl::pseudoAccountAddress(*env.current(), keylet.key);
+
5934
+
5935 env(pay(env.master.id(), accountId, XRP(1000)), seq(autofill), fee(autofill), sig(autofill));
+
5936 }
5937
-
5938 void
-
- -
5940 {
-
5941 testcase("Deposit and Withdraw Rounding V2");
-
5942 using namespace jtx;
-
5943
-
5944 auto const XPM = gw["XPM"];
-
5945 STAmount xrpBalance{XRPAmount(692'614'492'126)};
-
5946 STAmount xpmBalance{XPM, UINT64_C(18'610'359'80246901), -8};
-
5947 STAmount amount{XPM, UINT64_C(6'566'496939465400), -12};
-
5948 std::uint16_t tfee = 941;
+
5938 AMM ammAlice(
+
5939 env,
+
5940 alice,
+
5941 amount,
+
5942 amount2,
+
5943 features[featureSingleAssetVault] ? ter{terADDRESS_COLLISION} : ter{tecDUPLICATE});
+
5944 };
+
5945
+
5946 testCase("tecDUPLICATE", testable_amendments() - featureSingleAssetVault);
+
5947 testCase("terADDRESS_COLLISION", testable_amendments() | featureSingleAssetVault);
+
5948 }
+
5949
-
5950 auto test = [&](auto&& cb, std::uint16_t tfee_) {
-
5951 Env env(*this, features);
-
5952 env.fund(XRP(1'000'000), gw);
-
5953 env.fund(XRP(1'000), alice);
-
5954 env(trust(alice, XPM(7'000)));
-
5955 env(pay(gw, alice, amount));
-
5956
-
5957 AMM amm(env, gw, xrpBalance, xpmBalance, CreateArg{.tfee = tfee_});
-
5958 // AMM LPToken balance required to replicate single deposit failure
-
5959 STAmount lptAMMBalance{amm.lptIssue(), UINT64_C(3'234'987'266'485968), -6};
-
5960 auto const burn = IOUAmount{amm.getLPTokensBalance() - lptAMMBalance};
-
5961 // burn tokens to get to the required AMM state
-
5962 env(amm.bid(BidArg{.account = gw, .bidMin = burn, .bidMax = burn}));
-
5963 cb(amm, env);
-
5964 };
-
5965 test(
-
5966 [&](AMM& amm, Env& env) {
-
5967 auto const err = env.enabled(fixAMMv1_3) ? ter(tesSUCCESS) : ter(tecUNFUNDED_AMM);
-
5968 amm.deposit(DepositArg{.account = alice, .asset1In = amount, .err = err});
-
5969 },
-
5970 tfee);
-
5971 test(
-
5972 [&](AMM& amm, Env& env) {
-
5973 auto const [amount, amount2, lptAMM] = amm.balances(XRP, XPM);
-
5974 auto const withdraw = STAmount{XPM, 1, -5};
-
5975 amm.withdraw(WithdrawArg{.asset1Out = STAmount{XPM, 1, -5}});
-
5976 auto const [amount_, amount2_, lptAMM_] = amm.balances(XRP, XPM);
-
5977 if (!env.enabled(fixAMMv1_3))
-
5978 BEAST_EXPECT((amount2 - amount2_) > withdraw);
-
5979 else
-
5980 BEAST_EXPECT((amount2 - amount2_) <= withdraw);
+
5950 void
+
+ +
5952 {
+
5953 testcase("Deposit and Withdraw Rounding V2");
+
5954 using namespace jtx;
+
5955
+
5956 auto const XPM = gw["XPM"];
+
5957 STAmount xrpBalance{XRPAmount(692'614'492'126)};
+
5958 STAmount xpmBalance{XPM, UINT64_C(18'610'359'80246901), -8};
+
5959 STAmount amount{XPM, UINT64_C(6'566'496939465400), -12};
+
5960 std::uint16_t tfee = 941;
+
5961
+
5962 auto test = [&](auto&& cb, std::uint16_t tfee_) {
+
5963 Env env(*this, features);
+
5964 env.fund(XRP(1'000'000), gw);
+
5965 env.fund(XRP(1'000), alice);
+
5966 env(trust(alice, XPM(7'000)));
+
5967 env(pay(gw, alice, amount));
+
5968
+
5969 AMM amm(env, gw, xrpBalance, xpmBalance, CreateArg{.tfee = tfee_});
+
5970 // AMM LPToken balance required to replicate single deposit failure
+
5971 STAmount lptAMMBalance{amm.lptIssue(), UINT64_C(3'234'987'266'485968), -6};
+
5972 auto const burn = IOUAmount{amm.getLPTokensBalance() - lptAMMBalance};
+
5973 // burn tokens to get to the required AMM state
+
5974 env(amm.bid(BidArg{.account = gw, .bidMin = burn, .bidMax = burn}));
+
5975 cb(amm, env);
+
5976 };
+
5977 test(
+
5978 [&](AMM& amm, Env& env) {
+
5979 auto const err = env.enabled(fixAMMv1_3) ? ter(tesSUCCESS) : ter(tecUNFUNDED_AMM);
+
5980 amm.deposit(DepositArg{.account = alice, .asset1In = amount, .err = err});
5981 },
-
5982 0);
-
5983 }
+
5982 tfee);
+
5983 test(
+
5984 [&](AMM& amm, Env& env) {
+
5985 auto const [amount, amount2, lptAMM] = amm.balances(XRP, XPM);
+
5986 auto const withdraw = STAmount{XPM, 1, -5};
+
5987 amm.withdraw(WithdrawArg{.asset1Out = STAmount{XPM, 1, -5}});
+
5988 auto const [amount_, amount2_, lptAMM_] = amm.balances(XRP, XPM);
+
5989 if (!env.enabled(fixAMMv1_3))
+
5990 BEAST_EXPECT((amount2 - amount2_) > withdraw);
+
5991 else
+
5992 BEAST_EXPECT((amount2 - amount2_) <= withdraw);
+
5993 },
+
5994 0);
+
5995 }
-
5984
-
5985 void
-
-
5986 invariant(jtx::AMM& amm, jtx::Env& env, std::string const& msg, bool shouldFail)
-
5987 {
-
5988 auto const [amount, amount2, lptBalance] = amm.balances(GBP, EUR);
-
5989
-
5990 NumberMantissaScaleGuard sg(MantissaRange::small);
-
5991 NumberRoundModeGuard g(env.enabled(fixAMMv1_3) ? Number::upward : Number::getround());
-
5992 auto const res = root2(amount * amount2);
-
5993
-
5994 if (shouldFail)
-
5995 BEAST_EXPECT(res < lptBalance);
-
5996 else
-
5997 BEAST_EXPECT(res >= lptBalance);
-
5998 }
-
-
5999
-
6000 void
-
- -
6002 {
-
6003 testcase("Deposit Rounding");
-
6004 using namespace jtx;
+
5996
+
5997 void
+
+
5998 invariant(jtx::AMM& amm, jtx::Env& env, std::string const& msg, bool shouldFail)
+
5999 {
+
6000 auto const [amount, amount2, lptBalance] = amm.balances(GBP, EUR);
+
6001
+
6002 NumberMantissaScaleGuard sg(MantissaRange::small);
+
6003 NumberRoundModeGuard g(env.enabled(fixAMMv1_3) ? Number::upward : Number::getround());
+
6004 auto const res = root2(amount * amount2);
6005
-
6006 // Single asset deposit
-
6007 for (auto const& deposit :
-
6008 {STAmount(EUR, 1, 1),
-
6009 STAmount(EUR, 1, 2),
-
6010 STAmount(EUR, 1, 5),
-
6011 STAmount(EUR, 1, -3), // fail
-
6012 STAmount(EUR, 1, -6),
-
6013 STAmount(EUR, 1, -9)})
-
6014 {
-
6015 testAMM(
-
6016 [&](AMM& ammAlice, Env& env) {
-
6017 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
-
6018 env.close();
-
6019
-
6020 ammAlice.deposit(DepositArg{.account = bob, .asset1In = deposit});
-
6021 invariant(ammAlice, env, "dep1", deposit == STAmount{EUR, 1, -3} && !env.enabled(fixAMMv1_3));
-
6022 },
-
6023 {{GBP(30'000), EUR(30'000)}},
-
6024 0,
- -
6026 {all});
-
6027 }
-
6028
-
6029 // Two-asset proportional deposit (1:1 pool ratio)
-
6030 testAMM(
-
6031 [&](AMM& ammAlice, Env& env) {
-
6032 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
-
6033 env.close();
-
6034
-
6035 STAmount const depositEuro{EUR, UINT64_C(10'1234567890123456), -16};
-
6036 STAmount const depositGBP{GBP, UINT64_C(10'1234567890123456), -16};
-
6037
-
6038 ammAlice.deposit(DepositArg{.account = bob, .asset1In = depositEuro, .asset2In = depositGBP});
-
6039 invariant(ammAlice, env, "dep2", false);
-
6040 },
-
6041 {{GBP(30'000), EUR(30'000)}},
-
6042 0,
- -
6044 {all});
-
6045
-
6046 // Two-asset proportional deposit (1:3 pool ratio)
-
6047 for (auto const& exponent : {1, 2, 3, 4, -3 /*fail*/, -6, -9})
-
6048 {
-
6049 testAMM(
-
6050 [&](AMM& ammAlice, Env& env) {
-
6051 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
-
6052 env.close();
-
6053
-
6054 STAmount const depositEuro{EUR, 1, exponent};
-
6055 STAmount const depositGBP{GBP, 1, exponent};
-
6056
-
6057 ammAlice.deposit(DepositArg{.account = bob, .asset1In = depositEuro, .asset2In = depositGBP});
-
6058 invariant(ammAlice, env, "dep3", exponent != -3 && !env.enabled(fixAMMv1_3));
-
6059 },
-
6060 {{GBP(10'000), EUR(30'000)}},
-
6061 0,
- -
6063 {all});
-
6064 }
+
6006 if (shouldFail)
+
6007 BEAST_EXPECT(res < lptBalance);
+
6008 else
+
6009 BEAST_EXPECT(res >= lptBalance);
+
6010 }
+
+
6011
+
6012 void
+
+ +
6014 {
+
6015 testcase("Deposit Rounding");
+
6016 using namespace jtx;
+
6017
+
6018 // Single asset deposit
+
6019 for (auto const& deposit :
+
6020 {STAmount(EUR, 1, 1),
+
6021 STAmount(EUR, 1, 2),
+
6022 STAmount(EUR, 1, 5),
+
6023 STAmount(EUR, 1, -3), // fail
+
6024 STAmount(EUR, 1, -6),
+
6025 STAmount(EUR, 1, -9)})
+
6026 {
+
6027 testAMM(
+
6028 [&](AMM& ammAlice, Env& env) {
+
6029 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
+
6030 env.close();
+
6031
+
6032 ammAlice.deposit(DepositArg{.account = bob, .asset1In = deposit});
+
6033 invariant(ammAlice, env, "dep1", deposit == STAmount{EUR, 1, -3} && !env.enabled(fixAMMv1_3));
+
6034 },
+
6035 {{GBP(30'000), EUR(30'000)}},
+
6036 0,
+ +
6038 {all});
+
6039 }
+
6040
+
6041 // Two-asset proportional deposit (1:1 pool ratio)
+
6042 testAMM(
+
6043 [&](AMM& ammAlice, Env& env) {
+
6044 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
+
6045 env.close();
+
6046
+
6047 STAmount const depositEuro{EUR, UINT64_C(10'1234567890123456), -16};
+
6048 STAmount const depositGBP{GBP, UINT64_C(10'1234567890123456), -16};
+
6049
+
6050 ammAlice.deposit(DepositArg{.account = bob, .asset1In = depositEuro, .asset2In = depositGBP});
+
6051 invariant(ammAlice, env, "dep2", false);
+
6052 },
+
6053 {{GBP(30'000), EUR(30'000)}},
+
6054 0,
+ +
6056 {all});
+
6057
+
6058 // Two-asset proportional deposit (1:3 pool ratio)
+
6059 for (auto const& exponent : {1, 2, 3, 4, -3 /*fail*/, -6, -9})
+
6060 {
+
6061 testAMM(
+
6062 [&](AMM& ammAlice, Env& env) {
+
6063 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
+
6064 env.close();
6065
-
6066 // tfLPToken deposit
-
6067 testAMM(
-
6068 [&](AMM& ammAlice, Env& env) {
-
6069 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
-
6070 env.close();
-
6071
-
6072 ammAlice.deposit(DepositArg{.account = bob, .tokens = IOUAmount{10'1234567890123456, -16}});
-
6073 invariant(ammAlice, env, "dep4", false);
-
6074 },
-
6075 {{GBP(7'000), EUR(30'000)}},
-
6076 0,
- -
6078 {all});
-
6079
-
6080 // tfOneAssetLPToken deposit
-
6081 for (auto const& tokens :
-
6082 {IOUAmount{1, -3},
-
6083 IOUAmount{1, -2},
-
6084 IOUAmount{1, -1},
-
6085 IOUAmount{1},
-
6086 IOUAmount{10},
-
6087 IOUAmount{100},
-
6088 IOUAmount{1'000},
-
6089 IOUAmount{10'000}})
-
6090 {
-
6091 testAMM(
-
6092 [&](AMM& ammAlice, Env& env) {
-
6093 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(1'000'000)}, Fund::Acct);
-
6094 env.close();
-
6095
-
6096 ammAlice.deposit(DepositArg{.account = bob, .tokens = tokens, .asset1In = STAmount{EUR, 1, 6}});
-
6097 invariant(ammAlice, env, "dep5", false);
-
6098 },
-
6099 {{GBP(7'000), EUR(30'000)}},
-
6100 0,
- -
6102 {all});
-
6103 }
-
6104
-
6105 // Single deposit with EP not exceeding specified:
-
6106 // 1'000 GBP with EP not to exceed 5 (GBP/TokensOut)
-
6107 testAMM(
-
6108 [&](AMM& ammAlice, Env& env) {
-
6109 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
-
6110 env.close();
-
6111
-
6112 ammAlice.deposit(bob, GBP(1'000), std::nullopt, STAmount{GBP, 5});
-
6113 invariant(ammAlice, env, "dep6", false);
-
6114 },
-
6115 {{GBP(30'000), EUR(30'000)}},
-
6116 0,
- -
6118 {all});
-
6119 }
+
6066 STAmount const depositEuro{EUR, 1, exponent};
+
6067 STAmount const depositGBP{GBP, 1, exponent};
+
6068
+
6069 ammAlice.deposit(DepositArg{.account = bob, .asset1In = depositEuro, .asset2In = depositGBP});
+
6070 invariant(ammAlice, env, "dep3", exponent != -3 && !env.enabled(fixAMMv1_3));
+
6071 },
+
6072 {{GBP(10'000), EUR(30'000)}},
+
6073 0,
+ +
6075 {all});
+
6076 }
+
6077
+
6078 // tfLPToken deposit
+
6079 testAMM(
+
6080 [&](AMM& ammAlice, Env& env) {
+
6081 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
+
6082 env.close();
+
6083
+
6084 ammAlice.deposit(DepositArg{.account = bob, .tokens = IOUAmount{10'1234567890123456, -16}});
+
6085 invariant(ammAlice, env, "dep4", false);
+
6086 },
+
6087 {{GBP(7'000), EUR(30'000)}},
+
6088 0,
+ +
6090 {all});
+
6091
+
6092 // tfOneAssetLPToken deposit
+
6093 for (auto const& tokens :
+
6094 {IOUAmount{1, -3},
+
6095 IOUAmount{1, -2},
+
6096 IOUAmount{1, -1},
+
6097 IOUAmount{1},
+
6098 IOUAmount{10},
+
6099 IOUAmount{100},
+
6100 IOUAmount{1'000},
+
6101 IOUAmount{10'000}})
+
6102 {
+
6103 testAMM(
+
6104 [&](AMM& ammAlice, Env& env) {
+
6105 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(1'000'000)}, Fund::Acct);
+
6106 env.close();
+
6107
+
6108 ammAlice.deposit(DepositArg{.account = bob, .tokens = tokens, .asset1In = STAmount{EUR, 1, 6}});
+
6109 invariant(ammAlice, env, "dep5", false);
+
6110 },
+
6111 {{GBP(7'000), EUR(30'000)}},
+
6112 0,
+ +
6114 {all});
+
6115 }
+
6116
+
6117 // Single deposit with EP not exceeding specified:
+
6118 // 1'000 GBP with EP not to exceed 5 (GBP/TokensOut)
+
6119 testAMM(
+
6120 [&](AMM& ammAlice, Env& env) {
+
6121 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
+
6122 env.close();
+
6123
+
6124 ammAlice.deposit(bob, GBP(1'000), std::nullopt, STAmount{GBP, 5});
+
6125 invariant(ammAlice, env, "dep6", false);
+
6126 },
+
6127 {{GBP(30'000), EUR(30'000)}},
+
6128 0,
+ +
6130 {all});
+
6131 }
-
6120
-
6121 void
-
- -
6123 {
-
6124 testcase("Withdraw Rounding");
-
6125
-
6126 using namespace jtx;
-
6127
-
6128 // tfLPToken mode
-
6129 testAMM(
-
6130 [&](AMM& ammAlice, Env& env) {
-
6131 ammAlice.withdraw(alice, 1'000);
-
6132 invariant(ammAlice, env, "with1", false);
-
6133 },
-
6134 {{GBP(7'000), EUR(30'000)}},
-
6135 0,
- -
6137 {all});
-
6138
-
6139 // tfWithdrawAll mode
-
6140 testAMM(
-
6141 [&](AMM& ammAlice, Env& env) {
-
6142 ammAlice.withdraw(WithdrawArg{.account = alice, .flags = tfWithdrawAll});
-
6143 invariant(ammAlice, env, "with2", false);
-
6144 },
-
6145 {{GBP(7'000), EUR(30'000)}},
-
6146 0,
- -
6148 {all});
-
6149
-
6150 // tfTwoAsset withdraw mode
-
6151 testAMM(
-
6152 [&](AMM& ammAlice, Env& env) {
-
6153 ammAlice.withdraw(WithdrawArg{
-
6154 .account = alice,
-
6155 .asset1Out = STAmount{GBP, 3'500},
-
6156 .asset2Out = STAmount{EUR, 15'000},
-
6157 .flags = tfTwoAsset});
-
6158 invariant(ammAlice, env, "with3", false);
-
6159 },
-
6160 {{GBP(7'000), EUR(30'000)}},
-
6161 0,
- -
6163 {all});
-
6164
-
6165 // tfSingleAsset withdraw mode
-
6166 // Note: This test fails with 0 trading fees, but doesn't fail if
-
6167 // trading fees is set to 1'000 -- I suspect the compound operations
-
6168 // in AMMHelpers.cpp:withdrawByTokens compensate for the rounding
-
6169 // errors
-
6170 testAMM(
-
6171 [&](AMM& ammAlice, Env& env) {
-
6172 ammAlice.withdraw(
-
6173 WithdrawArg{.account = alice, .asset1Out = STAmount{GBP, 1'234}, .flags = tfSingleAsset});
-
6174 invariant(ammAlice, env, "with4", false);
-
6175 },
-
6176 {{GBP(7'000), EUR(30'000)}},
-
6177 0,
- -
6179 {all});
-
6180
-
6181 // tfOneAssetWithdrawAll mode
-
6182 testAMM(
-
6183 [&](AMM& ammAlice, Env& env) {
-
6184 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
-
6185 env.close();
-
6186
-
6187 ammAlice.deposit(DepositArg{.account = bob, .asset1In = STAmount{GBP, 3'456}});
-
6188
-
6189 ammAlice.withdraw(
-
6190 WithdrawArg{.account = bob, .asset1Out = STAmount{GBP, 1'000}, .flags = tfOneAssetWithdrawAll});
-
6191 invariant(ammAlice, env, "with5", false);
-
6192 },
-
6193 {{GBP(7'000), EUR(30'000)}},
-
6194 0,
- -
6196 {all});
-
6197
-
6198 // tfOneAssetLPToken mode
-
6199 testAMM(
-
6200 [&](AMM& ammAlice, Env& env) {
-
6201 ammAlice.withdraw(WithdrawArg{
-
6202 .account = alice, .tokens = 1'000, .asset1Out = STAmount{GBP, 100}, .flags = tfOneAssetLPToken});
-
6203 invariant(ammAlice, env, "with6", false);
-
6204 },
-
6205 {{GBP(7'000), EUR(30'000)}},
-
6206 0,
- -
6208 {all});
-
6209
-
6210 // tfLimitLPToken mode
-
6211 testAMM(
-
6212 [&](AMM& ammAlice, Env& env) {
-
6213 ammAlice.withdraw(WithdrawArg{
-
6214 .account = alice, .asset1Out = STAmount{GBP, 100}, .maxEP = IOUAmount{2}, .flags = tfLimitLPToken});
-
6215 invariant(ammAlice, env, "with7", true);
-
6216 },
-
6217 {{GBP(7'000), EUR(30'000)}},
-
6218 0,
- -
6220 {all});
-
6221 }
+
6132
+
6133 void
+
+ +
6135 {
+
6136 testcase("Withdraw Rounding");
+
6137
+
6138 using namespace jtx;
+
6139
+
6140 // tfLPToken mode
+
6141 testAMM(
+
6142 [&](AMM& ammAlice, Env& env) {
+
6143 ammAlice.withdraw(alice, 1'000);
+
6144 invariant(ammAlice, env, "with1", false);
+
6145 },
+
6146 {{GBP(7'000), EUR(30'000)}},
+
6147 0,
+ +
6149 {all});
+
6150
+
6151 // tfWithdrawAll mode
+
6152 testAMM(
+
6153 [&](AMM& ammAlice, Env& env) {
+
6154 ammAlice.withdraw(WithdrawArg{.account = alice, .flags = tfWithdrawAll});
+
6155 invariant(ammAlice, env, "with2", false);
+
6156 },
+
6157 {{GBP(7'000), EUR(30'000)}},
+
6158 0,
+ +
6160 {all});
+
6161
+
6162 // tfTwoAsset withdraw mode
+
6163 testAMM(
+
6164 [&](AMM& ammAlice, Env& env) {
+
6165 ammAlice.withdraw(
+ +
6167 .account = alice,
+
6168 .asset1Out = STAmount{GBP, 3'500},
+
6169 .asset2Out = STAmount{EUR, 15'000},
+
6170 .flags = tfTwoAsset});
+
6171 invariant(ammAlice, env, "with3", false);
+
6172 },
+
6173 {{GBP(7'000), EUR(30'000)}},
+
6174 0,
+ +
6176 {all});
+
6177
+
6178 // tfSingleAsset withdraw mode
+
6179 // Note: This test fails with 0 trading fees, but doesn't fail if
+
6180 // trading fees is set to 1'000 -- I suspect the compound operations
+
6181 // in AMMHelpers.cpp:withdrawByTokens compensate for the rounding
+
6182 // errors
+
6183 testAMM(
+
6184 [&](AMM& ammAlice, Env& env) {
+
6185 ammAlice.withdraw(
+
6186 WithdrawArg{.account = alice, .asset1Out = STAmount{GBP, 1'234}, .flags = tfSingleAsset});
+
6187 invariant(ammAlice, env, "with4", false);
+
6188 },
+
6189 {{GBP(7'000), EUR(30'000)}},
+
6190 0,
+ +
6192 {all});
+
6193
+
6194 // tfOneAssetWithdrawAll mode
+
6195 testAMM(
+
6196 [&](AMM& ammAlice, Env& env) {
+
6197 fund(env, gw, {bob}, XRP(10'000'000), {GBP(100'000), EUR(100'000)}, Fund::Acct);
+
6198 env.close();
+
6199
+
6200 ammAlice.deposit(DepositArg{.account = bob, .asset1In = STAmount{GBP, 3'456}});
+
6201
+
6202 ammAlice.withdraw(
+
6203 WithdrawArg{.account = bob, .asset1Out = STAmount{GBP, 1'000}, .flags = tfOneAssetWithdrawAll});
+
6204 invariant(ammAlice, env, "with5", false);
+
6205 },
+
6206 {{GBP(7'000), EUR(30'000)}},
+
6207 0,
+ +
6209 {all});
+
6210
+
6211 // tfOneAssetLPToken mode
+
6212 testAMM(
+
6213 [&](AMM& ammAlice, Env& env) {
+
6214 ammAlice.withdraw(
+ +
6216 .account = alice,
+
6217 .tokens = 1'000,
+
6218 .asset1Out = STAmount{GBP, 100},
+
6219 .flags = tfOneAssetLPToken});
+
6220 invariant(ammAlice, env, "with6", false);
+
6221 },
+
6222 {{GBP(7'000), EUR(30'000)}},
+
6223 0,
+ +
6225 {all});
+
6226
+
6227 // tfLimitLPToken mode
+
6228 testAMM(
+
6229 [&](AMM& ammAlice, Env& env) {
+
6230 ammAlice.withdraw(
+ +
6232 .account = alice,
+
6233 .asset1Out = STAmount{GBP, 100},
+
6234 .maxEP = IOUAmount{2},
+
6235 .flags = tfLimitLPToken});
+
6236 invariant(ammAlice, env, "with7", true);
+
6237 },
+
6238 {{GBP(7'000), EUR(30'000)}},
+
6239 0,
+ +
6241 {all});
+
6242 }
-
6222
-
6223 void
-
-
6224 run() override
-
6225 {
- -
6227 testInvalidInstance();
-
6228 testInstanceCreate();
-
6229 testInvalidDeposit(all);
-
6230 testInvalidDeposit(all - featureAMMClawback);
-
6231 testDeposit();
-
6232 testInvalidWithdraw();
-
6233 testWithdraw();
-
6234 testInvalidFeeVote();
-
6235 testFeeVote();
-
6236 testInvalidBid();
-
6237 testBid(all);
-
6238 testBid(all - fixAMMv1_3);
-
6239 testBid(all - fixAMMv1_1 - fixAMMv1_3);
-
6240 testInvalidAMMPayment();
-
6241 testBasicPaymentEngine(all);
-
6242 testBasicPaymentEngine(all - fixAMMv1_1 - fixAMMv1_3);
-
6243 testBasicPaymentEngine(all - fixReducedOffersV2);
-
6244 testBasicPaymentEngine(all - fixAMMv1_1 - fixAMMv1_3 - fixReducedOffersV2);
-
6245 testAMMTokens();
-
6246 testAmendment();
-
6247 testFlags();
-
6248 testRippling();
-
6249 testAMMAndCLOB(all);
-
6250 testAMMAndCLOB(all - fixAMMv1_1 - fixAMMv1_3);
-
6251 testTradingFee(all);
-
6252 testTradingFee(all - fixAMMv1_3);
-
6253 testTradingFee(all - fixAMMv1_1 - fixAMMv1_3);
-
6254 testAdjustedTokens(all);
-
6255 testAdjustedTokens(all - fixAMMv1_3);
-
6256 testAdjustedTokens(all - fixAMMv1_1 - fixAMMv1_3);
-
6257 testAutoDelete();
-
6258 testClawback();
-
6259 testAMMID();
-
6260 testSelection(all);
-
6261 testSelection(all - fixAMMv1_1 - fixAMMv1_3);
-
6262 testFixDefaultInnerObj();
-
6263 testMalformed();
-
6264 testFixOverflowOffer(all);
-
6265 testFixOverflowOffer(all - fixAMMv1_3);
-
6266 testFixOverflowOffer(all - fixAMMv1_1 - fixAMMv1_3);
-
6267 testSwapRounding();
-
6268 testFixChangeSpotPriceQuality(all);
-
6269 testFixChangeSpotPriceQuality(all - fixAMMv1_1 - fixAMMv1_3);
-
6270 testFixAMMOfferBlockedByLOB(all);
-
6271 testFixAMMOfferBlockedByLOB(all - fixAMMv1_1 - fixAMMv1_3);
-
6272 testLPTokenBalance(all);
-
6273 testLPTokenBalance(all - fixAMMv1_3);
-
6274 testLPTokenBalance(all - fixAMMv1_1 - fixAMMv1_3);
-
6275 testAMMClawback(all);
-
6276 testAMMClawback(all - featureSingleAssetVault);
-
6277 testAMMClawback(all - featureAMMClawback - featureSingleAssetVault);
-
6278 testAMMClawback(all - featureAMMClawback);
-
6279 testAMMClawback(all - fixAMMv1_1 - fixAMMv1_3 - featureAMMClawback);
-
6280 testAMMDepositWithFrozenAssets(all);
-
6281 testAMMDepositWithFrozenAssets(all - featureAMMClawback);
-
6282 testAMMDepositWithFrozenAssets(all - fixAMMv1_1 - featureAMMClawback);
-
6283 testAMMDepositWithFrozenAssets(all - fixAMMv1_1 - fixAMMv1_3 - featureAMMClawback);
-
6284 testFixReserveCheckOnWithdrawal(all);
-
6285 testFixReserveCheckOnWithdrawal(all - fixAMMv1_2);
-
6286 testDepositAndWithdrawRounding(all);
-
6287 testDepositAndWithdrawRounding(all - fixAMMv1_3);
-
6288 testDepositRounding(all);
-
6289 testDepositRounding(all - fixAMMv1_3);
-
6290 testWithdrawRounding(all);
-
6291 testWithdrawRounding(all - fixAMMv1_3);
-
6292 testFailedPseudoAccount();
-
6293 }
+
6243
+
6244 void
+
+
6245 run() override
+
6246 {
+ +
6248 testInvalidInstance();
+
6249 testInstanceCreate();
+
6250 testInvalidDeposit(all);
+
6251 testInvalidDeposit(all - featureAMMClawback);
+
6252 testDeposit();
+
6253 testInvalidWithdraw();
+
6254 testWithdraw();
+
6255 testInvalidFeeVote();
+
6256 testFeeVote();
+
6257 testInvalidBid();
+
6258 testBid(all);
+
6259 testBid(all - fixAMMv1_3);
+
6260 testBid(all - fixAMMv1_1 - fixAMMv1_3);
+
6261 testInvalidAMMPayment();
+
6262 testBasicPaymentEngine(all);
+
6263 testBasicPaymentEngine(all - fixAMMv1_1 - fixAMMv1_3);
+
6264 testBasicPaymentEngine(all - fixReducedOffersV2);
+
6265 testBasicPaymentEngine(all - fixAMMv1_1 - fixAMMv1_3 - fixReducedOffersV2);
+
6266 testAMMTokens();
+
6267 testAmendment();
+
6268 testFlags();
+
6269 testRippling();
+
6270 testAMMAndCLOB(all);
+
6271 testAMMAndCLOB(all - fixAMMv1_1 - fixAMMv1_3);
+
6272 testTradingFee(all);
+
6273 testTradingFee(all - fixAMMv1_3);
+
6274 testTradingFee(all - fixAMMv1_1 - fixAMMv1_3);
+
6275 testAdjustedTokens(all);
+
6276 testAdjustedTokens(all - fixAMMv1_3);
+
6277 testAdjustedTokens(all - fixAMMv1_1 - fixAMMv1_3);
+
6278 testAutoDelete();
+
6279 testClawback();
+
6280 testAMMID();
+
6281 testSelection(all);
+
6282 testSelection(all - fixAMMv1_1 - fixAMMv1_3);
+
6283 testFixDefaultInnerObj();
+
6284 testMalformed();
+
6285 testFixOverflowOffer(all);
+
6286 testFixOverflowOffer(all - fixAMMv1_3);
+
6287 testFixOverflowOffer(all - fixAMMv1_1 - fixAMMv1_3);
+
6288 testSwapRounding();
+
6289 testFixChangeSpotPriceQuality(all);
+
6290 testFixChangeSpotPriceQuality(all - fixAMMv1_1 - fixAMMv1_3);
+
6291 testFixAMMOfferBlockedByLOB(all);
+
6292 testFixAMMOfferBlockedByLOB(all - fixAMMv1_1 - fixAMMv1_3);
+
6293 testLPTokenBalance(all);
+
6294 testLPTokenBalance(all - fixAMMv1_3);
+
6295 testLPTokenBalance(all - fixAMMv1_1 - fixAMMv1_3);
+
6296 testAMMClawback(all);
+
6297 testAMMClawback(all - featureSingleAssetVault);
+
6298 testAMMClawback(all - featureAMMClawback - featureSingleAssetVault);
+
6299 testAMMClawback(all - featureAMMClawback);
+
6300 testAMMClawback(all - fixAMMv1_1 - fixAMMv1_3 - featureAMMClawback);
+
6301 testAMMDepositWithFrozenAssets(all);
+
6302 testAMMDepositWithFrozenAssets(all - featureAMMClawback);
+
6303 testAMMDepositWithFrozenAssets(all - fixAMMv1_1 - featureAMMClawback);
+
6304 testAMMDepositWithFrozenAssets(all - fixAMMv1_1 - fixAMMv1_3 - featureAMMClawback);
+
6305 testFixReserveCheckOnWithdrawal(all);
+
6306 testFixReserveCheckOnWithdrawal(all - fixAMMv1_2);
+
6307 testDepositAndWithdrawRounding(all);
+
6308 testDepositAndWithdrawRounding(all - fixAMMv1_3);
+
6309 testDepositRounding(all);
+
6310 testDepositRounding(all - fixAMMv1_3);
+
6311 testWithdrawRounding(all);
+
6312 testWithdrawRounding(all - fixAMMv1_3);
+
6313 testFailedPseudoAccount();
+
6314 }
-
6294};
+
6315};
-
6295
-
6296BEAST_DEFINE_TESTSUITE_PRIO(AMM, app, xrpl, 1);
-
6297
-
6298} // namespace test
-
6299} // namespace xrpl
+
6316
+
6317BEAST_DEFINE_TESTSUITE_PRIO(AMM, app, xrpl, 1);
+
6318
+
6319} // namespace test
+
6320} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
@@ -6555,7 +6576,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
XRPAmount txfee(Env const &env, std::uint16_t n)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value accountBalance(Env &env, Account const &acct)
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
@@ -6604,7 +6625,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfNoRippleDirect
Definition TxFlags.h:87
-
Number root2(Number f)
Definition Number.cpp:1010
+
Number root2(Number f)
Definition Number.cpp:1011
constexpr std::uint32_t tfSetfAuth
Definition TxFlags.h:95
std::optional< TAmounts< TIn, TOut > > changeSpotPriceQuality(TAmounts< TIn, TOut > const &pool, Quality const &quality, std::uint16_t tfee, Rules const &rules, beast::Journal j)
Generate AMM offer so that either updated Spot Price Quality (SPQ) is equal to LOB quality (in this c...
Definition AMMHelpers.h:281
constexpr std::uint32_t asfRequireAuth
Definition TxFlags.h:58
@@ -6654,47 +6675,47 @@ $(document).ready(function() { init_codefold(0); });
uint256 key
Definition Keylet.h:20
Basic tests of AMM that do not use offers.
Definition AMM_test.cpp:32
- -
void testAMMAndCLOB(FeatureBitset features)
- - -
void run() override
Runs the suite.
-
void testTradingFee(FeatureBitset features)
- -
void testWithdrawRounding(FeatureBitset all)
- + +
void testAMMAndCLOB(FeatureBitset features)
+ + +
void run() override
Runs the suite.
+
void testTradingFee(FeatureBitset features)
+ +
void testWithdrawRounding(FeatureBitset all)
+
static FeatureBitset testable_amendments()
Definition AMM_test.cpp:38
- - - -
void testFixAMMOfferBlockedByLOB(FeatureBitset features)
-
void testSelection(FeatureBitset features)
+ + + +
void testFixAMMOfferBlockedByLOB(FeatureBitset features)
+
void testSelection(FeatureBitset features)
void testInvalidDeposit(FeatureBitset features)
Definition AMM_test.cpp:396
- -
void invariant(jtx::AMM &amm, jtx::Env &env, std::string const &msg, bool shouldFail)
-
void testBid(FeatureBitset features)
+ +
void invariant(jtx::AMM &amm, jtx::Env &env, std::string const &msg, bool shouldFail)
+
void testBid(FeatureBitset features)
- -
void testFixChangeSpotPriceQuality(FeatureBitset features)
- -
void testDepositRounding(FeatureBitset all)
- + +
void testFixChangeSpotPriceQuality(FeatureBitset features)
+ +
void testDepositRounding(FeatureBitset all)
+ -
void testFixOverflowOffer(FeatureBitset featuresInitial)
- -
void testAMMClawback(FeatureBitset features)
- -
void testLPTokenBalance(FeatureBitset features)
-
void testAMMDepositWithFrozenAssets(FeatureBitset features)
-
void testAdjustedTokens(FeatureBitset features)
- -
void testBasicPaymentEngine(FeatureBitset features)
-
void testFixReserveCheckOnWithdrawal(FeatureBitset features)
-
void testDepositAndWithdrawRounding(FeatureBitset features)
+
void testFixOverflowOffer(FeatureBitset featuresInitial)
+ +
void testAMMClawback(FeatureBitset features)
+ +
void testLPTokenBalance(FeatureBitset features)
+
void testAMMDepositWithFrozenAssets(FeatureBitset features)
+
void testAdjustedTokens(FeatureBitset features)
+ +
void testBasicPaymentEngine(FeatureBitset features)
+
void testFixReserveCheckOnWithdrawal(FeatureBitset features)
+
void testDepositAndWithdrawRounding(FeatureBitset features)
NumberMantissaScaleGuard const sg_
Definition AMM_test.cpp:34
- - - + + +
std::uint16_t tfee
Definition AMM.h:45
diff --git a/AcceptedLedgerTx_8cpp_source.html b/AcceptedLedgerTx_8cpp_source.html index ed8b4be5ed..ec33423208 100644 --- a/AcceptedLedgerTx_8cpp_source.html +++ b/AcceptedLedgerTx_8cpp_source.html @@ -146,7 +146,7 @@ $(document).ready(function() { init_codefold(0); });
59} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A generic endpoint for log messages.
Definition Journal.h:40
static Sink & getNullSink()
Returns a Sink which does nothing.
boost::container::flat_set< AccountID > mAffected
diff --git a/AccountChannels_8cpp_source.html b/AccountChannels_8cpp_source.html index e382ceab69..534eab0805 100644 --- a/AccountChannels_8cpp_source.html +++ b/AccountChannels_8cpp_source.html @@ -281,7 +281,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A public key.
Definition PublicKey.h:42
diff --git a/AccountCurrenciesHandler_8cpp_source.html b/AccountCurrenciesHandler_8cpp_source.html index f7cb5817fb..d6a0cd7a96 100644 --- a/AccountCurrenciesHandler_8cpp_source.html +++ b/AccountCurrenciesHandler_8cpp_source.html @@ -162,7 +162,7 @@ $(document).ready(function() { init_codefold(0); });
77} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
static std::vector< RPCTrustLine > getItems(AccountID const &accountID, ReadView const &view)
Definition TrustLine.cpp:81
Currency const & getCurrency() const
Definition STAmount.h:460
diff --git a/AccountCurrencies__test_8cpp_source.html b/AccountCurrencies__test_8cpp_source.html index c595caeb11..ed613f2d3c 100644 --- a/AccountCurrencies__test_8cpp_source.html +++ b/AccountCurrencies__test_8cpp_source.html @@ -277,7 +277,7 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
diff --git a/AccountDelete__test_8cpp_source.html b/AccountDelete__test_8cpp_source.html index b20c25bc01..1f87f5005d 100644 --- a/AccountDelete__test_8cpp_source.html +++ b/AccountDelete__test_8cpp_source.html @@ -1138,7 +1138,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -1206,7 +1206,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
void incLgrSeqForAccDel(jtx::Env &env, jtx::Account const &acc, std::uint32_t margin=0)
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:23
diff --git a/AccountInfo_8cpp_source.html b/AccountInfo_8cpp_source.html index 152db904ea..4186b8e82a 100644 --- a/AccountInfo_8cpp_source.html +++ b/AccountInfo_8cpp_source.html @@ -387,7 +387,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Json::Value getJson(JsonOptions options=JsonOptions::none) const override
LedgerEntryType getType() const
diff --git a/AccountInfo__test_8cpp_source.html b/AccountInfo__test_8cpp_source.html index 8b850e913d..7b60880671 100644 --- a/AccountInfo__test_8cpp_source.html +++ b/AccountInfo__test_8cpp_source.html @@ -715,8 +715,8 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
UInt size() const
Number of values in array or object.
-
bool isObject() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isObject() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -740,7 +740,7 @@ $(document).ready(function() { init_codefold(0); });
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
auto const data
General field definitions, or fields used in multiple transaction namespaces.
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
diff --git a/AccountLines_8cpp_source.html b/AccountLines_8cpp_source.html index b3d2caf4df..02cfb50fe9 100644 --- a/AccountLines_8cpp_source.html +++ b/AccountLines_8cpp_source.html @@ -320,7 +320,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Currency currency
Definition Issue.h:15
static std::optional< RPCTrustLine > makeItem(AccountID const &accountID, std::shared_ptr< SLE const > const &sle)
Definition TrustLine.cpp:73
diff --git a/AccountLines__test_8cpp_source.html b/AccountLines__test_8cpp_source.html index 104a246ca9..9508d6c39e 100644 --- a/AccountLines__test_8cpp_source.html +++ b/AccountLines__test_8cpp_source.html @@ -1310,7 +1310,7 @@ $(document).ready(function() { init_codefold(0); });
T back(T... args)
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:516
diff --git a/AccountObjects_8cpp_source.html b/AccountObjects_8cpp_source.html index 3ea688d348..cb9d39ceaf 100644 --- a/AccountObjects_8cpp_source.html +++ b/AccountObjects_8cpp_source.html @@ -535,7 +535,7 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A view into a ledger.
Definition ReadView.h:31
virtual std::optional< key_type > succ(key_type const &key, std::optional< key_type > const &last=std::nullopt) const =0
Return the key of the next state item.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
@@ -577,7 +577,7 @@ $(document).ready(function() { init_codefold(0); });
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
Json::Value doAccountNFTs(RPC::JsonContext &context)
General RPC command that can retrieve objects in the account root.
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
bool getAccountObjects(ReadView const &ledger, AccountID const &account, std::optional< std::vector< LedgerEntryType > > const &typeFilter, uint256 dirIndex, uint256 entryIndex, std::uint32_t const limit, Json::Value &jvResult)
Gathers all objects for an account in a ledger.
Json::Value doAccountObjects(RPC::JsonContext &context)
Json::Value rpcError(error_code_i iError)
Definition RPCErr.cpp:12
diff --git a/AccountObjects__test_8cpp_source.html b/AccountObjects__test_8cpp_source.html index 22c7db9071..d4a6a1db4a 100644 --- a/AccountObjects__test_8cpp_source.html +++ b/AccountObjects__test_8cpp_source.html @@ -862,581 +862,582 @@ $(document).ready(function() { init_codefold(0); });
769 // xchain_create_account_claim_id should be present on the door
770 // account (Account::master) to collect the signatures until a
771 // quorum is reached
- -
773 x.scAttester, x.jvb, x.mcCarol, amt, x.reward, x.payees[0], true, 1, x.scuAlice, x.signers[0]));
-
774 scEnv.close();
-
775
-
776 auto scEnvAcctObjs = [&](Account const& acct, char const* type) {
-
777 Json::Value params;
-
778 params[jss::account] = acct.human();
-
779 params[jss::type] = type;
-
780 params[jss::ledger_index] = "validated";
-
781 return scEnv.rpc("json", "account_objects", to_string(params));
-
782 };
-
783
-
784 {
-
785 // Find the xchain_create_account_claim_id
-
786 Json::Value const resp = scEnvAcctObjs(Account::master, jss::xchain_owned_create_account_claim_id);
-
787 BEAST_EXPECT(acctObjsIsSize(resp, 1));
-
788
-
789 auto const& xchain_create_account_claim_id = resp[jss::result][jss::account_objects][0u];
-
790 BEAST_EXPECT(xchain_create_account_claim_id[sfAccount.jsonName] == Account::master.human());
-
791 BEAST_EXPECT(xchain_create_account_claim_id[sfXChainAccountCreateCount.getJsonName()].asUInt() == 1);
-
792 }
-
793 }
-
794
-
795 // gw creates an offer that we can look for in the ledger.
-
796 env(offer(gw, USD(7), XRP(14)));
-
797 env.close();
-
798 {
-
799 // Find the offer.
-
800 Json::Value const resp = acctObjs(gw, jss::offer);
-
801 BEAST_EXPECT(acctObjsIsSize(resp, 1));
-
802
-
803 auto const& offer = resp[jss::result][jss::account_objects][0u];
-
804 BEAST_EXPECT(offer[sfAccount.jsonName] == gw.human());
-
805 BEAST_EXPECT(offer[sfTakerGets.jsonName].asUInt() == 14'000'000);
-
806 BEAST_EXPECT(offer[sfTakerPays.jsonName][jss::value].asUInt() == 7);
-
807 }
-
808 {
-
809 // Create a payment channel from qw to alice that we can look
-
810 // for.
-
811 Json::Value jvPayChan;
-
812 jvPayChan[jss::TransactionType] = jss::PaymentChannelCreate;
-
813 jvPayChan[jss::Account] = gw.human();
-
814 jvPayChan[jss::Destination] = alice.human();
-
815 jvPayChan[jss::Amount] = XRP(300).value().getJson(JsonOptions::none);
-
816 jvPayChan[sfSettleDelay.jsonName] = 24 * 60 * 60;
-
817 jvPayChan[sfPublicKey.jsonName] = strHex(gw.pk().slice());
-
818 env(jvPayChan);
-
819 env.close();
-
820 }
-
821 {
-
822 // Find the payment channel.
-
823 Json::Value const resp = acctObjs(gw, jss::payment_channel);
-
824 BEAST_EXPECT(acctObjsIsSize(resp, 1));
-
825
-
826 auto const& payChan = resp[jss::result][jss::account_objects][0u];
-
827 BEAST_EXPECT(payChan[sfAccount.jsonName] == gw.human());
-
828 BEAST_EXPECT(payChan[sfAmount.jsonName].asUInt() == 300'000'000);
-
829 BEAST_EXPECT(payChan[sfSettleDelay.jsonName].asUInt() == 24 * 60 * 60);
-
830 }
-
831
-
832 {
-
833 // gw creates a DID that we can look for in the ledger.
-
834 Json::Value jvDID;
-
835 jvDID[jss::TransactionType] = jss::DIDSet;
-
836 jvDID[jss::Account] = gw.human();
-
837 jvDID[sfURI.jsonName] = strHex(std::string{"uri"});
-
838 env(jvDID);
-
839 env.close();
-
840 }
-
841 {
-
842 // Find the DID.
-
843 Json::Value const resp = acctObjs(gw, jss::did);
-
844 BEAST_EXPECT(acctObjsIsSize(resp, 1));
-
845
-
846 auto const& did = resp[jss::result][jss::account_objects][0u];
-
847 BEAST_EXPECT(did[sfAccount.jsonName] == gw.human());
-
848 BEAST_EXPECT(did[sfURI.jsonName] == strHex(std::string{"uri"}));
-
849 }
-
850 // Make gw multisigning by adding a signerList.
-
851 env(jtx::signers(gw, 6, {{alice, 7}}));
-
852 env.close();
-
853 {
-
854 // Find the signer list.
-
855 Json::Value const resp = acctObjs(gw, jss::signer_list);
-
856 BEAST_EXPECT(acctObjsIsSize(resp, 1));
-
857
-
858 auto const& signerList = resp[jss::result][jss::account_objects][0u];
-
859 BEAST_EXPECT(signerList[sfSignerQuorum.jsonName] == 6);
-
860 auto const& entry = signerList[sfSignerEntries.jsonName][0u][sfSignerEntry.jsonName];
-
861 BEAST_EXPECT(entry[sfAccount.jsonName] == alice.human());
-
862 BEAST_EXPECT(entry[sfSignerWeight.jsonName].asUInt() == 7);
-
863 }
-
864
-
865 {
-
866 auto const seq = env.seq(gw);
-
867 // Create a Ticket for gw.
-
868 env(ticket::create(gw, 1));
-
869 env.close();
-
870
-
871 // Find the ticket.
-
872 Json::Value const resp = acctObjs(gw, jss::ticket);
-
873 BEAST_EXPECT(acctObjsIsSize(resp, 1));
-
874
-
875 auto const& ticket = resp[jss::result][jss::account_objects][0u];
-
876 BEAST_EXPECT(ticket[sfAccount.jsonName] == gw.human());
-
877 BEAST_EXPECT(ticket[sfLedgerEntryType.jsonName] == jss::Ticket);
-
878 BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == seq + 1);
-
879 }
-
880
-
881 {
-
882 // See how "deletion_blockers_only" handles gw's directory.
-
883 Json::Value params;
-
884 params[jss::account] = gw.human();
-
885 params[jss::deletion_blockers_only] = true;
-
886 auto resp = env.rpc("json", "account_objects", to_string(params));
-
887
-
888 std::vector<std::string> const expectedLedgerTypes = [] {
- -
890 jss::Escrow.c_str(),
-
891 jss::Check.c_str(),
-
892 jss::NFTokenPage.c_str(),
-
893 jss::RippleState.c_str(),
-
894 jss::PayChannel.c_str(),
-
895 jss::PermissionedDomain.c_str()};
-
896 std::sort(v.begin(), v.end());
-
897 return v;
-
898 }();
-
899
-
900 std::uint32_t const expectedAccountObjects{static_cast<std::uint32_t>(std::size(expectedLedgerTypes))};
-
901
-
902 if (BEAST_EXPECT(acctObjsIsSize(resp, expectedAccountObjects)))
-
903 {
-
904 auto const& aobjs = resp[jss::result][jss::account_objects];
-
905 std::vector<std::string> gotLedgerTypes;
-
906 gotLedgerTypes.reserve(expectedAccountObjects);
-
907 for (std::uint32_t i = 0; i < expectedAccountObjects; ++i)
-
908 {
-
909 gotLedgerTypes.push_back(aobjs[i]["LedgerEntryType"].asString());
-
910 }
-
911 std::sort(gotLedgerTypes.begin(), gotLedgerTypes.end());
-
912 BEAST_EXPECT(gotLedgerTypes == expectedLedgerTypes);
-
913 }
-
914 }
-
915 {
-
916 // See how "deletion_blockers_only" with `type` handles gw's
-
917 // directory.
-
918 Json::Value params;
-
919 params[jss::account] = gw.human();
-
920 params[jss::deletion_blockers_only] = true;
-
921 params[jss::type] = jss::escrow;
-
922 auto resp = env.rpc("json", "account_objects", to_string(params));
-
923
-
924 if (BEAST_EXPECT(acctObjsIsSize(resp, 1u)))
-
925 {
-
926 auto const& aobjs = resp[jss::result][jss::account_objects];
-
927 BEAST_EXPECT(aobjs[0u]["LedgerEntryType"] == jss::Escrow);
-
928 }
-
929 }
-
930 {
-
931 // Make a lambda to get the types
-
932 auto getTypes = [&](Json::Value const& resp, std::vector<std::string>& typesOut) {
-
933 auto const objs = resp[jss::result][jss::account_objects];
-
934 for (auto const& obj : resp[jss::result][jss::account_objects])
-
935 typesOut.push_back(obj[sfLedgerEntryType.fieldName].asString());
-
936 std::sort(typesOut.begin(), typesOut.end());
-
937 };
-
938 // Make a lambda we can use to check the number of fetched
-
939 // account objects and their ledger type
-
940 auto expectObjects = [&](Json::Value const& resp, std::vector<std::string> const& types) -> bool {
-
941 if (!acctObjsIsSize(resp, types.size()))
-
942 return false;
- -
944 getTypes(resp, typesOut);
-
945 return types == typesOut;
-
946 };
-
947 // Find AMM objects
-
948 AMM amm(env, gw, XRP(1'000), USD(1'000));
-
949 amm.deposit(alice, USD(1));
-
950 // AMM account has 4 objects: AMM object and 3 trustlines
-
951 auto const lines = getAccountLines(env, amm.ammAccount());
-
952 BEAST_EXPECT(lines[jss::lines].size() == 3);
-
953 // request AMM only, doesn't depend on the limit
-
954 BEAST_EXPECT(acctObjsIsSize(acctObjs(amm.ammAccount(), jss::amm), 1));
-
955 // request first two objects
-
956 auto resp = acctObjs(amm.ammAccount(), std::nullopt, 2);
- -
958 getTypes(resp, typesOut);
-
959 // request next two objects
-
960 resp = acctObjs(amm.ammAccount(), std::nullopt, 10, resp[jss::result][jss::marker].asString());
-
961 getTypes(resp, typesOut);
-
962 BEAST_EXPECT(
-
963 (typesOut ==
- -
965 jss::AMM.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str()}));
-
966 // filter by state: there are three trustlines
-
967 resp = acctObjs(amm.ammAccount(), jss::state, 10);
-
968 BEAST_EXPECT(
-
969 expectObjects(resp, {jss::RippleState.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str()}));
-
970 // AMM account doesn't own offers
-
971 BEAST_EXPECT(acctObjsIsSize(acctObjs(amm.ammAccount(), jss::offer), 0));
-
972 // gw account doesn't own AMM object
-
973 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::amm), 0));
-
974 }
-
975
-
976 // we still expect invalid field type reported for the following types
-
977 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::amendments)));
-
978 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::directory)));
-
979 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::fee)));
-
980 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::hashes)));
-
981 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::NegativeUNL)));
-
982
-
983 // Run up the number of directory entries so gw has two
-
984 // directory nodes.
-
985 for (int d = 1'000'032; d >= 1'000'000; --d)
-
986 {
-
987 env(offer(gw, USD(1), drops(d)));
-
988 env.close();
-
989 }
-
990
-
991 // Verify that the non-returning types still don't return anything.
-
992 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::account), 0));
-
993 }
+
772 scEnv(
+ +
774 x.scAttester, x.jvb, x.mcCarol, amt, x.reward, x.payees[0], true, 1, x.scuAlice, x.signers[0]));
+
775 scEnv.close();
+
776
+
777 auto scEnvAcctObjs = [&](Account const& acct, char const* type) {
+
778 Json::Value params;
+
779 params[jss::account] = acct.human();
+
780 params[jss::type] = type;
+
781 params[jss::ledger_index] = "validated";
+
782 return scEnv.rpc("json", "account_objects", to_string(params));
+
783 };
+
784
+
785 {
+
786 // Find the xchain_create_account_claim_id
+
787 Json::Value const resp = scEnvAcctObjs(Account::master, jss::xchain_owned_create_account_claim_id);
+
788 BEAST_EXPECT(acctObjsIsSize(resp, 1));
+
789
+
790 auto const& xchain_create_account_claim_id = resp[jss::result][jss::account_objects][0u];
+
791 BEAST_EXPECT(xchain_create_account_claim_id[sfAccount.jsonName] == Account::master.human());
+
792 BEAST_EXPECT(xchain_create_account_claim_id[sfXChainAccountCreateCount.getJsonName()].asUInt() == 1);
+
793 }
+
794 }
+
795
+
796 // gw creates an offer that we can look for in the ledger.
+
797 env(offer(gw, USD(7), XRP(14)));
+
798 env.close();
+
799 {
+
800 // Find the offer.
+
801 Json::Value const resp = acctObjs(gw, jss::offer);
+
802 BEAST_EXPECT(acctObjsIsSize(resp, 1));
+
803
+
804 auto const& offer = resp[jss::result][jss::account_objects][0u];
+
805 BEAST_EXPECT(offer[sfAccount.jsonName] == gw.human());
+
806 BEAST_EXPECT(offer[sfTakerGets.jsonName].asUInt() == 14'000'000);
+
807 BEAST_EXPECT(offer[sfTakerPays.jsonName][jss::value].asUInt() == 7);
+
808 }
+
809 {
+
810 // Create a payment channel from qw to alice that we can look
+
811 // for.
+
812 Json::Value jvPayChan;
+
813 jvPayChan[jss::TransactionType] = jss::PaymentChannelCreate;
+
814 jvPayChan[jss::Account] = gw.human();
+
815 jvPayChan[jss::Destination] = alice.human();
+
816 jvPayChan[jss::Amount] = XRP(300).value().getJson(JsonOptions::none);
+
817 jvPayChan[sfSettleDelay.jsonName] = 24 * 60 * 60;
+
818 jvPayChan[sfPublicKey.jsonName] = strHex(gw.pk().slice());
+
819 env(jvPayChan);
+
820 env.close();
+
821 }
+
822 {
+
823 // Find the payment channel.
+
824 Json::Value const resp = acctObjs(gw, jss::payment_channel);
+
825 BEAST_EXPECT(acctObjsIsSize(resp, 1));
+
826
+
827 auto const& payChan = resp[jss::result][jss::account_objects][0u];
+
828 BEAST_EXPECT(payChan[sfAccount.jsonName] == gw.human());
+
829 BEAST_EXPECT(payChan[sfAmount.jsonName].asUInt() == 300'000'000);
+
830 BEAST_EXPECT(payChan[sfSettleDelay.jsonName].asUInt() == 24 * 60 * 60);
+
831 }
+
832
+
833 {
+
834 // gw creates a DID that we can look for in the ledger.
+
835 Json::Value jvDID;
+
836 jvDID[jss::TransactionType] = jss::DIDSet;
+
837 jvDID[jss::Account] = gw.human();
+
838 jvDID[sfURI.jsonName] = strHex(std::string{"uri"});
+
839 env(jvDID);
+
840 env.close();
+
841 }
+
842 {
+
843 // Find the DID.
+
844 Json::Value const resp = acctObjs(gw, jss::did);
+
845 BEAST_EXPECT(acctObjsIsSize(resp, 1));
+
846
+
847 auto const& did = resp[jss::result][jss::account_objects][0u];
+
848 BEAST_EXPECT(did[sfAccount.jsonName] == gw.human());
+
849 BEAST_EXPECT(did[sfURI.jsonName] == strHex(std::string{"uri"}));
+
850 }
+
851 // Make gw multisigning by adding a signerList.
+
852 env(jtx::signers(gw, 6, {{alice, 7}}));
+
853 env.close();
+
854 {
+
855 // Find the signer list.
+
856 Json::Value const resp = acctObjs(gw, jss::signer_list);
+
857 BEAST_EXPECT(acctObjsIsSize(resp, 1));
+
858
+
859 auto const& signerList = resp[jss::result][jss::account_objects][0u];
+
860 BEAST_EXPECT(signerList[sfSignerQuorum.jsonName] == 6);
+
861 auto const& entry = signerList[sfSignerEntries.jsonName][0u][sfSignerEntry.jsonName];
+
862 BEAST_EXPECT(entry[sfAccount.jsonName] == alice.human());
+
863 BEAST_EXPECT(entry[sfSignerWeight.jsonName].asUInt() == 7);
+
864 }
+
865
+
866 {
+
867 auto const seq = env.seq(gw);
+
868 // Create a Ticket for gw.
+
869 env(ticket::create(gw, 1));
+
870 env.close();
+
871
+
872 // Find the ticket.
+
873 Json::Value const resp = acctObjs(gw, jss::ticket);
+
874 BEAST_EXPECT(acctObjsIsSize(resp, 1));
+
875
+
876 auto const& ticket = resp[jss::result][jss::account_objects][0u];
+
877 BEAST_EXPECT(ticket[sfAccount.jsonName] == gw.human());
+
878 BEAST_EXPECT(ticket[sfLedgerEntryType.jsonName] == jss::Ticket);
+
879 BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == seq + 1);
+
880 }
+
881
+
882 {
+
883 // See how "deletion_blockers_only" handles gw's directory.
+
884 Json::Value params;
+
885 params[jss::account] = gw.human();
+
886 params[jss::deletion_blockers_only] = true;
+
887 auto resp = env.rpc("json", "account_objects", to_string(params));
+
888
+
889 std::vector<std::string> const expectedLedgerTypes = [] {
+ +
891 jss::Escrow.c_str(),
+
892 jss::Check.c_str(),
+
893 jss::NFTokenPage.c_str(),
+
894 jss::RippleState.c_str(),
+
895 jss::PayChannel.c_str(),
+
896 jss::PermissionedDomain.c_str()};
+
897 std::sort(v.begin(), v.end());
+
898 return v;
+
899 }();
+
900
+
901 std::uint32_t const expectedAccountObjects{static_cast<std::uint32_t>(std::size(expectedLedgerTypes))};
+
902
+
903 if (BEAST_EXPECT(acctObjsIsSize(resp, expectedAccountObjects)))
+
904 {
+
905 auto const& aobjs = resp[jss::result][jss::account_objects];
+
906 std::vector<std::string> gotLedgerTypes;
+
907 gotLedgerTypes.reserve(expectedAccountObjects);
+
908 for (std::uint32_t i = 0; i < expectedAccountObjects; ++i)
+
909 {
+
910 gotLedgerTypes.push_back(aobjs[i]["LedgerEntryType"].asString());
+
911 }
+
912 std::sort(gotLedgerTypes.begin(), gotLedgerTypes.end());
+
913 BEAST_EXPECT(gotLedgerTypes == expectedLedgerTypes);
+
914 }
+
915 }
+
916 {
+
917 // See how "deletion_blockers_only" with `type` handles gw's
+
918 // directory.
+
919 Json::Value params;
+
920 params[jss::account] = gw.human();
+
921 params[jss::deletion_blockers_only] = true;
+
922 params[jss::type] = jss::escrow;
+
923 auto resp = env.rpc("json", "account_objects", to_string(params));
+
924
+
925 if (BEAST_EXPECT(acctObjsIsSize(resp, 1u)))
+
926 {
+
927 auto const& aobjs = resp[jss::result][jss::account_objects];
+
928 BEAST_EXPECT(aobjs[0u]["LedgerEntryType"] == jss::Escrow);
+
929 }
+
930 }
+
931 {
+
932 // Make a lambda to get the types
+
933 auto getTypes = [&](Json::Value const& resp, std::vector<std::string>& typesOut) {
+
934 auto const objs = resp[jss::result][jss::account_objects];
+
935 for (auto const& obj : resp[jss::result][jss::account_objects])
+
936 typesOut.push_back(obj[sfLedgerEntryType.fieldName].asString());
+
937 std::sort(typesOut.begin(), typesOut.end());
+
938 };
+
939 // Make a lambda we can use to check the number of fetched
+
940 // account objects and their ledger type
+
941 auto expectObjects = [&](Json::Value const& resp, std::vector<std::string> const& types) -> bool {
+
942 if (!acctObjsIsSize(resp, types.size()))
+
943 return false;
+ +
945 getTypes(resp, typesOut);
+
946 return types == typesOut;
+
947 };
+
948 // Find AMM objects
+
949 AMM amm(env, gw, XRP(1'000), USD(1'000));
+
950 amm.deposit(alice, USD(1));
+
951 // AMM account has 4 objects: AMM object and 3 trustlines
+
952 auto const lines = getAccountLines(env, amm.ammAccount());
+
953 BEAST_EXPECT(lines[jss::lines].size() == 3);
+
954 // request AMM only, doesn't depend on the limit
+
955 BEAST_EXPECT(acctObjsIsSize(acctObjs(amm.ammAccount(), jss::amm), 1));
+
956 // request first two objects
+
957 auto resp = acctObjs(amm.ammAccount(), std::nullopt, 2);
+ +
959 getTypes(resp, typesOut);
+
960 // request next two objects
+
961 resp = acctObjs(amm.ammAccount(), std::nullopt, 10, resp[jss::result][jss::marker].asString());
+
962 getTypes(resp, typesOut);
+
963 BEAST_EXPECT(
+
964 (typesOut ==
+ +
966 jss::AMM.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str()}));
+
967 // filter by state: there are three trustlines
+
968 resp = acctObjs(amm.ammAccount(), jss::state, 10);
+
969 BEAST_EXPECT(
+
970 expectObjects(resp, {jss::RippleState.c_str(), jss::RippleState.c_str(), jss::RippleState.c_str()}));
+
971 // AMM account doesn't own offers
+
972 BEAST_EXPECT(acctObjsIsSize(acctObjs(amm.ammAccount(), jss::offer), 0));
+
973 // gw account doesn't own AMM object
+
974 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::amm), 0));
+
975 }
+
976
+
977 // we still expect invalid field type reported for the following types
+
978 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::amendments)));
+
979 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::directory)));
+
980 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::fee)));
+
981 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::hashes)));
+
982 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::NegativeUNL)));
+
983
+
984 // Run up the number of directory entries so gw has two
+
985 // directory nodes.
+
986 for (int d = 1'000'032; d >= 1'000'000; --d)
+
987 {
+
988 env(offer(gw, USD(1), drops(d)));
+
989 env.close();
+
990 }
+
991
+
992 // Verify that the non-returning types still don't return anything.
+
993 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::account), 0));
+
994 }
-
994
-
995 void
-
- -
997 {
-
998 // there's some bug found in account_nfts method that it did not
-
999 // return invalid params when providing unassociated nft marker.
-
1000 // this test tests both situations when providing valid nft marker
-
1001 // and unassociated nft marker.
-
1002 testcase("NFTsMarker");
-
1003
-
1004 using namespace jtx;
-
1005 Env env(*this);
-
1006
-
1007 Account const bob{"bob"};
-
1008 env.fund(XRP(10000), bob);
-
1009
-
1010 static constexpr unsigned nftsSize = 10;
-
1011 for (unsigned i = 0; i < nftsSize; i++)
-
1012 {
-
1013 env(token::mint(bob, 0));
-
1014 }
-
1015
-
1016 env.close();
-
1017
-
1018 // save the NFTokenIDs to use later
-
1019 std::vector<Json::Value> tokenIDs;
-
1020 {
-
1021 Json::Value params;
-
1022 params[jss::account] = bob.human();
-
1023 params[jss::ledger_index] = "validated";
-
1024 Json::Value const resp = env.rpc("json", "account_nfts", to_string(params));
-
1025 Json::Value const& nfts = resp[jss::result][jss::account_nfts];
-
1026 for (Json::Value const& nft : nfts)
-
1027 tokenIDs.push_back(nft["NFTokenID"]);
-
1028 }
-
1029
-
1030 // this lambda function is used to check if the account_nfts method
-
1031 // returns the correct token information. lastIndex is used to query the
-
1032 // last marker.
-
1033 auto compareNFTs = [&tokenIDs, &env, &bob](unsigned const limit, unsigned const lastIndex) {
-
1034 Json::Value params;
-
1035 params[jss::account] = bob.human();
-
1036 params[jss::limit] = limit;
-
1037 params[jss::marker] = tokenIDs[lastIndex];
-
1038 params[jss::ledger_index] = "validated";
-
1039 Json::Value const resp = env.rpc("json", "account_nfts", to_string(params));
-
1040
-
1041 if (resp[jss::result].isMember(jss::error))
-
1042 return false;
-
1043
-
1044 Json::Value const& nfts = resp[jss::result][jss::account_nfts];
-
1045 unsigned const nftsCount =
-
1046 tokenIDs.size() - lastIndex - 1 < limit ? tokenIDs.size() - lastIndex - 1 : limit;
-
1047
-
1048 if (nfts.size() != nftsCount)
-
1049 return false;
-
1050
-
1051 for (unsigned i = 0; i < nftsCount; i++)
-
1052 {
-
1053 if (nfts[i]["NFTokenID"] != tokenIDs[lastIndex + 1 + i])
-
1054 return false;
-
1055 }
-
1056
-
1057 return true;
-
1058 };
-
1059
-
1060 // test a valid marker which is equal to the third tokenID
-
1061 BEAST_EXPECT(compareNFTs(4, 2));
-
1062
-
1063 // test a valid marker which is equal to the 8th tokenID
-
1064 BEAST_EXPECT(compareNFTs(4, 7));
-
1065
-
1066 // lambda that holds common code for invalid cases.
-
1067 auto testInvalidMarker = [&env, &bob](auto marker, char const* errorMessage) {
-
1068 Json::Value params;
-
1069 params[jss::account] = bob.human();
-
1070 params[jss::limit] = 4;
-
1071 params[jss::ledger_index] = jss::validated;
-
1072 params[jss::marker] = marker;
-
1073 Json::Value const resp = env.rpc("json", "account_nfts", to_string(params));
-
1074 return resp[jss::result][jss::error_message] == errorMessage;
-
1075 };
-
1076
-
1077 // test an invalid marker that is not a string
-
1078 BEAST_EXPECT(testInvalidMarker(17, "Invalid field \'marker\', not string."));
-
1079
-
1080 // test an invalid marker that has a non-hex character
-
1081 BEAST_EXPECT(testInvalidMarker(
-
1082 "00000000F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B900000000000000000G", "Invalid field \'marker\'."));
-
1083
-
1084 // this lambda function is used to create some fake marker using given
-
1085 // taxon and sequence because we want to test some unassociated markers
-
1086 // later
-
1087 auto createFakeNFTMarker = [](AccountID const& issuer,
-
1088 std::uint32_t taxon,
-
1089 std::uint32_t tokenSeq,
-
1090 std::uint16_t flags = 0,
-
1091 std::uint16_t fee = 0) {
-
1092 // the marker has the exact same format as an NFTokenID
-
1093 return to_string(NFTokenMint::createNFTokenID(flags, fee, issuer, nft::toTaxon(taxon), tokenSeq));
-
1094 };
-
1095
-
1096 // test an unassociated marker which does not exist in the NFTokenIDs
-
1097 BEAST_EXPECT(
-
1098 testInvalidMarker(createFakeNFTMarker(bob.id(), 0x000000000, 0x00000000), "Invalid field \'marker\'."));
-
1099
-
1100 // test an unassociated marker which exceeds the maximum value of the
-
1101 // existing NFTokenID
-
1102 BEAST_EXPECT(
-
1103 testInvalidMarker(createFakeNFTMarker(bob.id(), 0xFFFFFFFF, 0xFFFFFFFF), "Invalid field \'marker\'."));
-
1104 }
+
995
+
996 void
+
+ +
998 {
+
999 // there's some bug found in account_nfts method that it did not
+
1000 // return invalid params when providing unassociated nft marker.
+
1001 // this test tests both situations when providing valid nft marker
+
1002 // and unassociated nft marker.
+
1003 testcase("NFTsMarker");
+
1004
+
1005 using namespace jtx;
+
1006 Env env(*this);
+
1007
+
1008 Account const bob{"bob"};
+
1009 env.fund(XRP(10000), bob);
+
1010
+
1011 static constexpr unsigned nftsSize = 10;
+
1012 for (unsigned i = 0; i < nftsSize; i++)
+
1013 {
+
1014 env(token::mint(bob, 0));
+
1015 }
+
1016
+
1017 env.close();
+
1018
+
1019 // save the NFTokenIDs to use later
+
1020 std::vector<Json::Value> tokenIDs;
+
1021 {
+
1022 Json::Value params;
+
1023 params[jss::account] = bob.human();
+
1024 params[jss::ledger_index] = "validated";
+
1025 Json::Value const resp = env.rpc("json", "account_nfts", to_string(params));
+
1026 Json::Value const& nfts = resp[jss::result][jss::account_nfts];
+
1027 for (Json::Value const& nft : nfts)
+
1028 tokenIDs.push_back(nft["NFTokenID"]);
+
1029 }
+
1030
+
1031 // this lambda function is used to check if the account_nfts method
+
1032 // returns the correct token information. lastIndex is used to query the
+
1033 // last marker.
+
1034 auto compareNFTs = [&tokenIDs, &env, &bob](unsigned const limit, unsigned const lastIndex) {
+
1035 Json::Value params;
+
1036 params[jss::account] = bob.human();
+
1037 params[jss::limit] = limit;
+
1038 params[jss::marker] = tokenIDs[lastIndex];
+
1039 params[jss::ledger_index] = "validated";
+
1040 Json::Value const resp = env.rpc("json", "account_nfts", to_string(params));
+
1041
+
1042 if (resp[jss::result].isMember(jss::error))
+
1043 return false;
+
1044
+
1045 Json::Value const& nfts = resp[jss::result][jss::account_nfts];
+
1046 unsigned const nftsCount =
+
1047 tokenIDs.size() - lastIndex - 1 < limit ? tokenIDs.size() - lastIndex - 1 : limit;
+
1048
+
1049 if (nfts.size() != nftsCount)
+
1050 return false;
+
1051
+
1052 for (unsigned i = 0; i < nftsCount; i++)
+
1053 {
+
1054 if (nfts[i]["NFTokenID"] != tokenIDs[lastIndex + 1 + i])
+
1055 return false;
+
1056 }
+
1057
+
1058 return true;
+
1059 };
+
1060
+
1061 // test a valid marker which is equal to the third tokenID
+
1062 BEAST_EXPECT(compareNFTs(4, 2));
+
1063
+
1064 // test a valid marker which is equal to the 8th tokenID
+
1065 BEAST_EXPECT(compareNFTs(4, 7));
+
1066
+
1067 // lambda that holds common code for invalid cases.
+
1068 auto testInvalidMarker = [&env, &bob](auto marker, char const* errorMessage) {
+
1069 Json::Value params;
+
1070 params[jss::account] = bob.human();
+
1071 params[jss::limit] = 4;
+
1072 params[jss::ledger_index] = jss::validated;
+
1073 params[jss::marker] = marker;
+
1074 Json::Value const resp = env.rpc("json", "account_nfts", to_string(params));
+
1075 return resp[jss::result][jss::error_message] == errorMessage;
+
1076 };
+
1077
+
1078 // test an invalid marker that is not a string
+
1079 BEAST_EXPECT(testInvalidMarker(17, "Invalid field \'marker\', not string."));
+
1080
+
1081 // test an invalid marker that has a non-hex character
+
1082 BEAST_EXPECT(testInvalidMarker(
+
1083 "00000000F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B900000000000000000G", "Invalid field \'marker\'."));
+
1084
+
1085 // this lambda function is used to create some fake marker using given
+
1086 // taxon and sequence because we want to test some unassociated markers
+
1087 // later
+
1088 auto createFakeNFTMarker = [](AccountID const& issuer,
+
1089 std::uint32_t taxon,
+
1090 std::uint32_t tokenSeq,
+
1091 std::uint16_t flags = 0,
+
1092 std::uint16_t fee = 0) {
+
1093 // the marker has the exact same format as an NFTokenID
+
1094 return to_string(NFTokenMint::createNFTokenID(flags, fee, issuer, nft::toTaxon(taxon), tokenSeq));
+
1095 };
+
1096
+
1097 // test an unassociated marker which does not exist in the NFTokenIDs
+
1098 BEAST_EXPECT(
+
1099 testInvalidMarker(createFakeNFTMarker(bob.id(), 0x000000000, 0x00000000), "Invalid field \'marker\'."));
+
1100
+
1101 // test an unassociated marker which exceeds the maximum value of the
+
1102 // existing NFTokenID
+
1103 BEAST_EXPECT(
+
1104 testInvalidMarker(createFakeNFTMarker(bob.id(), 0xFFFFFFFF, 0xFFFFFFFF), "Invalid field \'marker\'."));
+
1105 }
-
1105
-
1106 void
-
- -
1108 {
-
1109 testcase("account_nfts");
-
1110
-
1111 using namespace jtx;
-
1112 Env env(*this);
-
1113
-
1114 // test validation
-
1115 {
-
1116 auto testInvalidAccountParam = [&](auto const& param) {
-
1117 Json::Value params;
-
1118 params[jss::account] = param;
-
1119 auto jrr = env.rpc("json", "account_nfts", to_string(params))[jss::result];
-
1120 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
-
1121 BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'account'.");
-
1122 };
-
1123
-
1124 testInvalidAccountParam(1);
-
1125 testInvalidAccountParam(1.1);
-
1126 testInvalidAccountParam(true);
-
1127 testInvalidAccountParam(Json::Value(Json::nullValue));
-
1128 testInvalidAccountParam(Json::Value(Json::objectValue));
-
1129 testInvalidAccountParam(Json::Value(Json::arrayValue));
-
1130 }
-
1131 }
+
1106
+
1107 void
+
+ +
1109 {
+
1110 testcase("account_nfts");
+
1111
+
1112 using namespace jtx;
+
1113 Env env(*this);
+
1114
+
1115 // test validation
+
1116 {
+
1117 auto testInvalidAccountParam = [&](auto const& param) {
+
1118 Json::Value params;
+
1119 params[jss::account] = param;
+
1120 auto jrr = env.rpc("json", "account_nfts", to_string(params))[jss::result];
+
1121 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
+
1122 BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'account'.");
+
1123 };
+
1124
+
1125 testInvalidAccountParam(1);
+
1126 testInvalidAccountParam(1.1);
+
1127 testInvalidAccountParam(true);
+
1128 testInvalidAccountParam(Json::Value(Json::nullValue));
+
1129 testInvalidAccountParam(Json::Value(Json::objectValue));
+
1130 testInvalidAccountParam(Json::Value(Json::arrayValue));
+
1131 }
+
1132 }
-
1132
-
1133 void
-
- -
1135 {
-
1136 testcase("AccountObjectMarker");
-
1137
-
1138 using namespace jtx;
-
1139 Env env(*this);
-
1140
-
1141 Account const alice{"alice"};
-
1142 Account const bob{"bob"};
-
1143 Account const carol{"carol"};
-
1144 env.fund(XRP(10000), alice, bob, carol);
-
1145
-
1146 unsigned const accountObjectSize = 30;
-
1147 for (unsigned i = 0; i < accountObjectSize; i++)
-
1148 env(check::create(alice, bob, XRP(10)));
-
1149
-
1150 for (unsigned i = 0; i < 10; i++)
-
1151 env(token::mint(carol, 0));
-
1152
-
1153 env.close();
-
1154
-
1155 unsigned const limit = 11;
-
1156 Json::Value marker;
-
1157
-
1158 // test account_objects with a limit and update marker
-
1159 {
-
1160 Json::Value params;
-
1161 params[jss::account] = bob.human();
-
1162 params[jss::limit] = limit;
-
1163 params[jss::ledger_index] = "validated";
-
1164 auto resp = env.rpc("json", "account_objects", to_string(params));
-
1165 auto& accountObjects = resp[jss::result][jss::account_objects];
-
1166 marker = resp[jss::result][jss::marker];
-
1167 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
-
1168 BEAST_EXPECT(accountObjects.size() == limit);
-
1169 }
-
1170
-
1171 // test account_objects with valid marker and update marker
-
1172 {
-
1173 Json::Value params;
-
1174 params[jss::account] = bob.human();
-
1175 params[jss::limit] = limit;
-
1176 params[jss::marker] = marker;
-
1177 params[jss::ledger_index] = "validated";
-
1178 auto resp = env.rpc("json", "account_objects", to_string(params));
-
1179 auto& accountObjects = resp[jss::result][jss::account_objects];
-
1180 marker = resp[jss::result][jss::marker];
-
1181 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
-
1182 BEAST_EXPECT(accountObjects.size() == limit);
-
1183 }
-
1184
-
1185 // this lambda function is used to check invalid marker response.
-
1186 auto testInvalidMarker = [&](std::string& marker) {
-
1187 Json::Value params;
-
1188 params[jss::account] = bob.human();
-
1189 params[jss::limit] = limit;
-
1190 params[jss::ledger_index] = jss::validated;
-
1191 params[jss::marker] = marker;
-
1192 Json::Value const resp = env.rpc("json", "account_objects", to_string(params));
-
1193 return resp[jss::result][jss::error_message] == "Invalid field \'marker\'.";
-
1194 };
-
1195
-
1196 auto const markerStr = marker.asString();
-
1197 auto const& idx = markerStr.find(',');
-
1198 auto const dirIndex = markerStr.substr(0, idx);
-
1199 auto const entryIndex = markerStr.substr(idx + 1);
-
1200
-
1201 // test account_objects with an invalid marker that contains no ','
-
1202 {
-
1203 std::string s = dirIndex + entryIndex;
-
1204 BEAST_EXPECT(testInvalidMarker(s));
-
1205 }
-
1206
-
1207 // test invalid marker by adding invalid string after the maker:
-
1208 // "dirIndex,entryIndex,1234"
-
1209 {
-
1210 std::string s = markerStr + ",1234";
-
1211 BEAST_EXPECT(testInvalidMarker(s));
-
1212 }
-
1213
-
1214 // test account_objects with an invalid marker containing invalid
-
1215 // dirIndex by replacing some characters from the dirIndex.
-
1216 {
-
1217 std::string s = markerStr;
-
1218 s.replace(0, 7, "FFFFFFF");
-
1219 BEAST_EXPECT(testInvalidMarker(s));
-
1220 }
-
1221
-
1222 // test account_objects with an invalid marker containing invalid
-
1223 // entryIndex by replacing some characters from the entryIndex.
-
1224 {
-
1225 std::string s = entryIndex;
-
1226 s.replace(0, 7, "FFFFFFF");
-
1227 s = dirIndex + ',' + s;
-
1228 BEAST_EXPECT(testInvalidMarker(s));
-
1229 }
-
1230
-
1231 // test account_objects with an invalid marker containing invalid
-
1232 // dirIndex with marker: ",entryIndex"
-
1233 {
-
1234 std::string s = ',' + entryIndex;
-
1235 BEAST_EXPECT(testInvalidMarker(s));
-
1236 }
-
1237
-
1238 // test account_objects with marker: "0,entryIndex", this is still
-
1239 // valid, because when dirIndex = 0, we will use root key to find
-
1240 // dir.
-
1241 {
-
1242 std::string s = "0," + entryIndex;
-
1243 Json::Value params;
-
1244 params[jss::account] = bob.human();
-
1245 params[jss::limit] = limit;
-
1246 params[jss::marker] = s;
-
1247 params[jss::ledger_index] = "validated";
-
1248 auto resp = env.rpc("json", "account_objects", to_string(params));
-
1249 auto& accountObjects = resp[jss::result][jss::account_objects];
-
1250 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
-
1251 BEAST_EXPECT(accountObjects.size() == limit);
-
1252 }
-
1253
-
1254 // test account_objects with an invalid marker containing invalid
-
1255 // entryIndex with marker: "dirIndex,"
-
1256 {
-
1257 std::string s = dirIndex + ',';
-
1258 BEAST_EXPECT(testInvalidMarker(s));
-
1259 }
-
1260
-
1261 // test account_objects with an invalid marker containing invalid
-
1262 // entryIndex with marker: "dirIndex,0"
-
1263 {
-
1264 std::string s = dirIndex + ",0";
-
1265 BEAST_EXPECT(testInvalidMarker(s));
-
1266 }
-
1267
-
1268 // continue getting account_objects with valid marker. This will be the
-
1269 // last page, so response will not contain any marker.
-
1270 {
-
1271 Json::Value params;
-
1272 params[jss::account] = bob.human();
-
1273 params[jss::limit] = limit;
-
1274 params[jss::marker] = marker;
-
1275 params[jss::ledger_index] = "validated";
-
1276 auto resp = env.rpc("json", "account_objects", to_string(params));
-
1277 auto& accountObjects = resp[jss::result][jss::account_objects];
-
1278 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
-
1279 BEAST_EXPECT(accountObjects.size() == accountObjectSize - limit * 2);
-
1280 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
-
1281 }
-
1282
-
1283 // test account_objects when the account only have nft pages, but
-
1284 // provided invalid entry index.
-
1285 {
-
1286 Json::Value params;
-
1287 params[jss::account] = carol.human();
-
1288 params[jss::limit] = 10;
-
1289 params[jss::marker] = "0," + entryIndex;
-
1290 params[jss::ledger_index] = "validated";
-
1291 auto resp = env.rpc("json", "account_objects", to_string(params));
-
1292 auto& accountObjects = resp[jss::result][jss::account_objects];
-
1293 BEAST_EXPECT(accountObjects.size() == 0);
-
1294 }
-
1295 }
+
1133
+
1134 void
+
+ +
1136 {
+
1137 testcase("AccountObjectMarker");
+
1138
+
1139 using namespace jtx;
+
1140 Env env(*this);
+
1141
+
1142 Account const alice{"alice"};
+
1143 Account const bob{"bob"};
+
1144 Account const carol{"carol"};
+
1145 env.fund(XRP(10000), alice, bob, carol);
+
1146
+
1147 unsigned const accountObjectSize = 30;
+
1148 for (unsigned i = 0; i < accountObjectSize; i++)
+
1149 env(check::create(alice, bob, XRP(10)));
+
1150
+
1151 for (unsigned i = 0; i < 10; i++)
+
1152 env(token::mint(carol, 0));
+
1153
+
1154 env.close();
+
1155
+
1156 unsigned const limit = 11;
+
1157 Json::Value marker;
+
1158
+
1159 // test account_objects with a limit and update marker
+
1160 {
+
1161 Json::Value params;
+
1162 params[jss::account] = bob.human();
+
1163 params[jss::limit] = limit;
+
1164 params[jss::ledger_index] = "validated";
+
1165 auto resp = env.rpc("json", "account_objects", to_string(params));
+
1166 auto& accountObjects = resp[jss::result][jss::account_objects];
+
1167 marker = resp[jss::result][jss::marker];
+
1168 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
+
1169 BEAST_EXPECT(accountObjects.size() == limit);
+
1170 }
+
1171
+
1172 // test account_objects with valid marker and update marker
+
1173 {
+
1174 Json::Value params;
+
1175 params[jss::account] = bob.human();
+
1176 params[jss::limit] = limit;
+
1177 params[jss::marker] = marker;
+
1178 params[jss::ledger_index] = "validated";
+
1179 auto resp = env.rpc("json", "account_objects", to_string(params));
+
1180 auto& accountObjects = resp[jss::result][jss::account_objects];
+
1181 marker = resp[jss::result][jss::marker];
+
1182 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
+
1183 BEAST_EXPECT(accountObjects.size() == limit);
+
1184 }
+
1185
+
1186 // this lambda function is used to check invalid marker response.
+
1187 auto testInvalidMarker = [&](std::string& marker) {
+
1188 Json::Value params;
+
1189 params[jss::account] = bob.human();
+
1190 params[jss::limit] = limit;
+
1191 params[jss::ledger_index] = jss::validated;
+
1192 params[jss::marker] = marker;
+
1193 Json::Value const resp = env.rpc("json", "account_objects", to_string(params));
+
1194 return resp[jss::result][jss::error_message] == "Invalid field \'marker\'.";
+
1195 };
+
1196
+
1197 auto const markerStr = marker.asString();
+
1198 auto const& idx = markerStr.find(',');
+
1199 auto const dirIndex = markerStr.substr(0, idx);
+
1200 auto const entryIndex = markerStr.substr(idx + 1);
+
1201
+
1202 // test account_objects with an invalid marker that contains no ','
+
1203 {
+
1204 std::string s = dirIndex + entryIndex;
+
1205 BEAST_EXPECT(testInvalidMarker(s));
+
1206 }
+
1207
+
1208 // test invalid marker by adding invalid string after the maker:
+
1209 // "dirIndex,entryIndex,1234"
+
1210 {
+
1211 std::string s = markerStr + ",1234";
+
1212 BEAST_EXPECT(testInvalidMarker(s));
+
1213 }
+
1214
+
1215 // test account_objects with an invalid marker containing invalid
+
1216 // dirIndex by replacing some characters from the dirIndex.
+
1217 {
+
1218 std::string s = markerStr;
+
1219 s.replace(0, 7, "FFFFFFF");
+
1220 BEAST_EXPECT(testInvalidMarker(s));
+
1221 }
+
1222
+
1223 // test account_objects with an invalid marker containing invalid
+
1224 // entryIndex by replacing some characters from the entryIndex.
+
1225 {
+
1226 std::string s = entryIndex;
+
1227 s.replace(0, 7, "FFFFFFF");
+
1228 s = dirIndex + ',' + s;
+
1229 BEAST_EXPECT(testInvalidMarker(s));
+
1230 }
+
1231
+
1232 // test account_objects with an invalid marker containing invalid
+
1233 // dirIndex with marker: ",entryIndex"
+
1234 {
+
1235 std::string s = ',' + entryIndex;
+
1236 BEAST_EXPECT(testInvalidMarker(s));
+
1237 }
+
1238
+
1239 // test account_objects with marker: "0,entryIndex", this is still
+
1240 // valid, because when dirIndex = 0, we will use root key to find
+
1241 // dir.
+
1242 {
+
1243 std::string s = "0," + entryIndex;
+
1244 Json::Value params;
+
1245 params[jss::account] = bob.human();
+
1246 params[jss::limit] = limit;
+
1247 params[jss::marker] = s;
+
1248 params[jss::ledger_index] = "validated";
+
1249 auto resp = env.rpc("json", "account_objects", to_string(params));
+
1250 auto& accountObjects = resp[jss::result][jss::account_objects];
+
1251 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
+
1252 BEAST_EXPECT(accountObjects.size() == limit);
+
1253 }
+
1254
+
1255 // test account_objects with an invalid marker containing invalid
+
1256 // entryIndex with marker: "dirIndex,"
+
1257 {
+
1258 std::string s = dirIndex + ',';
+
1259 BEAST_EXPECT(testInvalidMarker(s));
+
1260 }
+
1261
+
1262 // test account_objects with an invalid marker containing invalid
+
1263 // entryIndex with marker: "dirIndex,0"
+
1264 {
+
1265 std::string s = dirIndex + ",0";
+
1266 BEAST_EXPECT(testInvalidMarker(s));
+
1267 }
+
1268
+
1269 // continue getting account_objects with valid marker. This will be the
+
1270 // last page, so response will not contain any marker.
+
1271 {
+
1272 Json::Value params;
+
1273 params[jss::account] = bob.human();
+
1274 params[jss::limit] = limit;
+
1275 params[jss::marker] = marker;
+
1276 params[jss::ledger_index] = "validated";
+
1277 auto resp = env.rpc("json", "account_objects", to_string(params));
+
1278 auto& accountObjects = resp[jss::result][jss::account_objects];
+
1279 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
+
1280 BEAST_EXPECT(accountObjects.size() == accountObjectSize - limit * 2);
+
1281 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
+
1282 }
+
1283
+
1284 // test account_objects when the account only have nft pages, but
+
1285 // provided invalid entry index.
+
1286 {
+
1287 Json::Value params;
+
1288 params[jss::account] = carol.human();
+
1289 params[jss::limit] = 10;
+
1290 params[jss::marker] = "0," + entryIndex;
+
1291 params[jss::ledger_index] = "validated";
+
1292 auto resp = env.rpc("json", "account_objects", to_string(params));
+
1293 auto& accountObjects = resp[jss::result][jss::account_objects];
+
1294 BEAST_EXPECT(accountObjects.size() == 0);
+
1295 }
+
1296 }
-
1296
-
1297 void
-
-
1298 run() override
-
1299 {
-
1300 testErrors();
- - - - - - -
1307 }
+
1297
+
1298 void
+
+
1299 run() override
+
1300 {
+
1301 testErrors();
+ + + + + + +
1308 }
-
1308};
+
1309};
-
1309
-
1310BEAST_DEFINE_TESTSUITE(AccountObjects, rpc, xrpl);
-
1311
-
1312} // namespace test
-
1313} // namespace xrpl
+
1310
+
1311BEAST_DEFINE_TESTSUITE(AccountObjects, rpc, xrpl);
+
1312
+
1313} // namespace test
+
1314} // namespace xrpl
T begin(T... args)
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
+
bool isArray() const
UInt size() const
Number of values in array or object.
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
static uint256 createNFTokenID(std::uint16_t flags, std::uint16_t fee, AccountID const &issuer, nft::Taxon taxon, std::uint32_t tokenSeq)
-
void run() override
Runs the suite.
+
void run() override
Runs the suite.
- - + + - +
Convenience class to test AMM functionality.
Definition AMM.h:104
Immutable cryptographic account descriptor.
Definition Account.h:19
@@ -1469,7 +1470,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value mint(jtx::Account const &account, std::uint32_t nfTokenTaxon)
Mint an NFToken.
Definition token.cpp:15
uint256 getNextID(jtx::Env const &env, jtx::Account const &issuer, std::uint32_t nfTokenTaxon, std::uint16_t flags, std::uint16_t xferFee)
Get the next NFTokenID that will be issued.
Definition token.cpp:49
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
FeatureBitset testable_amendments()
Definition Env.h:76
diff --git a/AccountOffers__test_8cpp_source.html b/AccountOffers__test_8cpp_source.html index 7d086e744d..f543c094da 100644 --- a/AccountOffers__test_8cpp_source.html +++ b/AccountOffers__test_8cpp_source.html @@ -375,10 +375,10 @@ $(document).ready(function() { init_codefold(0); });
280} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
std::string toStyledString() const
-
bool isString() const
+
std::string toStyledString() const
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -397,7 +397,7 @@ $(document).ready(function() { init_codefold(0); });
@ nullValue
'null' value
Definition json_value.h:19
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
bool checkArraySize(Json::Value const &val, unsigned int size)
diff --git a/AccountTx_8cpp_source.html b/AccountTx_8cpp_source.html index 932739f9bc..27026caa84 100644 --- a/AccountTx_8cpp_source.html +++ b/AccountTx_8cpp_source.html @@ -522,11 +522,11 @@ $(document).ready(function() { init_codefold(0); });
429} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Int asInt() const
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
Stream debug() const
Definition Journal.h:300
virtual Config & config()=0
bool useTxTables() const
Definition Config.h:318
diff --git a/AccountTx__test_8cpp_source.html b/AccountTx__test_8cpp_source.html index 2097d95b43..3ebb6729a9 100644 --- a/AccountTx__test_8cpp_source.html +++ b/AccountTx__test_8cpp_source.html @@ -914,7 +914,7 @@ $(document).ready(function() { init_codefold(0); });
T bind_front(T... args)
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:516
@@ -962,7 +962,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
std::unique_ptr< Config > makeConfig(std::map< std::string, std::string > extraTxQ={}, std::map< std::string, std::string > extraVoting={})
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:23
diff --git a/AmendmentTable_8cpp_source.html b/AmendmentTable_8cpp_source.html index 14ffa54122..cfcd2ea68b 100644 --- a/AmendmentTable_8cpp_source.html +++ b/AmendmentTable_8cpp_source.html @@ -1183,15 +1183,15 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfLostMajority
Definition TxFlags.h:109
constexpr std::uint32_t tfGotMajority
Definition TxFlags.h:108
-
void readAmendments(soci::session &session, std::function< void(boost::optional< std::string > amendment_hash, boost::optional< std::string > amendment_name, boost::optional< AmendmentVote > vote)> const &callback)
readAmendments Reads all amendments from the FeatureVotes table.
Definition Wallet.cpp:212
-
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:188
+
void readAmendments(soci::session &session, std::function< void(boost::optional< std::string > amendment_hash, boost::optional< std::string > amendment_name, boost::optional< AmendmentVote > vote)> const &callback)
readAmendments Reads all amendments from the FeatureVotes table.
Definition Wallet.cpp:213
+
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:189
AmendmentVote
Definition Wallet.h:119
constexpr std::ratio< 80, 100 > amendmentMajorityCalcThreshold
The minimum amount of support an amendment should have.
-
void voteAmendment(soci::session &session, uint256 const &amendment, std::string const &name, AmendmentVote vote)
voteAmendment Set the veto value for a particular amendment.
Definition Wallet.cpp:244
+
void voteAmendment(soci::session &session, uint256 const &amendment, std::string const &name, AmendmentVote vote)
voteAmendment Set the veto value for a particular amendment.
Definition Wallet.cpp:245
static std::vector< std::pair< uint256, std::string > > parseSection(Section const &section)
diff --git a/AmountConversions_8h_source.html b/AmountConversions_8h_source.html index 0a7e02777c..cfc80d28b4 100644 --- a/AmountConversions_8h_source.html +++ b/AmountConversions_8h_source.html @@ -295,9 +295,9 @@ $(document).ready(function() { init_codefold(0); });
184
185} // namespace xrpl
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
-
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:161
-
exponent_type exponent() const noexcept
Definition IOUAmount.h:155
-
int signum() const noexcept
Return the sign of the amount.
Definition IOUAmount.h:149
+
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:163
+
exponent_type exponent() const noexcept
Definition IOUAmount.h:157
+
int signum() const noexcept
Return the sign of the amount.
Definition IOUAmount.h:151
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
AccountID account
Definition Issue.h:16
diff --git a/ApiVersion_8h_source.html b/ApiVersion_8h_source.html index d720983152..d0e66d43b6 100644 --- a/ApiVersion_8h_source.html +++ b/ApiVersion_8h_source.html @@ -209,8 +209,8 @@ $(document).ready(function() { init_codefold(0); });
152} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
Int asInt() const
-
bool isObject() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isObject() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A Semantic Version number.
std::string print() const
Produce a string from semantic version components.
diff --git a/Application_8cpp_source.html b/Application_8cpp_source.html index a7f9563ed2..167b0d3e5f 100644 --- a/Application_8cpp_source.html +++ b/Application_8cpp_source.html @@ -344,1939 +344,1943 @@ $(document).ready(function() { init_codefold(0); });
243 , m_journal(logs_->journal("Application"))
244
245 // PerfLog must be started before any other threads are launched.
-
246 , perfLog_(perf::make_PerfLog(
-
247 perf::setup_PerfLog(config_->section("perf"), config_->CONFIG_DIR),
-
248 *this,
-
249 logs_->journal("PerfLog"),
-
250 [this] { signalStop("PerfLog"); }))
-
251
-
252 , m_txMaster(*this)
-
253
-
254 , m_collectorManager(make_CollectorManager(config_->section(SECTION_INSIGHT), logs_->journal("Collector")))
-
255
- - - -
259 return 1;
-
260
-
261 if (config->WORKERS)
-
262 return config->WORKERS;
-
263
-
264 auto count = static_cast<int>(std::thread::hardware_concurrency());
+
246 , perfLog_(
+
247 perf::make_PerfLog(
+
248 perf::setup_PerfLog(config_->section("perf"), config_->CONFIG_DIR),
+
249 *this,
+
250 logs_->journal("PerfLog"),
+
251 [this] { signalStop("PerfLog"); }))
+
252
+
253 , m_txMaster(*this)
+
254
+
255 , m_collectorManager(make_CollectorManager(config_->section(SECTION_INSIGHT), logs_->journal("Collector")))
+
256
+
257 , m_jobQueue(
+ + + +
261 return 1;
+
262
+
263 if (config->WORKERS)
+
264 return config->WORKERS;
265
-
266 // Be more aggressive about the number of threads to use
-
267 // for the job queue if the server is configured as
-
268 // "large" or "huge" if there are enough cores.
-
269 if (config->NODE_SIZE >= 4 && count >= 16)
-
270 count = 6 + std::min(count, 8);
-
271 else if (config->NODE_SIZE >= 3 && count >= 8)
-
272 count = 4 + std::min(count, 6);
-
273 else
-
274 count = 2 + std::min(count, 4);
-
275
-
276 return count;
-
277 }(config_),
-
278 m_collectorManager->group("jobq"),
-
279 logs_->journal("JobQueue"),
-
280 *logs_,
-
281 *perfLog_))
-
282
- +
266 auto count = static_cast<int>(std::thread::hardware_concurrency());
+
267
+
268 // Be more aggressive about the number of threads to use
+
269 // for the job queue if the server is configured as
+
270 // "large" or "huge" if there are enough cores.
+
271 if (config->NODE_SIZE >= 4 && count >= 16)
+
272 count = 6 + std::min(count, 8);
+
273 else if (config->NODE_SIZE >= 3 && count >= 8)
+
274 count = 4 + std::min(count, 6);
+
275 else
+
276 count = 2 + std::min(count, 4);
+
277
+
278 return count;
+
279 }(config_),
+
280 m_collectorManager->group("jobq"),
+
281 logs_->journal("JobQueue"),
+
282 *logs_,
+
283 *perfLog_))
284
-
285 , m_shaMapStore(make_SHAMapStore(*this, m_nodeStoreScheduler, logs_->journal("SHAMapStore")))
+
286
-
287 , m_tempNodeCache("NodeCache", 16384, std::chrono::seconds{90}, stopwatch(), logs_->journal("TaggedCache"))
+
287 , m_shaMapStore(make_SHAMapStore(*this, m_nodeStoreScheduler, logs_->journal("SHAMapStore")))
288
-
289 , cachedSLEs_("Cached SLEs", 0, std::chrono::minutes(1), stopwatch(), logs_->journal("CachedSLEs"))
+
289 , m_tempNodeCache("NodeCache", 16384, std::chrono::seconds{90}, stopwatch(), logs_->journal("TaggedCache"))
290
- +
291 , cachedSLEs_("Cached SLEs", 0, std::chrono::minutes(1), stopwatch(), logs_->journal("CachedSLEs"))
292
-
293 , m_resourceManager(Resource::make_Manager(m_collectorManager->collector(), logs_->journal("Resource")))
+
294
-
295 , m_nodeStore(m_shaMapStore->makeNodeStore(config_->PREFETCH_WORKERS > 0 ? config_->PREFETCH_WORKERS : 4))
+
295 , m_resourceManager(Resource::make_Manager(m_collectorManager->collector(), logs_->journal("Resource")))
296
- +
297 , m_nodeStore(m_shaMapStore->makeNodeStore(config_->PREFETCH_WORKERS > 0 ? config_->PREFETCH_WORKERS : 4))
298
-
299 , m_orderBookDB(make_OrderBookDB(*this, {config_->PATH_SEARCH_MAX, config_->standalone()}))
+
300
- -
302 std::make_unique<PathRequests>(*this, logs_->journal("PathRequest"), m_collectorManager->collector()))
-
303
- -
305 *this,
-
306 stopwatch(),
-
307 m_collectorManager->collector(),
-
308 logs_->journal("LedgerMaster")))
-
309
-
310 , ledgerCleaner_(make_LedgerCleaner(*this, logs_->journal("LedgerCleaner")))
-
311
-
312 // VFALCO NOTE must come before NetworkOPs to prevent a crash due
-
313 // to dependencies in the destructor.
-
314 //
- -
316
- -
318 *this,
-
319 m_collectorManager->collector(),
-
320 [this](std::shared_ptr<SHAMap> const& set, bool fromAcquire) { gotTXSet(set, fromAcquire); }))
-
321
- -
323
- -
325 "AcceptedLedger",
-
326 4,
- -
328 stopwatch(),
-
329 logs_->journal("TaggedCache"))
-
330
- -
332 *this,
-
333 stopwatch(),
-
334 config_->standalone(),
-
335 config_->NETWORK_QUORUM,
-
336 config_->START_VALID,
-
337 *m_jobQueue,
- - - -
341 logs_->journal("NetworkOPs"),
-
342 m_collectorManager->collector()))
-
343
-
344 , cluster_(std::make_unique<Cluster>(logs_->journal("Overlay")))
-
345
-
346 , peerReservations_(std::make_unique<PeerReservationTable>(logs_->journal("PeerReservationTable")))
-
347
-
348 , validatorManifests_(std::make_unique<ManifestCache>(logs_->journal("ManifestCache")))
-
349
-
350 , publisherManifests_(std::make_unique<ManifestCache>(logs_->journal("ManifestCache")))
-
351
- - - - -
356 config_->legacy("database_path"),
-
357 logs_->journal("ValidatorList"),
-
358 config_->VALIDATION_QUORUM))
-
359
- -
361
- -
363 *this,
- -
365 *m_jobQueue,
- - - -
369
-
370 , mFeeTrack(std::make_unique<LoadFeeTrack>(logs_->journal("LoadManager")))
-
371
- +
301 , m_orderBookDB(make_OrderBookDB(*this, {config_->PATH_SEARCH_MAX, config_->standalone()}))
+
302
+ +
304 std::make_unique<PathRequests>(*this, logs_->journal("PathRequest"), m_collectorManager->collector()))
+
305
+ + +
308 *this,
+
309 stopwatch(),
+
310 m_collectorManager->collector(),
+
311 logs_->journal("LedgerMaster")))
+
312
+
313 , ledgerCleaner_(make_LedgerCleaner(*this, logs_->journal("LedgerCleaner")))
+
314
+
315 // VFALCO NOTE must come before NetworkOPs to prevent a crash due
+
316 // to dependencies in the destructor.
+
317 //
+ +
319
+ +
321 *this,
+
322 m_collectorManager->collector(),
+
323 [this](std::shared_ptr<SHAMap> const& set, bool fromAcquire) { gotTXSet(set, fromAcquire); }))
+
324
+ +
326
+ +
328 "AcceptedLedger",
+
329 4,
+ +
331 stopwatch(),
+
332 logs_->journal("TaggedCache"))
+
333
+ +
335 *this,
+
336 stopwatch(),
+
337 config_->standalone(),
+
338 config_->NETWORK_QUORUM,
+
339 config_->START_VALID,
+
340 *m_jobQueue,
+ + + +
344 logs_->journal("NetworkOPs"),
+
345 m_collectorManager->collector()))
+
346
+
347 , cluster_(std::make_unique<Cluster>(logs_->journal("Overlay")))
+
348
+
349 , peerReservations_(std::make_unique<PeerReservationTable>(logs_->journal("PeerReservationTable")))
+
350
+
351 , validatorManifests_(std::make_unique<ManifestCache>(logs_->journal("ManifestCache")))
+
352
+
353 , publisherManifests_(std::make_unique<ManifestCache>(logs_->journal("ManifestCache")))
+
354
+
355 , validators_(
+ + + + +
360 config_->legacy("database_path"),
+
361 logs_->journal("ValidatorList"),
+
362 config_->VALIDATION_QUORUM))
+
363
+ +
365
+ +
367 *this,
+ +
369 *m_jobQueue,
+ + +
373
-
374 , mValidations(ValidationParms(), stopwatch(), *this, logs_->journal("Validations"))
+
374 , mFeeTrack(std::make_unique<LoadFeeTrack>(logs_->journal("LoadManager")))
375
-
376 , m_loadManager(make_LoadManager(*this, logs_->journal("LoadManager")))
+
377
-
378 , txQ_(std::make_unique<TxQ>(setup_TxQ(*config_), logs_->journal("TxQ")))
+
378 , mValidations(ValidationParms(), stopwatch(), *this, logs_->journal("Validations"))
379
- +
380 , m_loadManager(make_LoadManager(*this, logs_->journal("LoadManager")))
381
- +
382 , txQ_(std::make_unique<TxQ>(setup_TxQ(*config_), logs_->journal("TxQ")))
383
- +
385
-
386 , checkSigs_(true)
+
387
-
388 , m_resolver(ResolverAsio::New(get_io_context(), logs_->journal("Resolver")))
+
389
- -
391 m_collectorManager->collector()->make_event("ios_latency"),
-
392 logs_->journal("Application"),
- - - -
396 {
- -
398
-
399 add(m_resourceManager.get());
-
400
-
401 //
-
402 // VFALCO - READ THIS!
-
403 //
-
404 // Do not start threads, open sockets, or do any sort of "real work"
-
405 // inside the constructor. Put it in start instead. Or if you must,
-
406 // put it in setup (but everything in setup should be moved to start
-
407 // anyway.
-
408 //
-
409 // The reason is that the unit tests require an Application object to
-
410 // be created. But we don't actually start all the threads, sockets,
-
411 // and services when running the unit tests. Therefore anything which
-
412 // needs to be stopped will not get stopped correctly if it is
-
413 // started in this constructor.
-
414 //
-
415
-
416 add(ledgerCleaner_.get());
-
417 }
+
390 , checkSigs_(true)
+
391
+
392 , m_resolver(ResolverAsio::New(get_io_context(), logs_->journal("Resolver")))
+
393
+ +
395 m_collectorManager->collector()->make_event("ios_latency"),
+
396 logs_->journal("Application"),
+ + + +
400 {
+ +
402
+
403 add(m_resourceManager.get());
+
404
+
405 //
+
406 // VFALCO - READ THIS!
+
407 //
+
408 // Do not start threads, open sockets, or do any sort of "real work"
+
409 // inside the constructor. Put it in start instead. Or if you must,
+
410 // put it in setup (but everything in setup should be moved to start
+
411 // anyway.
+
412 //
+
413 // The reason is that the unit tests require an Application object to
+
414 // be created. But we don't actually start all the threads, sockets,
+
415 // and services when running the unit tests. Therefore anything which
+
416 // needs to be stopped will not get stopped correctly if it is
+
417 // started in this constructor.
+
418 //
+
419
+
420 add(ledgerCleaner_.get());
+
421 }
-
418
-
419 //--------------------------------------------------------------------------
-
420
-
421 bool
-
422 setup(boost::program_options::variables_map const& cmdline) override;
-
423 void
-
424 start(bool withTimers) override;
-
425 void
-
426 run() override;
+
422
+
423 //--------------------------------------------------------------------------
+
424
+
425 bool
+
426 setup(boost::program_options::variables_map const& cmdline) override;
427 void
-
428 signalStop(std::string msg) override;
-
429 bool
-
430 checkSigs() const override;
+
428 start(bool withTimers) override;
+
429 void
+
430 run() override;
431 void
-
432 checkSigs(bool) override;
+
432 signalStop(std::string msg) override;
433 bool
-
434 isStopping() const override;
-
435 int
-
436 fdRequired() const override;
-
437
-
438 //--------------------------------------------------------------------------
-
439
- -
-
441 instanceID() const override
-
442 {
-
443 return instanceCookie_;
-
444 }
+
434 checkSigs() const override;
+
435 void
+
436 checkSigs(bool) override;
+
437 bool
+
438 isStopping() const override;
+
439 int
+
440 fdRequired() const override;
+
441
+
442 //--------------------------------------------------------------------------
+
443
+ +
+
445 instanceID() const override
+
446 {
+
447 return instanceCookie_;
+
448 }
-
445
-
446 Logs&
-
-
447 logs() override
-
448 {
-
449 return *logs_;
-
450 }
+
449
+
450 Logs&
+
+
451 logs() override
+
452 {
+
453 return *logs_;
+
454 }
-
451
-
452 Config&
-
-
453 config() override
-
454 {
-
455 return *config_;
-
456 }
+
455
+
456 Config&
+
+
457 config() override
+
458 {
+
459 return *config_;
+
460 }
-
457
- -
- -
460 {
-
461 return *m_collectorManager;
-
462 }
+
461
+ +
+ +
464 {
+
465 return *m_collectorManager;
+
466 }
-
463
-
464 Family&
-
-
465 getNodeFamily() override
-
466 {
-
467 return nodeFamily_;
-
468 }
+
467
+
468 Family&
+
+
469 getNodeFamily() override
+
470 {
+
471 return nodeFamily_;
+
472 }
-
469
- -
-
471 timeKeeper() override
-
472 {
-
473 return *timeKeeper_;
-
474 }
+
473
+ +
+
475 timeKeeper() override
+
476 {
+
477 return *timeKeeper_;
+
478 }
-
475
-
476 JobQueue&
-
-
477 getJobQueue() override
-
478 {
-
479 return *m_jobQueue;
-
480 }
+
479
+
480 JobQueue&
+
+
481 getJobQueue() override
+
482 {
+
483 return *m_jobQueue;
+
484 }
-
481
- -
-
483 nodeIdentity() override
-
484 {
-
485 if (nodeIdentity_)
-
486 return *nodeIdentity_;
-
487
-
488 LogicError("Accessing Application::nodeIdentity() before it is initialized.");
-
489 }
+
485
+ +
+
487 nodeIdentity() override
+
488 {
+
489 if (nodeIdentity_)
+
490 return *nodeIdentity_;
+
491
+
492 LogicError("Accessing Application::nodeIdentity() before it is initialized.");
+
493 }
-
490
- -
-
492 getValidationPublicKey() const override
-
493 {
-
494 if (!validatorKeys_.keys)
-
495 return {};
-
496
-
497 return validatorKeys_.keys->publicKey;
-
498 }
+
494
+ +
+
496 getValidationPublicKey() const override
+
497 {
+
498 if (!validatorKeys_.keys)
+
499 return {};
+
500
+
501 return validatorKeys_.keys->publicKey;
+
502 }
-
499
- -
-
501 getOPs() override
-
502 {
-
503 return *m_networkOPs;
-
504 }
+
503
+ +
+
505 getOPs() override
+
506 {
+
507 return *m_networkOPs;
+
508 }
-
505
-
506 virtual ServerHandler&
-
- -
508 {
-
509 XRPL_ASSERT(
- -
511 "xrpl::ApplicationImp::getServerHandler : non-null server "
-
512 "handle");
-
513 return *serverHandler_;
-
514 }
+
509
+
510 virtual ServerHandler&
+
+ +
512 {
+
513 XRPL_ASSERT(
+ +
515 "xrpl::ApplicationImp::getServerHandler : non-null server "
+
516 "handle");
+
517 return *serverHandler_;
+
518 }
-
515
-
516 boost::asio::io_context&
-
-
517 getIOContext() override
-
518 {
-
519 return get_io_context();
-
520 }
+
519
+
520 boost::asio::io_context&
+
+
521 getIOContext() override
+
522 {
+
523 return get_io_context();
+
524 }
-
521
- -
-
523 getIOLatency() override
-
524 {
-
525 return m_io_latency_sampler.get();
-
526 }
+
525
+ +
+
527 getIOLatency() override
+
528 {
+
529 return m_io_latency_sampler.get();
+
530 }
-
527
- -
- -
530 {
-
531 return *m_ledgerMaster;
-
532 }
+
531
+ +
+ +
534 {
+
535 return *m_ledgerMaster;
+
536 }
-
533
- -
- -
536 {
-
537 return *ledgerCleaner_;
-
538 }
+
537
+ +
+ +
540 {
+
541 return *ledgerCleaner_;
+
542 }
-
539
- -
- -
542 {
-
543 return *m_ledgerReplayer;
-
544 }
+
543
+ +
+ +
546 {
+
547 return *m_ledgerReplayer;
+
548 }
-
545
- -
- -
548 {
-
549 return *m_inboundLedgers;
-
550 }
+
549
+ +
+ +
552 {
+
553 return *m_inboundLedgers;
+
554 }
-
551
- -
- -
554 {
-
555 return *m_inboundTransactions;
-
556 }
+
555
+ +
+ +
558 {
+
559 return *m_inboundTransactions;
+
560 }
-
557
- -
- -
560 {
- -
562 }
+
561
+ +
+ +
564 {
+ +
566 }
-
563
-
564 void
-
-
565 gotTXSet(std::shared_ptr<SHAMap> const& set, bool fromAcquire)
-
566 {
-
567 if (set)
-
568 m_networkOPs->mapComplete(set, fromAcquire);
-
569 }
+
567
+
568 void
+
+
569 gotTXSet(std::shared_ptr<SHAMap> const& set, bool fromAcquire)
+
570 {
+
571 if (set)
+
572 m_networkOPs->mapComplete(set, fromAcquire);
+
573 }
-
570
- -
- -
573 {
-
574 return m_txMaster;
-
575 }
+
574
+ +
+ +
577 {
+
578 return m_txMaster;
+
579 }
-
576
- -
-
578 getPerfLog() override
-
579 {
-
580 return *perfLog_;
-
581 }
+
580
+ +
+
582 getPerfLog() override
+
583 {
+
584 return *perfLog_;
+
585 }
-
582
-
583 NodeCache&
-
- -
585 {
-
586 return m_tempNodeCache;
-
587 }
+
586
+
587 NodeCache&
+
+ +
589 {
+
590 return m_tempNodeCache;
+
591 }
-
588
- -
-
590 getNodeStore() override
-
591 {
-
592 return *m_nodeStore;
-
593 }
+
592
+ +
+
594 getNodeStore() override
+
595 {
+
596 return *m_nodeStore;
+
597 }
-
594
- -
-
596 getMasterMutex() override
-
597 {
-
598 return m_masterMutex;
-
599 }
+
598
+ +
+
600 getMasterMutex() override
+
601 {
+
602 return m_masterMutex;
+
603 }
-
600
- -
-
602 getLoadManager() override
-
603 {
-
604 return *m_loadManager;
-
605 }
+
604
+ +
+
606 getLoadManager() override
+
607 {
+
608 return *m_loadManager;
+
609 }
-
606
- -
- -
609 {
-
610 return *m_resourceManager;
-
611 }
+
610
+ +
+ +
613 {
+
614 return *m_resourceManager;
+
615 }
-
612
- -
-
614 getOrderBookDB() override
-
615 {
-
616 return *m_orderBookDB;
-
617 }
+
616
+ +
+
618 getOrderBookDB() override
+
619 {
+
620 return *m_orderBookDB;
+
621 }
-
618
- -
- -
621 {
-
622 return *m_pathRequests;
-
623 }
+
622
+ +
+ +
625 {
+
626 return *m_pathRequests;
+
627 }
-
624
- -
-
626 cachedSLEs() override
-
627 {
-
628 return cachedSLEs_;
-
629 }
+
628
+ +
+
630 cachedSLEs() override
+
631 {
+
632 return cachedSLEs_;
+
633 }
-
630
- -
- -
633 {
-
634 return *m_amendmentTable;
-
635 }
+
634
+ +
+ +
637 {
+
638 return *m_amendmentTable;
+
639 }
-
636
- -
-
638 getFeeTrack() override
-
639 {
-
640 return *mFeeTrack;
-
641 }
+
640
+ +
+
642 getFeeTrack() override
+
643 {
+
644 return *mFeeTrack;
+
645 }
-
642
- -
-
644 getHashRouter() override
-
645 {
-
646 return *hashRouter_;
-
647 }
+
646
+ +
+
648 getHashRouter() override
+
649 {
+
650 return *hashRouter_;
+
651 }
-
648
- -
-
650 getValidations() override
-
651 {
-
652 return mValidations;
-
653 }
+
652
+ +
+
654 getValidations() override
+
655 {
+
656 return mValidations;
+
657 }
-
654
- -
-
656 validators() override
-
657 {
-
658 return *validators_;
-
659 }
+
658
+ +
+
660 validators() override
+
661 {
+
662 return *validators_;
+
663 }
-
660
- -
-
662 validatorSites() override
-
663 {
-
664 return *validatorSites_;
-
665 }
+
664
+ +
+
666 validatorSites() override
+
667 {
+
668 return *validatorSites_;
+
669 }
-
666
- -
- -
669 {
-
670 return *validatorManifests_;
-
671 }
+
670
+ +
+ +
673 {
+
674 return *validatorManifests_;
+
675 }
-
672
- -
- -
675 {
-
676 return *publisherManifests_;
-
677 }
+
676
+ +
+ +
679 {
+
680 return *publisherManifests_;
+
681 }
-
678
-
679 Cluster&
-
-
680 cluster() override
-
681 {
-
682 return *cluster_;
-
683 }
+
682
+
683 Cluster&
+
+
684 cluster() override
+
685 {
+
686 return *cluster_;
+
687 }
-
684
- -
- -
687 {
-
688 return *peerReservations_;
-
689 }
+
688
+ +
+ +
691 {
+
692 return *peerReservations_;
+
693 }
-
690
- -
-
692 getSHAMapStore() override
-
693 {
-
694 return *m_shaMapStore;
-
695 }
+
694
+ +
+
696 getSHAMapStore() override
+
697 {
+
698 return *m_shaMapStore;
+
699 }
-
696
- -
-
698 pendingSaves() override
-
699 {
-
700 return pendingSaves_;
-
701 }
+
700
+ +
+
702 pendingSaves() override
+
703 {
+
704 return pendingSaves_;
+
705 }
-
702
- -
-
704 openLedger() override
-
705 {
-
706 return *openLedger_;
-
707 }
+
706
+ +
+
708 openLedger() override
+
709 {
+
710 return *openLedger_;
+
711 }
-
708
-
709 OpenLedger const&
-
-
710 openLedger() const override
-
711 {
-
712 return *openLedger_;
-
713 }
+
712
+
713 OpenLedger const&
+
+
714 openLedger() const override
+
715 {
+
716 return *openLedger_;
+
717 }
-
714
-
715 Overlay&
-
-
716 overlay() override
-
717 {
-
718 XRPL_ASSERT(overlay_, "xrpl::ApplicationImp::overlay : non-null overlay");
-
719 return *overlay_;
-
720 }
+
718
+
719 Overlay&
+
+
720 overlay() override
+
721 {
+
722 XRPL_ASSERT(overlay_, "xrpl::ApplicationImp::overlay : non-null overlay");
+
723 return *overlay_;
+
724 }
-
721
-
722 TxQ&
-
-
723 getTxQ() override
-
724 {
-
725 XRPL_ASSERT(txQ_, "xrpl::ApplicationImp::getTxQ : non-null transaction queue");
-
726 return *txQ_;
-
727 }
+
725
+
726 TxQ&
+
+
727 getTxQ() override
+
728 {
+
729 XRPL_ASSERT(txQ_, "xrpl::ApplicationImp::getTxQ : non-null transaction queue");
+
730 return *txQ_;
+
731 }
-
728
- -
- -
731 {
-
732 XRPL_ASSERT(
- -
734 "xrpl::ApplicationImp::getRelationalDatabase : non-null "
-
735 "relational database");
-
736 return *relationalDatabase_;
-
737 }
+
732
+ +
+ +
735 {
+
736 XRPL_ASSERT(
+ +
738 "xrpl::ApplicationImp::getRelationalDatabase : non-null "
+
739 "relational database");
+
740 return *relationalDatabase_;
+
741 }
-
738
- -
-
740 getWalletDB() override
-
741 {
-
742 XRPL_ASSERT(mWalletDB, "xrpl::ApplicationImp::getWalletDB : non-null wallet database");
-
743 return *mWalletDB;
-
744 }
+
742
+ +
+
744 getWalletDB() override
+
745 {
+
746 XRPL_ASSERT(mWalletDB, "xrpl::ApplicationImp::getWalletDB : non-null wallet database");
+
747 return *mWalletDB;
+
748 }
-
745
-
746 bool
-
747 serverOkay(std::string& reason) override;
-
748
- -
750 journal(std::string const& name) override;
-
751
-
752 //--------------------------------------------------------------------------
-
753
-
754 bool
-
- -
756 {
-
757 XRPL_ASSERT(
-
758 mWalletDB.get() == nullptr,
-
759 "xrpl::ApplicationImp::initRelationalDatabase : null wallet "
-
760 "database");
-
761
-
762 try
-
763 {
- +
749
+
750 bool
+
751 serverOkay(std::string& reason) override;
+
752
+ +
754 journal(std::string const& name) override;
+
755
+
756 //--------------------------------------------------------------------------
+
757
+
758 bool
+
+ +
760 {
+
761 XRPL_ASSERT(
+
762 mWalletDB.get() == nullptr,
+
763 "xrpl::ApplicationImp::initRelationalDatabase : null wallet "
+
764 "database");
765
-
766 // wallet database
- -
768 setup.useGlobalPragma = false;
+
766 try
+
767 {
+
769
- -
771 }
-
772 catch (std::exception const& e)
-
773 {
-
774 JLOG(m_journal.fatal()) << "Failed to initialize SQL databases: " << e.what();
-
775 return false;
-
776 }
-
777
-
778 return true;
-
779 }
+
770 // wallet database
+ +
772 setup.useGlobalPragma = false;
+
773
+ +
775 }
+
776 catch (std::exception const& e)
+
777 {
+
778 JLOG(m_journal.fatal()) << "Failed to initialize SQL databases: " << e.what();
+
779 return false;
+
780 }
+
781
+
782 return true;
+
783 }
-
780
-
781 bool
-
- -
783 {
-
784 if (config_->doImport)
-
785 {
-
786 auto j = logs_->journal("NodeObject");
-
787 NodeStore::DummyScheduler dummyScheduler;
- - -
790 dummyScheduler,
-
791 0,
- -
793 j);
-
794
-
795 JLOG(j.warn()) << "Starting node import from '" << source->getName() << "' to '" << m_nodeStore->getName()
-
796 << "'.";
-
797
-
798 using namespace std::chrono;
-
799 auto const start = steady_clock::now();
-
800
-
801 m_nodeStore->importDatabase(*source);
-
802
-
803 auto const elapsed = duration_cast<seconds>(steady_clock::now() - start);
-
804 JLOG(j.warn()) << "Node import from '" << source->getName() << "' took " << elapsed.count() << " seconds.";
-
805 }
+
784
+
785 bool
+
+ +
787 {
+
788 if (config_->doImport)
+
789 {
+
790 auto j = logs_->journal("NodeObject");
+
791 NodeStore::DummyScheduler dummyScheduler;
+ + +
794 dummyScheduler,
+
795 0,
+ +
797 j);
+
798
+
799 JLOG(j.warn()) << "Starting node import from '" << source->getName() << "' to '" << m_nodeStore->getName()
+
800 << "'.";
+
801
+
802 using namespace std::chrono;
+
803 auto const start = steady_clock::now();
+
804
+
805 m_nodeStore->importDatabase(*source);
806
-
807 return true;
-
808 }
+
807 auto const elapsed = duration_cast<seconds>(steady_clock::now() - start);
+
808 JLOG(j.warn()) << "Node import from '" << source->getName() << "' took " << elapsed.count() << " seconds.";
+
809 }
+
810
+
811 return true;
+
812 }
-
809
-
810 //--------------------------------------------------------------------------
-
811 //
-
812 // PropertyStream
-
813 //
-
814
-
815 void
-
- -
817 {
-
818 }
+
813
+
814 //--------------------------------------------------------------------------
+
815 //
+
816 // PropertyStream
+
817 //
+
818
+
819 void
+
+ +
821 {
+
822 }
-
819
-
820 //--------------------------------------------------------------------------
-
821
-
822 void
-
- -
824 {
-
825 // Only start the timer if waitHandlerCounter_ is not yet joined.
-
826 if (auto optionalCountedHandler = waitHandlerCounter_.wrap([this](boost::system::error_code const& e) {
-
827 if (e.value() == boost::system::errc::success)
-
828 {
-
829 m_jobQueue->addJob(jtSWEEP, "sweep", [this]() { doSweep(); });
-
830 }
-
831 // Recover as best we can if an unexpected error occurs.
-
832 if (e.value() != boost::system::errc::success && e.value() != boost::asio::error::operation_aborted)
-
833 {
-
834 // Try again later and hope for the best.
-
835 JLOG(m_journal.error()) << "Sweep timer got error '" << e.message() << "'. Restarting timer.";
-
836 setSweepTimer();
-
837 }
-
838 }))
-
839 {
-
840 using namespace std::chrono;
-
841 sweepTimer_.expires_after(
-
842 seconds{config_->SWEEP_INTERVAL.value_or(config_->getValueFor(SizedItem::sweepInterval))});
-
843 sweepTimer_.async_wait(std::move(*optionalCountedHandler));
-
844 }
-
845 }
+
823
+
824 //--------------------------------------------------------------------------
+
825
+
826 void
+
+ +
828 {
+
829 // Only start the timer if waitHandlerCounter_ is not yet joined.
+
830 if (auto optionalCountedHandler = waitHandlerCounter_.wrap([this](boost::system::error_code const& e) {
+
831 if (e.value() == boost::system::errc::success)
+
832 {
+
833 m_jobQueue->addJob(jtSWEEP, "sweep", [this]() { doSweep(); });
+
834 }
+
835 // Recover as best we can if an unexpected error occurs.
+
836 if (e.value() != boost::system::errc::success && e.value() != boost::asio::error::operation_aborted)
+
837 {
+
838 // Try again later and hope for the best.
+
839 JLOG(m_journal.error()) << "Sweep timer got error '" << e.message() << "'. Restarting timer.";
+
840 setSweepTimer();
+
841 }
+
842 }))
+
843 {
+
844 using namespace std::chrono;
+
845 sweepTimer_.expires_after(
+
846 seconds{config_->SWEEP_INTERVAL.value_or(config_->getValueFor(SizedItem::sweepInterval))});
+
847 sweepTimer_.async_wait(std::move(*optionalCountedHandler));
+
848 }
+
849 }
-
846
-
847 void
-
- -
849 {
-
850 // Only start the timer if waitHandlerCounter_ is not yet joined.
-
851 if (auto optionalCountedHandler = waitHandlerCounter_.wrap([this](boost::system::error_code const& e) {
-
852 if (e.value() == boost::system::errc::success)
-
853 {
-
854 crypto_prng().mix_entropy();
-
855 setEntropyTimer();
-
856 }
-
857 // Recover as best we can if an unexpected error occurs.
-
858 if (e.value() != boost::system::errc::success && e.value() != boost::asio::error::operation_aborted)
-
859 {
-
860 // Try again later and hope for the best.
-
861 JLOG(m_journal.error()) << "Entropy timer got error '" << e.message() << "'. Restarting timer.";
-
862 setEntropyTimer();
-
863 }
-
864 }))
-
865 {
-
866 using namespace std::chrono_literals;
-
867 entropyTimer_.expires_after(5min);
-
868 entropyTimer_.async_wait(std::move(*optionalCountedHandler));
-
869 }
-
870 }
+
850
+
851 void
+
+ +
853 {
+
854 // Only start the timer if waitHandlerCounter_ is not yet joined.
+
855 if (auto optionalCountedHandler = waitHandlerCounter_.wrap([this](boost::system::error_code const& e) {
+
856 if (e.value() == boost::system::errc::success)
+
857 {
+
858 crypto_prng().mix_entropy();
+
859 setEntropyTimer();
+
860 }
+
861 // Recover as best we can if an unexpected error occurs.
+
862 if (e.value() != boost::system::errc::success && e.value() != boost::asio::error::operation_aborted)
+
863 {
+
864 // Try again later and hope for the best.
+
865 JLOG(m_journal.error()) << "Entropy timer got error '" << e.message() << "'. Restarting timer.";
+
866 setEntropyTimer();
+
867 }
+
868 }))
+
869 {
+
870 using namespace std::chrono_literals;
+
871 entropyTimer_.expires_after(5min);
+
872 entropyTimer_.async_wait(std::move(*optionalCountedHandler));
+
873 }
+
874 }
-
871
-
872 void
-
- -
874 {
-
875 XRPL_ASSERT(relationalDatabase_, "xrpl::ApplicationImp::doSweep : non-null relational database");
-
876 if (!config_->standalone() && !relationalDatabase_->transactionDbHasSpace(*config_))
-
877 {
-
878 signalStop("Out of transaction DB space");
-
879 }
-
880
-
881 // VFALCO NOTE Does the order of calls matter?
-
882 // VFALCO TODO fix the dependency inversion using an observer,
-
883 // have listeners register for "onSweep ()" notification.
+
875
+
876 void
+
+ +
878 {
+
879 XRPL_ASSERT(relationalDatabase_, "xrpl::ApplicationImp::doSweep : non-null relational database");
+
880 if (!config_->standalone() && !relationalDatabase_->transactionDbHasSpace(*config_))
+
881 {
+
882 signalStop("Out of transaction DB space");
+
883 }
884
-
885 {
-
886 std::shared_ptr<FullBelowCache const> const fullBelowCache = nodeFamily_.getFullBelowCache();
-
887
-
888 std::shared_ptr<TreeNodeCache const> const treeNodeCache = nodeFamily_.getTreeNodeCache();
-
889
-
890 std::size_t const oldFullBelowSize = fullBelowCache->size();
-
891 std::size_t const oldTreeNodeSize = treeNodeCache->size();
-
892
-
893 nodeFamily_.sweep();
-
894
-
895 JLOG(m_journal.debug()) << "NodeFamily::FullBelowCache sweep. Size before: " << oldFullBelowSize
-
896 << "; size after: " << fullBelowCache->size();
-
897
-
898 JLOG(m_journal.debug()) << "NodeFamily::TreeNodeCache sweep. Size before: " << oldTreeNodeSize
-
899 << "; size after: " << treeNodeCache->size();
-
900 }
-
901 {
-
902 TaggedCache<uint256, Transaction> const& masterTxCache = getMasterTransaction().getCache();
-
903
-
904 std::size_t const oldMasterTxSize = masterTxCache.size();
-
905
-
906 getMasterTransaction().sweep();
+
885 // VFALCO NOTE Does the order of calls matter?
+
886 // VFALCO TODO fix the dependency inversion using an observer,
+
887 // have listeners register for "onSweep ()" notification.
+
888
+
889 {
+
890 std::shared_ptr<FullBelowCache const> const fullBelowCache = nodeFamily_.getFullBelowCache();
+
891
+
892 std::shared_ptr<TreeNodeCache const> const treeNodeCache = nodeFamily_.getTreeNodeCache();
+
893
+
894 std::size_t const oldFullBelowSize = fullBelowCache->size();
+
895 std::size_t const oldTreeNodeSize = treeNodeCache->size();
+
896
+
897 nodeFamily_.sweep();
+
898
+
899 JLOG(m_journal.debug()) << "NodeFamily::FullBelowCache sweep. Size before: " << oldFullBelowSize
+
900 << "; size after: " << fullBelowCache->size();
+
901
+
902 JLOG(m_journal.debug()) << "NodeFamily::TreeNodeCache sweep. Size before: " << oldTreeNodeSize
+
903 << "; size after: " << treeNodeCache->size();
+
904 }
+
905 {
+
906 TaggedCache<uint256, Transaction> const& masterTxCache = getMasterTransaction().getCache();
907
-
908 JLOG(m_journal.debug()) << "MasterTransaction sweep. Size before: " << oldMasterTxSize
-
909 << "; size after: " << masterTxCache.size();
-
910 }
-
911 {
-
912 std::size_t const oldLedgerMasterCacheSize = getLedgerMaster().getFetchPackCacheSize();
-
913
-
914 getLedgerMaster().sweep();
-
915
-
916 JLOG(m_journal.debug()) << "LedgerMaster sweep. Size before: " << oldLedgerMasterCacheSize
-
917 << "; size after: " << getLedgerMaster().getFetchPackCacheSize();
-
918 }
-
919 {
-
920 // NodeCache == TaggedCache<SHAMapHash, Blob>
-
921 std::size_t const oldTempNodeCacheSize = getTempNodeCache().size();
-
922
-
923 getTempNodeCache().sweep();
-
924
-
925 JLOG(m_journal.debug()) << "TempNodeCache sweep. Size before: " << oldTempNodeCacheSize
-
926 << "; size after: " << getTempNodeCache().size();
-
927 }
-
928 {
-
929 std::size_t const oldCurrentCacheSize = getValidations().sizeOfCurrentCache();
-
930 std::size_t const oldSizeSeqEnforcesSize = getValidations().sizeOfSeqEnforcersCache();
-
931 std::size_t const oldByLedgerSize = getValidations().sizeOfByLedgerCache();
-
932 std::size_t const oldBySequenceSize = getValidations().sizeOfBySequenceCache();
-
933
-
934 getValidations().expire(m_journal);
-
935
-
936 JLOG(m_journal.debug()) << "Validations Current expire. Size before: " << oldCurrentCacheSize
-
937 << "; size after: " << getValidations().sizeOfCurrentCache();
-
938
-
939 JLOG(m_journal.debug()) << "Validations SeqEnforcer expire. Size before: " << oldSizeSeqEnforcesSize
-
940 << "; size after: " << getValidations().sizeOfSeqEnforcersCache();
-
941
-
942 JLOG(m_journal.debug()) << "Validations ByLedger expire. Size before: " << oldByLedgerSize
-
943 << "; size after: " << getValidations().sizeOfByLedgerCache();
-
944
-
945 JLOG(m_journal.debug()) << "Validations BySequence expire. Size before: " << oldBySequenceSize
-
946 << "; size after: " << getValidations().sizeOfBySequenceCache();
-
947 }
-
948 {
-
949 std::size_t const oldInboundLedgersSize = getInboundLedgers().cacheSize();
-
950
-
951 getInboundLedgers().sweep();
-
952
-
953 JLOG(m_journal.debug()) << "InboundLedgers sweep. Size before: " << oldInboundLedgersSize
-
954 << "; size after: " << getInboundLedgers().cacheSize();
-
955 }
-
956 {
-
957 size_t const oldTasksSize = getLedgerReplayer().tasksSize();
-
958 size_t const oldDeltasSize = getLedgerReplayer().deltasSize();
-
959 size_t const oldSkipListsSize = getLedgerReplayer().skipListsSize();
-
960
-
961 getLedgerReplayer().sweep();
-
962
-
963 JLOG(m_journal.debug()) << "LedgerReplayer tasks sweep. Size before: " << oldTasksSize
-
964 << "; size after: " << getLedgerReplayer().tasksSize();
-
965
-
966 JLOG(m_journal.debug()) << "LedgerReplayer deltas sweep. Size before: " << oldDeltasSize
-
967 << "; size after: " << getLedgerReplayer().deltasSize();
-
968
-
969 JLOG(m_journal.debug()) << "LedgerReplayer skipLists sweep. Size before: " << oldSkipListsSize
-
970 << "; size after: " << getLedgerReplayer().skipListsSize();
-
971 }
-
972 {
-
973 std::size_t const oldAcceptedLedgerSize = m_acceptedLedgerCache.size();
-
974
-
975 m_acceptedLedgerCache.sweep();
-
976
-
977 JLOG(m_journal.debug()) << "AcceptedLedgerCache sweep. Size before: " << oldAcceptedLedgerSize
-
978 << "; size after: " << m_acceptedLedgerCache.size();
-
979 }
-
980 {
-
981 std::size_t const oldCachedSLEsSize = cachedSLEs_.size();
-
982
-
983 cachedSLEs_.sweep();
-
984
-
985 JLOG(m_journal.debug()) << "CachedSLEs sweep. Size before: " << oldCachedSLEsSize
-
986 << "; size after: " << cachedSLEs_.size();
-
987 }
+
908 std::size_t const oldMasterTxSize = masterTxCache.size();
+
909
+
910 getMasterTransaction().sweep();
+
911
+
912 JLOG(m_journal.debug()) << "MasterTransaction sweep. Size before: " << oldMasterTxSize
+
913 << "; size after: " << masterTxCache.size();
+
914 }
+
915 {
+
916 std::size_t const oldLedgerMasterCacheSize = getLedgerMaster().getFetchPackCacheSize();
+
917
+
918 getLedgerMaster().sweep();
+
919
+
920 JLOG(m_journal.debug()) << "LedgerMaster sweep. Size before: " << oldLedgerMasterCacheSize
+
921 << "; size after: " << getLedgerMaster().getFetchPackCacheSize();
+
922 }
+
923 {
+
924 // NodeCache == TaggedCache<SHAMapHash, Blob>
+
925 std::size_t const oldTempNodeCacheSize = getTempNodeCache().size();
+
926
+
927 getTempNodeCache().sweep();
+
928
+
929 JLOG(m_journal.debug()) << "TempNodeCache sweep. Size before: " << oldTempNodeCacheSize
+
930 << "; size after: " << getTempNodeCache().size();
+
931 }
+
932 {
+
933 std::size_t const oldCurrentCacheSize = getValidations().sizeOfCurrentCache();
+
934 std::size_t const oldSizeSeqEnforcesSize = getValidations().sizeOfSeqEnforcersCache();
+
935 std::size_t const oldByLedgerSize = getValidations().sizeOfByLedgerCache();
+
936 std::size_t const oldBySequenceSize = getValidations().sizeOfBySequenceCache();
+
937
+
938 getValidations().expire(m_journal);
+
939
+
940 JLOG(m_journal.debug()) << "Validations Current expire. Size before: " << oldCurrentCacheSize
+
941 << "; size after: " << getValidations().sizeOfCurrentCache();
+
942
+
943 JLOG(m_journal.debug()) << "Validations SeqEnforcer expire. Size before: " << oldSizeSeqEnforcesSize
+
944 << "; size after: " << getValidations().sizeOfSeqEnforcersCache();
+
945
+
946 JLOG(m_journal.debug()) << "Validations ByLedger expire. Size before: " << oldByLedgerSize
+
947 << "; size after: " << getValidations().sizeOfByLedgerCache();
+
948
+
949 JLOG(m_journal.debug()) << "Validations BySequence expire. Size before: " << oldBySequenceSize
+
950 << "; size after: " << getValidations().sizeOfBySequenceCache();
+
951 }
+
952 {
+
953 std::size_t const oldInboundLedgersSize = getInboundLedgers().cacheSize();
+
954
+
955 getInboundLedgers().sweep();
+
956
+
957 JLOG(m_journal.debug()) << "InboundLedgers sweep. Size before: " << oldInboundLedgersSize
+
958 << "; size after: " << getInboundLedgers().cacheSize();
+
959 }
+
960 {
+
961 size_t const oldTasksSize = getLedgerReplayer().tasksSize();
+
962 size_t const oldDeltasSize = getLedgerReplayer().deltasSize();
+
963 size_t const oldSkipListsSize = getLedgerReplayer().skipListsSize();
+
964
+
965 getLedgerReplayer().sweep();
+
966
+
967 JLOG(m_journal.debug()) << "LedgerReplayer tasks sweep. Size before: " << oldTasksSize
+
968 << "; size after: " << getLedgerReplayer().tasksSize();
+
969
+
970 JLOG(m_journal.debug()) << "LedgerReplayer deltas sweep. Size before: " << oldDeltasSize
+
971 << "; size after: " << getLedgerReplayer().deltasSize();
+
972
+
973 JLOG(m_journal.debug()) << "LedgerReplayer skipLists sweep. Size before: " << oldSkipListsSize
+
974 << "; size after: " << getLedgerReplayer().skipListsSize();
+
975 }
+
976 {
+
977 std::size_t const oldAcceptedLedgerSize = m_acceptedLedgerCache.size();
+
978
+
979 m_acceptedLedgerCache.sweep();
+
980
+
981 JLOG(m_journal.debug()) << "AcceptedLedgerCache sweep. Size before: " << oldAcceptedLedgerSize
+
982 << "; size after: " << m_acceptedLedgerCache.size();
+
983 }
+
984 {
+
985 std::size_t const oldCachedSLEsSize = cachedSLEs_.size();
+
986
+
987 cachedSLEs_.sweep();
988
-
989 // Set timer to do another sweep later.
-
990 setSweepTimer();
-
991 }
-
+
989 JLOG(m_journal.debug()) << "CachedSLEs sweep. Size before: " << oldCachedSLEsSize
+
990 << "; size after: " << cachedSLEs_.size();
+
991 }
992
- -
- -
995 {
-
996 return maxDisallowedLedger_;
-
997 }
+
993 // Set timer to do another sweep later.
+
994 setSweepTimer();
+
995 }
-
998
-
999 virtual std::optional<uint256> const&
-
-
1000 trapTxID() const override
-
1001 {
-
1002 return trapTxID_;
-
1003 }
+
996
+ +
+ +
999 {
+
1000 return maxDisallowedLedger_;
+
1001 }
-
1004
-
1005private:
-
1006 // For a newly-started validator, this is the greatest persisted ledger
-
1007 // and new validations must be greater than this.
-
1008 std::atomic<LedgerIndex> maxDisallowedLedger_{0};
-
1009
-
1010 void
-
1011 startGenesisLedger();
-
1012
- -
1014 getLastFullLedger();
-
1015
- -
1017 loadLedgerFromFile(std::string const& ledgerID);
-
1018
-
1019 bool
-
1020 loadOldLedger(std::string const& ledgerID, bool replay, bool isFilename, std::optional<uint256> trapTxID);
-
1021
-
1022 void
-
1023 setMaxDisallowedLedger();
-
1024
- -
-
1026 app() override
-
1027 {
-
1028 return *this;
-
1029 }
+
1002
+
1003 virtual std::optional<uint256> const&
+
+
1004 trapTxID() const override
+
1005 {
+
1006 return trapTxID_;
+
1007 }
-
1030};
+
1008
+
1009private:
+
1010 // For a newly-started validator, this is the greatest persisted ledger
+
1011 // and new validations must be greater than this.
+
1012 std::atomic<LedgerIndex> maxDisallowedLedger_{0};
+
1013
+
1014 void
+
1015 startGenesisLedger();
+
1016
+ +
1018 getLastFullLedger();
+
1019
+ +
1021 loadLedgerFromFile(std::string const& ledgerID);
+
1022
+
1023 bool
+
1024 loadOldLedger(std::string const& ledgerID, bool replay, bool isFilename, std::optional<uint256> trapTxID);
+
1025
+
1026 void
+
1027 setMaxDisallowedLedger();
+
1028
+ +
+
1030 app() override
+
1031 {
+
1032 return *this;
+
1033 }
-
1031
-
1032//------------------------------------------------------------------------------
-
1033
-
1034// TODO Break this up into smaller, more digestible initialization segments.
-
1035bool
-
-
1036ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
-
1037{
-
1038 // We want to intercept CTRL-C and the standard termination signal SIGTERM
-
1039 // and terminate the process. This handler will NEVER be invoked twice.
-
1040 //
-
1041 // Note that async_wait is "one-shot": for each call, the handler will be
-
1042 // invoked exactly once, either when one of the registered signals in the
-
1043 // signal set occurs or the signal set is cancelled. Subsequent signals are
-
1044 // effectively ignored (technically, they are queued up, waiting for a call
-
1045 // to async_wait).
-
1046 m_signals.add(SIGINT);
-
1047 m_signals.add(SIGTERM);
-
1048 m_signals.async_wait([this](boost::system::error_code const& ec, int signum) {
-
1049 // Indicates the signal handler has been aborted; do nothing
-
1050 if (ec == boost::asio::error::operation_aborted)
-
1051 return;
-
1052
-
1053 JLOG(m_journal.info()) << "Received signal " << signum;
-
1054
-
1055 if (signum == SIGTERM || signum == SIGINT)
-
1056 signalStop("Signal: " + to_string(signum));
-
1057 });
+
1034};
+
+
1035
+
1036//------------------------------------------------------------------------------
+
1037
+
1038// TODO Break this up into smaller, more digestible initialization segments.
+
1039bool
+
+
1040ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
+
1041{
+
1042 // We want to intercept CTRL-C and the standard termination signal SIGTERM
+
1043 // and terminate the process. This handler will NEVER be invoked twice.
+
1044 //
+
1045 // Note that async_wait is "one-shot": for each call, the handler will be
+
1046 // invoked exactly once, either when one of the registered signals in the
+
1047 // signal set occurs or the signal set is cancelled. Subsequent signals are
+
1048 // effectively ignored (technically, they are queued up, waiting for a call
+
1049 // to async_wait).
+
1050 m_signals.add(SIGINT);
+
1051 m_signals.add(SIGTERM);
+
1052 m_signals.async_wait([this](boost::system::error_code const& ec, int signum) {
+
1053 // Indicates the signal handler has been aborted; do nothing
+
1054 if (ec == boost::asio::error::operation_aborted)
+
1055 return;
+
1056
+
1057 JLOG(m_journal.info()) << "Received signal " << signum;
1058
-
1059 auto debug_log = config_->getDebugLogFile();
-
1060
-
1061 if (!debug_log.empty())
-
1062 {
-
1063 // Let debug messages go to the file but only WARNING or higher to
-
1064 // regular output (unless verbose)
-
1065
-
1066 if (!logs_->open(debug_log))
-
1067 std::cerr << "Can't open log file " << debug_log << '\n';
-
1068
-
1069 using namespace beast::severities;
-
1070 if (logs_->threshold() > kDebug)
-
1071 logs_->threshold(kDebug);
-
1072 }
-
1073
-
1074 JLOG(m_journal.info()) << "Process starting: " << BuildInfo::getFullVersionString()
-
1075 << ", Instance Cookie: " << instanceCookie_;
-
1076
-
1077 if (numberOfThreads(*config_) < 2)
-
1078 {
-
1079 JLOG(m_journal.warn()) << "Limited to a single I/O service thread by "
-
1080 "system configuration.";
-
1081 }
-
1082
-
1083 // Optionally turn off logging to console.
-
1084 logs_->silent(config_->silent());
-
1085
-
1086 if (!initRelationalDatabase() || !initNodeStore())
-
1087 return false;
-
1088
-
1089 if (!peerReservations_->load(getWalletDB()))
-
1090 {
-
1091 JLOG(m_journal.fatal()) << "Cannot find peer reservations!";
-
1092 return false;
-
1093 }
-
1094
-
1095 if (validatorKeys_.keys)
-
1096 setMaxDisallowedLedger();
-
1097
-
1098 // Configure the amendments the server supports
-
1099 {
-
1100 auto const supported = []() {
-
1101 auto const& amendments = detail::supportedAmendments();
- -
1103 supported.reserve(amendments.size());
-
1104 for (auto const& [a, vote] : amendments)
-
1105 {
-
1106 auto const f = xrpl::getRegisteredFeature(a);
-
1107 XRPL_ASSERT(f, "xrpl::ApplicationImp::setup : registered feature");
-
1108 if (f)
-
1109 supported.emplace_back(a, *f, vote);
-
1110 }
-
1111 return supported;
-
1112 }();
-
1113 Section const& downVoted = config_->section(SECTION_VETO_AMENDMENTS);
-
1114
-
1115 Section const& upVoted = config_->section(SECTION_AMENDMENTS);
-
1116
-
1117 m_amendmentTable = make_AmendmentTable(
-
1118 *this, config().AMENDMENT_MAJORITY_TIME, supported, upVoted, downVoted, logs_->journal("Amendments"));
-
1119 }
+
1059 if (signum == SIGTERM || signum == SIGINT)
+
1060 signalStop("Signal: " + to_string(signum));
+
1061 });
+
1062
+
1063 auto debug_log = config_->getDebugLogFile();
+
1064
+
1065 if (!debug_log.empty())
+
1066 {
+
1067 // Let debug messages go to the file but only WARNING or higher to
+
1068 // regular output (unless verbose)
+
1069
+
1070 if (!logs_->open(debug_log))
+
1071 std::cerr << "Can't open log file " << debug_log << '\n';
+
1072
+
1073 using namespace beast::severities;
+
1074 if (logs_->threshold() > kDebug)
+
1075 logs_->threshold(kDebug);
+
1076 }
+
1077
+
1078 JLOG(m_journal.info()) << "Process starting: " << BuildInfo::getFullVersionString()
+
1079 << ", Instance Cookie: " << instanceCookie_;
+
1080
+
1081 if (numberOfThreads(*config_) < 2)
+
1082 {
+
1083 JLOG(m_journal.warn()) << "Limited to a single I/O service thread by "
+
1084 "system configuration.";
+
1085 }
+
1086
+
1087 // Optionally turn off logging to console.
+
1088 logs_->silent(config_->silent());
+
1089
+
1090 if (!initRelationalDatabase() || !initNodeStore())
+
1091 return false;
+
1092
+
1093 if (!peerReservations_->load(getWalletDB()))
+
1094 {
+
1095 JLOG(m_journal.fatal()) << "Cannot find peer reservations!";
+
1096 return false;
+
1097 }
+
1098
+
1099 if (validatorKeys_.keys)
+
1100 setMaxDisallowedLedger();
+
1101
+
1102 // Configure the amendments the server supports
+
1103 {
+
1104 auto const supported = []() {
+
1105 auto const& amendments = detail::supportedAmendments();
+ +
1107 supported.reserve(amendments.size());
+
1108 for (auto const& [a, vote] : amendments)
+
1109 {
+
1110 auto const f = xrpl::getRegisteredFeature(a);
+
1111 XRPL_ASSERT(f, "xrpl::ApplicationImp::setup : registered feature");
+
1112 if (f)
+
1113 supported.emplace_back(a, *f, vote);
+
1114 }
+
1115 return supported;
+
1116 }();
+
1117 Section const& downVoted = config_->section(SECTION_VETO_AMENDMENTS);
+
1118
+
1119 Section const& upVoted = config_->section(SECTION_AMENDMENTS);
1120
-
1121 Pathfinder::initPathTable();
-
1122
-
1123 auto const startUp = config_->START_UP;
-
1124 JLOG(m_journal.debug()) << "startUp: " << startUp;
-
1125 if (startUp == StartUpType::FRESH)
-
1126 {
-
1127 JLOG(m_journal.info()) << "Starting new Ledger";
-
1128
-
1129 startGenesisLedger();
-
1130 }
-
1131 else if (startUp == StartUpType::LOAD || startUp == StartUpType::LOAD_FILE || startUp == StartUpType::REPLAY)
-
1132 {
-
1133 JLOG(m_journal.info()) << "Loading specified Ledger";
-
1134
-
1135 if (!loadOldLedger(
-
1136 config_->START_LEDGER,
-
1137 startUp == StartUpType::REPLAY,
-
1138 startUp == StartUpType::LOAD_FILE,
-
1139 config_->TRAP_TX_HASH))
-
1140 {
-
1141 JLOG(m_journal.error()) << "The specified ledger could not be loaded.";
-
1142 if (config_->FAST_LOAD)
-
1143 {
-
1144 // Fall back to syncing from the network, such as
-
1145 // when there's no existing data.
-
1146 startGenesisLedger();
-
1147 }
-
1148 else
-
1149 {
-
1150 return false;
+
1121 m_amendmentTable = make_AmendmentTable(
+
1122 *this, config().AMENDMENT_MAJORITY_TIME, supported, upVoted, downVoted, logs_->journal("Amendments"));
+
1123 }
+
1124
+
1125 Pathfinder::initPathTable();
+
1126
+
1127 auto const startUp = config_->START_UP;
+
1128 JLOG(m_journal.debug()) << "startUp: " << startUp;
+
1129 if (startUp == StartUpType::FRESH)
+
1130 {
+
1131 JLOG(m_journal.info()) << "Starting new Ledger";
+
1132
+
1133 startGenesisLedger();
+
1134 }
+
1135 else if (startUp == StartUpType::LOAD || startUp == StartUpType::LOAD_FILE || startUp == StartUpType::REPLAY)
+
1136 {
+
1137 JLOG(m_journal.info()) << "Loading specified Ledger";
+
1138
+
1139 if (!loadOldLedger(
+
1140 config_->START_LEDGER,
+
1141 startUp == StartUpType::REPLAY,
+
1142 startUp == StartUpType::LOAD_FILE,
+
1143 config_->TRAP_TX_HASH))
+
1144 {
+
1145 JLOG(m_journal.error()) << "The specified ledger could not be loaded.";
+
1146 if (config_->FAST_LOAD)
+
1147 {
+
1148 // Fall back to syncing from the network, such as
+
1149 // when there's no existing data.
+
1150 startGenesisLedger();
1151 }
-
1152 }
-
1153 }
-
1154 else if (startUp == StartUpType::NETWORK)
-
1155 {
-
1156 // This should probably become the default once we have a stable
-
1157 // network.
-
1158 if (!config_->standalone())
-
1159 m_networkOPs->setNeedNetworkLedger();
-
1160
-
1161 startGenesisLedger();
-
1162 }
-
1163 else
-
1164 {
+
1152 else
+
1153 {
+
1154 return false;
+
1155 }
+
1156 }
+
1157 }
+
1158 else if (startUp == StartUpType::NETWORK)
+
1159 {
+
1160 // This should probably become the default once we have a stable
+
1161 // network.
+
1162 if (!config_->standalone())
+
1163 m_networkOPs->setNeedNetworkLedger();
+
1164
1165 startGenesisLedger();
1166 }
-
1167
-
1168 if (auto const& forcedRange = config().FORCED_LEDGER_RANGE_PRESENT)
-
1169 {
-
1170 m_ledgerMaster->setLedgerRangePresent(forcedRange->first, forcedRange->second);
-
1171 }
-
1172
-
1173 m_orderBookDB->setup(getLedgerMaster().getCurrentLedger());
-
1174
-
1175 nodeIdentity_ = getNodeIdentity(*this, cmdline);
+
1167 else
+
1168 {
+
1169 startGenesisLedger();
+
1170 }
+
1171
+
1172 if (auto const& forcedRange = config().FORCED_LEDGER_RANGE_PRESENT)
+
1173 {
+
1174 m_ledgerMaster->setLedgerRangePresent(forcedRange->first, forcedRange->second);
+
1175 }
1176
-
1177 if (!cluster_->load(config().section(SECTION_CLUSTER_NODES)))
-
1178 {
-
1179 JLOG(m_journal.fatal()) << "Invalid entry in cluster configuration.";
-
1180 return false;
-
1181 }
-
1182
-
1183 {
-
1184 if (validatorKeys_.configInvalid())
-
1185 return false;
+
1177 m_orderBookDB->setup(getLedgerMaster().getCurrentLedger());
+
1178
+
1179 nodeIdentity_ = getNodeIdentity(*this, cmdline);
+
1180
+
1181 if (!cluster_->load(config().section(SECTION_CLUSTER_NODES)))
+
1182 {
+
1183 JLOG(m_journal.fatal()) << "Invalid entry in cluster configuration.";
+
1184 return false;
+
1185 }
1186
-
1187 if (!validatorManifests_->load(
-
1188 getWalletDB(),
-
1189 "ValidatorManifests",
-
1190 validatorKeys_.manifest,
-
1191 config().section(SECTION_VALIDATOR_KEY_REVOCATION).values()))
-
1192 {
-
1193 JLOG(m_journal.fatal()) << "Invalid configured validator manifest.";
-
1194 return false;
-
1195 }
-
1196
-
1197 publisherManifests_->load(getWalletDB(), "PublisherManifests");
-
1198
-
1199 // It is possible to have a valid ValidatorKeys object without
-
1200 // setting the signingKey or masterKey. This occurs if the
-
1201 // configuration file does not have either
-
1202 // SECTION_VALIDATOR_TOKEN or SECTION_VALIDATION_SEED section.
-
1203
-
1204 // masterKey for the configuration-file specified validator keys
-
1205 std::optional<PublicKey> localSigningKey;
-
1206 if (validatorKeys_.keys)
-
1207 localSigningKey = validatorKeys_.keys->publicKey;
-
1208
-
1209 // Setup trusted validators
-
1210 if (!validators_->load(
-
1211 localSigningKey,
-
1212 config().section(SECTION_VALIDATORS).values(),
-
1213 config().section(SECTION_VALIDATOR_LIST_KEYS).values(),
-
1214 config().VALIDATOR_LIST_THRESHOLD))
-
1215 {
-
1216 JLOG(m_journal.fatal()) << "Invalid entry in validator configuration.";
-
1217 return false;
-
1218 }
-
1219 }
-
1220
-
1221 if (!validatorSites_->load(config().section(SECTION_VALIDATOR_LIST_SITES).values()))
-
1222 {
-
1223 JLOG(m_journal.fatal()) << "Invalid entry in [" << SECTION_VALIDATOR_LIST_SITES << "]";
-
1224 return false;
-
1225 }
-
1226
-
1227 // Tell the AmendmentTable who the trusted validators are.
-
1228 m_amendmentTable->trustChanged(validators_->getQuorumKeys().second);
-
1229
-
1230 //----------------------------------------------------------------------
-
1231 //
-
1232 // Server
-
1233 //
+
1187 {
+
1188 if (validatorKeys_.configInvalid())
+
1189 return false;
+
1190
+
1191 if (!validatorManifests_->load(
+
1192 getWalletDB(),
+
1193 "ValidatorManifests",
+
1194 validatorKeys_.manifest,
+
1195 config().section(SECTION_VALIDATOR_KEY_REVOCATION).values()))
+
1196 {
+
1197 JLOG(m_journal.fatal()) << "Invalid configured validator manifest.";
+
1198 return false;
+
1199 }
+
1200
+
1201 publisherManifests_->load(getWalletDB(), "PublisherManifests");
+
1202
+
1203 // It is possible to have a valid ValidatorKeys object without
+
1204 // setting the signingKey or masterKey. This occurs if the
+
1205 // configuration file does not have either
+
1206 // SECTION_VALIDATOR_TOKEN or SECTION_VALIDATION_SEED section.
+
1207
+
1208 // masterKey for the configuration-file specified validator keys
+
1209 std::optional<PublicKey> localSigningKey;
+
1210 if (validatorKeys_.keys)
+
1211 localSigningKey = validatorKeys_.keys->publicKey;
+
1212
+
1213 // Setup trusted validators
+
1214 if (!validators_->load(
+
1215 localSigningKey,
+
1216 config().section(SECTION_VALIDATORS).values(),
+
1217 config().section(SECTION_VALIDATOR_LIST_KEYS).values(),
+
1218 config().VALIDATOR_LIST_THRESHOLD))
+
1219 {
+
1220 JLOG(m_journal.fatal()) << "Invalid entry in validator configuration.";
+
1221 return false;
+
1222 }
+
1223 }
+
1224
+
1225 if (!validatorSites_->load(config().section(SECTION_VALIDATOR_LIST_SITES).values()))
+
1226 {
+
1227 JLOG(m_journal.fatal()) << "Invalid entry in [" << SECTION_VALIDATOR_LIST_SITES << "]";
+
1228 return false;
+
1229 }
+
1230
+
1231 // Tell the AmendmentTable who the trusted validators are.
+
1232 m_amendmentTable->trustChanged(validators_->getQuorumKeys().second);
+
1233
1234 //----------------------------------------------------------------------
-
1235
-
1236 // VFALCO NOTE Unfortunately, in stand-alone mode some code still
-
1237 // foolishly calls overlay(). When this is fixed we can
-
1238 // move the instantiation inside a conditional:
-
1239 //
-
1240 // if (!config_.standalone())
-
1241 overlay_ = make_Overlay(
-
1242 *this,
-
1243 setup_Overlay(*config_),
-
1244 *serverHandler_,
-
1245 *m_resourceManager,
-
1246 *m_resolver,
-
1247 get_io_context(),
-
1248 *config_,
-
1249 m_collectorManager->collector());
-
1250 add(*overlay_); // add to PropertyStream
-
1251
-
1252 // start first consensus round
-
1253 if (!m_networkOPs->beginConsensus(m_ledgerMaster->getClosedLedger()->header().hash, {}))
-
1254 {
-
1255 JLOG(m_journal.fatal()) << "Unable to start consensus";
-
1256 return false;
-
1257 }
-
1258
-
1259 {
-
1260 try
-
1261 {
-
1262 auto setup = setup_ServerHandler(*config_, beast::logstream{m_journal.error()});
-
1263 setup.makeContexts();
-
1264 serverHandler_->setup(setup, m_journal);
-
1265 fixConfigPorts(*config_, serverHandler_->endpoints());
-
1266 }
-
1267 catch (std::exception const& e)
-
1268 {
-
1269 if (auto stream = m_journal.fatal())
-
1270 {
-
1271 stream << "Unable to setup server handler";
-
1272 if (std::strlen(e.what()) > 0)
-
1273 stream << ": " << e.what();
-
1274 }
-
1275 return false;
-
1276 }
-
1277 }
-
1278
-
1279 // Begin connecting to network.
-
1280 if (!config_->standalone())
-
1281 {
-
1282 // Should this message be here, conceptually? In theory this sort
-
1283 // of message, if displayed, should be displayed from PeerFinder.
-
1284 if (config_->PEER_PRIVATE && config_->IPS_FIXED.empty())
-
1285 {
-
1286 JLOG(m_journal.warn()) << "No outbound peer connections will be made";
-
1287 }
-
1288
-
1289 // VFALCO NOTE the state timer resets the deadlock detector.
-
1290 //
-
1291 m_networkOPs->setStateTimer();
-
1292 }
-
1293 else
-
1294 {
-
1295 JLOG(m_journal.warn()) << "Running in standalone mode";
-
1296
-
1297 m_networkOPs->setStandAlone();
-
1298 }
-
1299
-
1300 if (config_->canSign())
-
1301 {
-
1302 JLOG(m_journal.warn()) << "*** The server is configured to allow the "
-
1303 "'sign' and 'sign_for'";
-
1304 JLOG(m_journal.warn()) << "*** commands. These commands have security "
-
1305 "implications and have";
-
1306 JLOG(m_journal.warn()) << "*** been deprecated. They will be removed "
-
1307 "in a future release of";
-
1308 JLOG(m_journal.warn()) << "*** rippled.";
-
1309 JLOG(m_journal.warn()) << "*** If you do not use them to sign "
-
1310 "transactions please edit your";
-
1311 JLOG(m_journal.warn()) << "*** configuration file and remove the [enable_signing] stanza.";
-
1312 JLOG(m_journal.warn()) << "*** If you do use them to sign transactions "
-
1313 "please migrate to a";
-
1314 JLOG(m_journal.warn()) << "*** standalone signing solution as soon as possible.";
-
1315 }
-
1316
-
1317 //
-
1318 // Execute start up rpc commands.
-
1319 //
-
1320 for (auto cmd : config_->section(SECTION_RPC_STARTUP).lines())
-
1321 {
-
1322 Json::Reader jrReader;
-
1323 Json::Value jvCommand;
-
1324
-
1325 if (!jrReader.parse(cmd, jvCommand))
-
1326 {
-
1327 JLOG(m_journal.fatal()) << "Couldn't parse entry in [" << SECTION_RPC_STARTUP << "]: '" << cmd;
-
1328 }
-
1329
-
1330 if (!config_->quiet())
-
1331 {
-
1332 JLOG(m_journal.fatal()) << "Startup RPC: " << jvCommand << std::endl;
-
1333 }
-
1334
-
1335 Resource::Charge loadType = Resource::feeReferenceRPC;
- -
1337 RPC::JsonContext context{
-
1338 {journal("RPCHandler"),
-
1339 *this,
-
1340 loadType,
-
1341 getOPs(),
-
1342 getLedgerMaster(),
-
1343 c,
-
1344 Role::ADMIN,
-
1345 {},
-
1346 {},
-
1347 RPC::apiMaximumSupportedVersion},
-
1348 jvCommand};
-
1349
-
1350 Json::Value jvResult;
-
1351 RPC::doCommand(context, jvResult);
-
1352
-
1353 if (!config_->quiet())
-
1354 {
-
1355 JLOG(m_journal.fatal()) << "Result: " << jvResult << std::endl;
-
1356 }
-
1357 }
-
1358
-
1359 validatorSites_->start();
-
1360
-
1361 return true;
-
1362}
-
-
1363
-
1364void
-
-
1365ApplicationImp::start(bool withTimers)
-
1366{
-
1367 JLOG(m_journal.info()) << "Application starting. Version is " << BuildInfo::getVersionString();
-
1368
-
1369 if (withTimers)
-
1370 {
-
1371 setSweepTimer();
-
1372 setEntropyTimer();
-
1373 }
-
1374
-
1375 m_io_latency_sampler.start();
-
1376 m_resolver->start();
-
1377 m_loadManager->start();
-
1378 m_shaMapStore->start();
-
1379 if (overlay_)
-
1380 overlay_->start();
-
1381
-
1382 if (grpcServer_->start())
-
1383 fixConfigPorts(*config_, {{SECTION_PORT_GRPC, grpcServer_->getEndpoint()}});
-
1384
-
1385 ledgerCleaner_->start();
-
1386 perfLog_->start();
-
1387}
+
1235 //
+
1236 // Server
+
1237 //
+
1238 //----------------------------------------------------------------------
+
1239
+
1240 // VFALCO NOTE Unfortunately, in stand-alone mode some code still
+
1241 // foolishly calls overlay(). When this is fixed we can
+
1242 // move the instantiation inside a conditional:
+
1243 //
+
1244 // if (!config_.standalone())
+
1245 overlay_ = make_Overlay(
+
1246 *this,
+
1247 setup_Overlay(*config_),
+
1248 *serverHandler_,
+
1249 *m_resourceManager,
+
1250 *m_resolver,
+
1251 get_io_context(),
+
1252 *config_,
+
1253 m_collectorManager->collector());
+
1254 add(*overlay_); // add to PropertyStream
+
1255
+
1256 // start first consensus round
+
1257 if (!m_networkOPs->beginConsensus(m_ledgerMaster->getClosedLedger()->header().hash, {}))
+
1258 {
+
1259 JLOG(m_journal.fatal()) << "Unable to start consensus";
+
1260 return false;
+
1261 }
+
1262
+
1263 {
+
1264 try
+
1265 {
+
1266 auto setup = setup_ServerHandler(*config_, beast::logstream{m_journal.error()});
+
1267 setup.makeContexts();
+
1268 serverHandler_->setup(setup, m_journal);
+
1269 fixConfigPorts(*config_, serverHandler_->endpoints());
+
1270 }
+
1271 catch (std::exception const& e)
+
1272 {
+
1273 if (auto stream = m_journal.fatal())
+
1274 {
+
1275 stream << "Unable to setup server handler";
+
1276 if (std::strlen(e.what()) > 0)
+
1277 stream << ": " << e.what();
+
1278 }
+
1279 return false;
+
1280 }
+
1281 }
+
1282
+
1283 // Begin connecting to network.
+
1284 if (!config_->standalone())
+
1285 {
+
1286 // Should this message be here, conceptually? In theory this sort
+
1287 // of message, if displayed, should be displayed from PeerFinder.
+
1288 if (config_->PEER_PRIVATE && config_->IPS_FIXED.empty())
+
1289 {
+
1290 JLOG(m_journal.warn()) << "No outbound peer connections will be made";
+
1291 }
+
1292
+
1293 // VFALCO NOTE the state timer resets the deadlock detector.
+
1294 //
+
1295 m_networkOPs->setStateTimer();
+
1296 }
+
1297 else
+
1298 {
+
1299 JLOG(m_journal.warn()) << "Running in standalone mode";
+
1300
+
1301 m_networkOPs->setStandAlone();
+
1302 }
+
1303
+
1304 if (config_->canSign())
+
1305 {
+
1306 JLOG(m_journal.warn()) << "*** The server is configured to allow the "
+
1307 "'sign' and 'sign_for'";
+
1308 JLOG(m_journal.warn()) << "*** commands. These commands have security "
+
1309 "implications and have";
+
1310 JLOG(m_journal.warn()) << "*** been deprecated. They will be removed "
+
1311 "in a future release of";
+
1312 JLOG(m_journal.warn()) << "*** rippled.";
+
1313 JLOG(m_journal.warn()) << "*** If you do not use them to sign "
+
1314 "transactions please edit your";
+
1315 JLOG(m_journal.warn()) << "*** configuration file and remove the [enable_signing] stanza.";
+
1316 JLOG(m_journal.warn()) << "*** If you do use them to sign transactions "
+
1317 "please migrate to a";
+
1318 JLOG(m_journal.warn()) << "*** standalone signing solution as soon as possible.";
+
1319 }
+
1320
+
1321 //
+
1322 // Execute start up rpc commands.
+
1323 //
+
1324 for (auto cmd : config_->section(SECTION_RPC_STARTUP).lines())
+
1325 {
+
1326 Json::Reader jrReader;
+
1327 Json::Value jvCommand;
+
1328
+
1329 if (!jrReader.parse(cmd, jvCommand))
+
1330 {
+
1331 JLOG(m_journal.fatal()) << "Couldn't parse entry in [" << SECTION_RPC_STARTUP << "]: '" << cmd;
+
1332 }
+
1333
+
1334 if (!config_->quiet())
+
1335 {
+
1336 JLOG(m_journal.fatal()) << "Startup RPC: " << jvCommand << std::endl;
+
1337 }
+
1338
+
1339 Resource::Charge loadType = Resource::feeReferenceRPC;
+ +
1341 RPC::JsonContext context{
+
1342 {journal("RPCHandler"),
+
1343 *this,
+
1344 loadType,
+
1345 getOPs(),
+
1346 getLedgerMaster(),
+
1347 c,
+
1348 Role::ADMIN,
+
1349 {},
+
1350 {},
+
1351 RPC::apiMaximumSupportedVersion},
+
1352 jvCommand};
+
1353
+
1354 Json::Value jvResult;
+
1355 RPC::doCommand(context, jvResult);
+
1356
+
1357 if (!config_->quiet())
+
1358 {
+
1359 JLOG(m_journal.fatal()) << "Result: " << jvResult << std::endl;
+
1360 }
+
1361 }
+
1362
+
1363 validatorSites_->start();
+
1364
+
1365 return true;
+
1366}
+
1367
+
1368void
+
+
1369ApplicationImp::start(bool withTimers)
+
1370{
+
1371 JLOG(m_journal.info()) << "Application starting. Version is " << BuildInfo::getVersionString();
+
1372
+
1373 if (withTimers)
+
1374 {
+
1375 setSweepTimer();
+
1376 setEntropyTimer();
+
1377 }
+
1378
+
1379 m_io_latency_sampler.start();
+
1380 m_resolver->start();
+
1381 m_loadManager->start();
+
1382 m_shaMapStore->start();
+
1383 if (overlay_)
+
1384 overlay_->start();
+
1385
+
1386 if (grpcServer_->start())
+
1387 fixConfigPorts(*config_, {{SECTION_PORT_GRPC, grpcServer_->getEndpoint()}});
1388
-
1389void
-
-
1390ApplicationImp::run()
-
1391{
-
1392 if (!config_->standalone())
-
1393 {
-
1394 // VFALCO NOTE This seems unnecessary. If we properly refactor the load
-
1395 // manager then the stall detector can just always be
-
1396 // "armed"
-
1397 //
-
1398 getLoadManager().activateStallDetector();
-
1399 }
-
1400
-
1401 isTimeToStop.wait(false, std::memory_order_relaxed);
-
1402
-
1403 JLOG(m_journal.debug()) << "Application stopping";
+
1389 ledgerCleaner_->start();
+
1390 perfLog_->start();
+
1391}
+
+
1392
+
1393void
+
+
1394ApplicationImp::run()
+
1395{
+
1396 if (!config_->standalone())
+
1397 {
+
1398 // VFALCO NOTE This seems unnecessary. If we properly refactor the load
+
1399 // manager then the stall detector can just always be
+
1400 // "armed"
+
1401 //
+
1402 getLoadManager().activateStallDetector();
+
1403 }
1404
-
1405 m_io_latency_sampler.cancel_async();
+
1405 isTimeToStop.wait(false, std::memory_order_relaxed);
1406
-
1407 // VFALCO Enormous hack, we have to force the probe to cancel
-
1408 // before we stop the io_context queue or else it never
-
1409 // unblocks in its destructor. The fix is to make all
-
1410 // io_objects gracefully handle exit so that we can
-
1411 // naturally return from io_context::run() instead of
-
1412 // forcing a call to io_context::stop()
-
1413 m_io_latency_sampler.cancel();
-
1414
-
1415 m_resolver->stop_async();
-
1416
-
1417 // NIKB This is a hack - we need to wait for the resolver to
-
1418 // stop. before we stop the io_server_queue or weird
-
1419 // things will happen.
-
1420 m_resolver->stop();
-
1421
-
1422 {
-
1423 try
-
1424 {
-
1425 sweepTimer_.cancel();
-
1426 }
-
1427 catch (boost::system::system_error const& e)
+
1407 JLOG(m_journal.debug()) << "Application stopping";
+
1408
+
1409 m_io_latency_sampler.cancel_async();
+
1410
+
1411 // VFALCO Enormous hack, we have to force the probe to cancel
+
1412 // before we stop the io_context queue or else it never
+
1413 // unblocks in its destructor. The fix is to make all
+
1414 // io_objects gracefully handle exit so that we can
+
1415 // naturally return from io_context::run() instead of
+
1416 // forcing a call to io_context::stop()
+
1417 m_io_latency_sampler.cancel();
+
1418
+
1419 m_resolver->stop_async();
+
1420
+
1421 // NIKB This is a hack - we need to wait for the resolver to
+
1422 // stop. before we stop the io_server_queue or weird
+
1423 // things will happen.
+
1424 m_resolver->stop();
+
1425
+
1426 {
+
1427 try
1428 {
-
1429 JLOG(m_journal.error()) << "Application: sweepTimer cancel error: " << e.what();
+
1429 sweepTimer_.cancel();
1430 }
-
1431
-
1432 try
-
1433 {
-
1434 entropyTimer_.cancel();
-
1435 }
-
1436 catch (boost::system::system_error const& e)
+
1431 catch (boost::system::system_error const& e)
+
1432 {
+
1433 JLOG(m_journal.error()) << "Application: sweepTimer cancel error: " << e.what();
+
1434 }
+
1435
+
1436 try
1437 {
-
1438 JLOG(m_journal.error()) << "Application: entropyTimer cancel error: " << e.what();
+
1438 entropyTimer_.cancel();
1439 }
-
1440 }
-
1441
-
1442 // Make sure that any waitHandlers pending in our timers are done
-
1443 // before we declare ourselves stopped.
-
1444 using namespace std::chrono_literals;
+
1440 catch (boost::system::system_error const& e)
+
1441 {
+
1442 JLOG(m_journal.error()) << "Application: entropyTimer cancel error: " << e.what();
+
1443 }
+
1444 }
1445
-
1446 waitHandlerCounter_.join("Application", 1s, m_journal);
-
1447
-
1448 mValidations.flush();
+
1446 // Make sure that any waitHandlers pending in our timers are done
+
1447 // before we declare ourselves stopped.
+
1448 using namespace std::chrono_literals;
1449
-
1450 validatorSites_->stop();
+
1450 waitHandlerCounter_.join("Application", 1s, m_journal);
1451
-
1452 // TODO Store manifests in manifests.sqlite instead of wallet.db
-
1453 validatorManifests_->save(
-
1454 getWalletDB(), "ValidatorManifests", [this](PublicKey const& pubKey) { return validators().listed(pubKey); });
+
1452 mValidations.flush();
+
1453
+
1454 validatorSites_->stop();
1455
-
1456 publisherManifests_->save(getWalletDB(), "PublisherManifests", [this](PublicKey const& pubKey) {
-
1457 return validators().trustedPublisher(pubKey);
-
1458 });
+
1456 // TODO Store manifests in manifests.sqlite instead of wallet.db
+
1457 validatorManifests_->save(
+
1458 getWalletDB(), "ValidatorManifests", [this](PublicKey const& pubKey) { return validators().listed(pubKey); });
1459
-
1460 // The order of these stop calls is delicate.
-
1461 // Re-ordering them risks undefined behavior.
-
1462 m_loadManager->stop();
-
1463 m_shaMapStore->stop();
-
1464 m_jobQueue->stop();
-
1465 if (overlay_)
-
1466 overlay_->stop();
-
1467 grpcServer_->stop();
-
1468 m_networkOPs->stop();
-
1469 serverHandler_->stop();
-
1470 m_ledgerReplayer->stop();
-
1471 m_inboundTransactions->stop();
-
1472 m_inboundLedgers->stop();
-
1473 ledgerCleaner_->stop();
-
1474 m_nodeStore->stop();
-
1475 perfLog_->stop();
-
1476
-
1477 JLOG(m_journal.info()) << "Done.";
-
1478}
-
-
1479
-
1480void
-
-
1481ApplicationImp::signalStop(std::string msg)
-
1482{
-
1483 if (!isTimeToStop.test_and_set(std::memory_order_acquire))
-
1484 {
-
1485 if (msg.empty())
-
1486 JLOG(m_journal.warn()) << "Server stopping";
-
1487 else
-
1488 JLOG(m_journal.warn()) << "Server stopping: " << msg;
-
1489
-
1490 isTimeToStop.notify_all();
-
1491 }
-
1492}
+
1460 publisherManifests_->save(getWalletDB(), "PublisherManifests", [this](PublicKey const& pubKey) {
+
1461 return validators().trustedPublisher(pubKey);
+
1462 });
+
1463
+
1464 // The order of these stop calls is delicate.
+
1465 // Re-ordering them risks undefined behavior.
+
1466 m_loadManager->stop();
+
1467 m_shaMapStore->stop();
+
1468 m_jobQueue->stop();
+
1469 if (overlay_)
+
1470 overlay_->stop();
+
1471 grpcServer_->stop();
+
1472 m_networkOPs->stop();
+
1473 serverHandler_->stop();
+
1474 m_ledgerReplayer->stop();
+
1475 m_inboundTransactions->stop();
+
1476 m_inboundLedgers->stop();
+
1477 ledgerCleaner_->stop();
+
1478 m_nodeStore->stop();
+
1479 perfLog_->stop();
+
1480
+
1481 JLOG(m_journal.info()) << "Done.";
+
1482}
+
1483
+
1484void
+
+
1485ApplicationImp::signalStop(std::string msg)
+
1486{
+
1487 if (!isTimeToStop.test_and_set(std::memory_order_acquire))
+
1488 {
+
1489 if (msg.empty())
+
1490 JLOG(m_journal.warn()) << "Server stopping";
+
1491 else
+
1492 JLOG(m_journal.warn()) << "Server stopping: " << msg;
1493
-
1494bool
-
-
1495ApplicationImp::checkSigs() const
-
1496{
-
1497 return checkSigs_;
-
1498}
+
1494 isTimeToStop.notify_all();
+
1495 }
+
1496}
-
1499
-
1500void
-
-
1501ApplicationImp::checkSigs(bool check)
-
1502{
-
1503 checkSigs_ = check;
-
1504}
+
1497
+
1498bool
+
+
1499ApplicationImp::checkSigs() const
+
1500{
+
1501 return checkSigs_;
+
1502}
-
1505
-
1506bool
-
-
1507ApplicationImp::isStopping() const
-
1508{
-
1509 return isTimeToStop.test(std::memory_order_relaxed);
-
1510}
+
1503
+
1504void
+
+
1505ApplicationImp::checkSigs(bool check)
+
1506{
+
1507 checkSigs_ = check;
+
1508}
-
1511
-
1512int
-
-
1513ApplicationImp::fdRequired() const
-
1514{
-
1515 // Standard handles, config file, misc I/O etc:
-
1516 int needed = 128;
-
1517
-
1518 // 2x the configured peer limit for peer connections:
-
1519 if (overlay_)
-
1520 needed += 2 * overlay_->limit();
+
1509
+
1510bool
+
+
1511ApplicationImp::isStopping() const
+
1512{
+
1513 return isTimeToStop.test(std::memory_order_relaxed);
+
1514}
+
+
1515
+
1516int
+
+
1517ApplicationImp::fdRequired() const
+
1518{
+
1519 // Standard handles, config file, misc I/O etc:
+
1520 int needed = 128;
1521
-
1522 // the number of fds needed by the backend (internally
-
1523 // doubled if online delete is enabled).
-
1524 needed += std::max(5, m_shaMapStore->fdRequired());
+
1522 // 2x the configured peer limit for peer connections:
+
1523 if (overlay_)
+
1524 needed += 2 * overlay_->limit();
1525
-
1526 // One fd per incoming connection a port can accept, or
-
1527 // if no limit is set, assume it'll handle 256 clients.
-
1528 for (auto const& p : serverHandler_->setup().ports)
-
1529 needed += std::max(256, p.limit);
-
1530
-
1531 // The minimum number of file descriptors we need is 1024:
-
1532 return std::max(1024, needed);
-
1533}
-
+
1526 // the number of fds needed by the backend (internally
+
1527 // doubled if online delete is enabled).
+
1528 needed += std::max(5, m_shaMapStore->fdRequired());
+
1529
+
1530 // One fd per incoming connection a port can accept, or
+
1531 // if no limit is set, assume it'll handle 256 clients.
+
1532 for (auto const& p : serverHandler_->setup().ports)
+
1533 needed += std::max(256, p.limit);
1534
-
1535//------------------------------------------------------------------------------
-
1536
-
1537void
-
-
1538ApplicationImp::startGenesisLedger()
-
1539{
-
1540 std::vector<uint256> const initialAmendments =
-
1541 (config_->START_UP == StartUpType::FRESH) ? m_amendmentTable->getDesired() : std::vector<uint256>{};
-
1542
-
1543 std::shared_ptr<Ledger> const genesis =
-
1544 std::make_shared<Ledger>(create_genesis, *config_, initialAmendments, nodeFamily_);
-
1545 m_ledgerMaster->storeLedger(genesis);
+
1535 // The minimum number of file descriptors we need is 1024:
+
1536 return std::max(1024, needed);
+
1537}
+
+
1538
+
1539//------------------------------------------------------------------------------
+
1540
+
1541void
+
+
1542ApplicationImp::startGenesisLedger()
+
1543{
+
1544 std::vector<uint256> const initialAmendments =
+
1545 (config_->START_UP == StartUpType::FRESH) ? m_amendmentTable->getDesired() : std::vector<uint256>{};
1546
-
1547 auto const next = std::make_shared<Ledger>(*genesis, timeKeeper().closeTime());
-
1548 next->updateSkipList();
-
1549 XRPL_ASSERT(
-
1550 next->header().seq < XRP_LEDGER_EARLIEST_FEES || next->read(keylet::fees()),
-
1551 "xrpl::ApplicationImp::startGenesisLedger : valid ledger fees");
-
1552 next->setImmutable();
-
1553 openLedger_.emplace(next, cachedSLEs_, logs_->journal("OpenLedger"));
-
1554 m_ledgerMaster->storeLedger(next);
-
1555 m_ledgerMaster->switchLCL(next);
-
1556}
+
1547 std::shared_ptr<Ledger> const genesis =
+
1548 std::make_shared<Ledger>(create_genesis, *config_, initialAmendments, nodeFamily_);
+
1549 m_ledgerMaster->storeLedger(genesis);
+
1550
+
1551 auto const next = std::make_shared<Ledger>(*genesis, timeKeeper().closeTime());
+
1552 next->updateSkipList();
+
1553 XRPL_ASSERT(
+
1554 next->header().seq < XRP_LEDGER_EARLIEST_FEES || next->read(keylet::fees()),
+
1555 "xrpl::ApplicationImp::startGenesisLedger : valid ledger fees");
+
1556 next->setImmutable();
+
1557 openLedger_.emplace(next, cachedSLEs_, logs_->journal("OpenLedger"));
+
1558 m_ledgerMaster->storeLedger(next);
+
1559 m_ledgerMaster->switchLCL(next);
+
1560}
-
1557
- -
-
1559ApplicationImp::getLastFullLedger()
-
1560{
-
1561 auto j = journal("Ledger");
-
1562
-
1563 try
-
1564 {
-
1565 auto const [ledger, seq, hash] = getLatestLedger(*this);
+
1561
+ +
+
1563ApplicationImp::getLastFullLedger()
+
1564{
+
1565 auto j = journal("Ledger");
1566
-
1567 if (!ledger)
-
1568 return ledger;
-
1569
-
1570 XRPL_ASSERT(
-
1571 ledger->header().seq < XRP_LEDGER_EARLIEST_FEES || ledger->read(keylet::fees()),
-
1572 "xrpl::ApplicationImp::getLastFullLedger : valid ledger fees");
-
1573 ledger->setImmutable();
-
1574
-
1575 if (getLedgerMaster().haveLedger(seq))
-
1576 ledger->setValidated();
-
1577
-
1578 if (ledger->header().hash == hash)
-
1579 {
-
1580 JLOG(j.trace()) << "Loaded ledger: " << hash;
-
1581 return ledger;
-
1582 }
-
1583
-
1584 if (auto stream = j.error())
-
1585 {
-
1586 stream << "Failed on ledger";
-
1587 Json::Value p;
-
1588 addJson(p, {*ledger, nullptr, LedgerFill::full});
-
1589 stream << p;
-
1590 }
-
1591
-
1592 return {};
-
1593 }
-
1594 catch (SHAMapMissingNode const& mn)
-
1595 {
-
1596 JLOG(j.warn()) << "Ledger in database: " << mn.what();
-
1597 return {};
-
1598 }
-
1599}
+
1567 try
+
1568 {
+
1569 auto const [ledger, seq, hash] = getLatestLedger(*this);
+
1570
+
1571 if (!ledger)
+
1572 return ledger;
+
1573
+
1574 XRPL_ASSERT(
+
1575 ledger->header().seq < XRP_LEDGER_EARLIEST_FEES || ledger->read(keylet::fees()),
+
1576 "xrpl::ApplicationImp::getLastFullLedger : valid ledger fees");
+
1577 ledger->setImmutable();
+
1578
+
1579 if (getLedgerMaster().haveLedger(seq))
+
1580 ledger->setValidated();
+
1581
+
1582 if (ledger->header().hash == hash)
+
1583 {
+
1584 JLOG(j.trace()) << "Loaded ledger: " << hash;
+
1585 return ledger;
+
1586 }
+
1587
+
1588 if (auto stream = j.error())
+
1589 {
+
1590 stream << "Failed on ledger";
+
1591 Json::Value p;
+
1592 addJson(p, {*ledger, nullptr, LedgerFill::full});
+
1593 stream << p;
+
1594 }
+
1595
+
1596 return {};
+
1597 }
+
1598 catch (SHAMapMissingNode const& mn)
+
1599 {
+
1600 JLOG(j.warn()) << "Ledger in database: " << mn.what();
+
1601 return {};
+
1602 }
+
1603}
-
1600
- -
-
1602ApplicationImp::loadLedgerFromFile(std::string const& name)
-
1603{
-
1604 try
-
1605 {
-
1606 std::ifstream ledgerFile(name, std::ios::in);
-
1607
-
1608 if (!ledgerFile)
-
1609 {
-
1610 JLOG(m_journal.fatal()) << "Unable to open file '" << name << "'";
-
1611 return nullptr;
-
1612 }
-
1613
-
1614 Json::Reader reader;
-
1615 Json::Value jLedger;
-
1616
-
1617 if (!reader.parse(ledgerFile, jLedger))
-
1618 {
-
1619 JLOG(m_journal.fatal()) << "Unable to parse ledger JSON";
-
1620 return nullptr;
-
1621 }
-
1622
- -
1624
-
1625 // accept a wrapped ledger
-
1626 if (ledger.get().isMember("result"))
-
1627 ledger = ledger.get()["result"];
+
1604
+ +
+
1606ApplicationImp::loadLedgerFromFile(std::string const& name)
+
1607{
+
1608 try
+
1609 {
+
1610 std::ifstream ledgerFile(name, std::ios::in);
+
1611
+
1612 if (!ledgerFile)
+
1613 {
+
1614 JLOG(m_journal.fatal()) << "Unable to open file '" << name << "'";
+
1615 return nullptr;
+
1616 }
+
1617
+
1618 Json::Reader reader;
+
1619 Json::Value jLedger;
+
1620
+
1621 if (!reader.parse(ledgerFile, jLedger))
+
1622 {
+
1623 JLOG(m_journal.fatal()) << "Unable to parse ledger JSON";
+
1624 return nullptr;
+
1625 }
+
1626
+
1628
-
1629 if (ledger.get().isMember("ledger"))
-
1630 ledger = ledger.get()["ledger"];
-
1631
-
1632 std::uint32_t seq = 1;
-
1633 auto closeTime = timeKeeper().closeTime();
-
1634 using namespace std::chrono_literals;
-
1635 auto closeTimeResolution = 30s;
-
1636 bool closeTimeEstimated = false;
-
1637 std::uint64_t totalDrops = 0;
-
1638
-
1639 if (ledger.get().isMember("accountState"))
-
1640 {
-
1641 if (ledger.get().isMember(jss::ledger_index))
-
1642 {
-
1643 seq = ledger.get()[jss::ledger_index].asUInt();
-
1644 }
-
1645
-
1646 if (ledger.get().isMember("close_time"))
-
1647 {
-
1648 using tp = NetClock::time_point;
-
1649 using d = tp::duration;
-
1650 closeTime = tp{d{ledger.get()["close_time"].asUInt()}};
-
1651 }
-
1652 if (ledger.get().isMember("close_time_resolution"))
-
1653 {
-
1654 using namespace std::chrono;
-
1655 closeTimeResolution = seconds{ledger.get()["close_time_resolution"].asUInt()};
-
1656 }
-
1657 if (ledger.get().isMember("close_time_estimated"))
-
1658 {
-
1659 closeTimeEstimated = ledger.get()["close_time_estimated"].asBool();
+
1629 // accept a wrapped ledger
+
1630 if (ledger.get().isMember("result"))
+
1631 ledger = ledger.get()["result"];
+
1632
+
1633 if (ledger.get().isMember("ledger"))
+
1634 ledger = ledger.get()["ledger"];
+
1635
+
1636 std::uint32_t seq = 1;
+
1637 auto closeTime = timeKeeper().closeTime();
+
1638 using namespace std::chrono_literals;
+
1639 auto closeTimeResolution = 30s;
+
1640 bool closeTimeEstimated = false;
+
1641 std::uint64_t totalDrops = 0;
+
1642
+
1643 if (ledger.get().isMember("accountState"))
+
1644 {
+
1645 if (ledger.get().isMember(jss::ledger_index))
+
1646 {
+
1647 seq = ledger.get()[jss::ledger_index].asUInt();
+
1648 }
+
1649
+
1650 if (ledger.get().isMember("close_time"))
+
1651 {
+
1652 using tp = NetClock::time_point;
+
1653 using d = tp::duration;
+
1654 closeTime = tp{d{ledger.get()["close_time"].asUInt()}};
+
1655 }
+
1656 if (ledger.get().isMember("close_time_resolution"))
+
1657 {
+
1658 using namespace std::chrono;
+
1659 closeTimeResolution = seconds{ledger.get()["close_time_resolution"].asUInt()};
1660 }
-
1661 if (ledger.get().isMember("total_coins"))
+
1661 if (ledger.get().isMember("close_time_estimated"))
1662 {
-
1663 totalDrops = beast::lexicalCastThrow<std::uint64_t>(ledger.get()["total_coins"].asString());
+
1663 closeTimeEstimated = ledger.get()["close_time_estimated"].asBool();
1664 }
-
1665
-
1666 ledger = ledger.get()["accountState"];
-
1667 }
-
1668
-
1669 if (!ledger.get().isArrayOrNull())
-
1670 {
-
1671 JLOG(m_journal.fatal()) << "State nodes must be an array";
-
1672 return nullptr;
-
1673 }
-
1674
-
1675 auto loadLedger = std::make_shared<Ledger>(seq, closeTime, *config_, nodeFamily_);
-
1676 loadLedger->setTotalDrops(totalDrops);
-
1677
-
1678 for (Json::UInt index = 0; index < ledger.get().size(); ++index)
-
1679 {
-
1680 Json::Value& entry = ledger.get()[index];
+
1665 if (ledger.get().isMember("total_coins"))
+
1666 {
+
1667 totalDrops = beast::lexicalCastThrow<std::uint64_t>(ledger.get()["total_coins"].asString());
+
1668 }
+
1669
+
1670 ledger = ledger.get()["accountState"];
+
1671 }
+
1672
+
1673 if (!ledger.get().isArrayOrNull())
+
1674 {
+
1675 JLOG(m_journal.fatal()) << "State nodes must be an array";
+
1676 return nullptr;
+
1677 }
+
1678
+
1679 auto loadLedger = std::make_shared<Ledger>(seq, closeTime, *config_, nodeFamily_);
+
1680 loadLedger->setTotalDrops(totalDrops);
1681
-
1682 if (!entry.isObjectOrNull())
-
1683 {
-
1684 JLOG(m_journal.fatal()) << "Invalid entry in ledger";
-
1685 return nullptr;
-
1686 }
-
1687
-
1688 uint256 uIndex;
-
1689
-
1690 if (!uIndex.parseHex(entry[jss::index].asString()))
-
1691 {
-
1692 JLOG(m_journal.fatal()) << "Invalid entry in ledger";
-
1693 return nullptr;
-
1694 }
-
1695
-
1696 entry.removeMember(jss::index);
-
1697
-
1698 STParsedJSONObject stp("sle", ledger.get()[index]);
+
1682 for (Json::UInt index = 0; index < ledger.get().size(); ++index)
+
1683 {
+
1684 Json::Value& entry = ledger.get()[index];
+
1685
+
1686 if (!entry.isObjectOrNull())
+
1687 {
+
1688 JLOG(m_journal.fatal()) << "Invalid entry in ledger";
+
1689 return nullptr;
+
1690 }
+
1691
+
1692 uint256 uIndex;
+
1693
+
1694 if (!uIndex.parseHex(entry[jss::index].asString()))
+
1695 {
+
1696 JLOG(m_journal.fatal()) << "Invalid entry in ledger";
+
1697 return nullptr;
+
1698 }
1699
-
1700 if (!stp.object || uIndex.isZero())
-
1701 {
-
1702 JLOG(m_journal.fatal()) << "Invalid entry in ledger";
-
1703 return nullptr;
-
1704 }
-
1705
-
1706 // VFALCO TODO This is the only place that
-
1707 // constructor is used, try to remove it
-
1708 STLedgerEntry sle(*stp.object, uIndex);
+
1700 entry.removeMember(jss::index);
+
1701
+
1702 STParsedJSONObject stp("sle", ledger.get()[index]);
+
1703
+
1704 if (!stp.object || uIndex.isZero())
+
1705 {
+
1706 JLOG(m_journal.fatal()) << "Invalid entry in ledger";
+
1707 return nullptr;
+
1708 }
1709
-
1710 if (!loadLedger->addSLE(sle))
-
1711 {
-
1712 JLOG(m_journal.fatal()) << "Couldn't add serialized ledger: " << uIndex;
-
1713 return nullptr;
-
1714 }
-
1715 }
-
1716
-
1717 loadLedger->stateMap().flushDirty(hotACCOUNT_NODE);
-
1718
-
1719 XRPL_ASSERT(
-
1720 loadLedger->header().seq < XRP_LEDGER_EARLIEST_FEES || loadLedger->read(keylet::fees()),
-
1721 "xrpl::ApplicationImp::loadLedgerFromFile : valid ledger fees");
-
1722 loadLedger->setAccepted(closeTime, closeTimeResolution, !closeTimeEstimated);
-
1723
-
1724 return loadLedger;
-
1725 }
-
1726 catch (std::exception const& x)
-
1727 {
-
1728 JLOG(m_journal.fatal()) << "Ledger contains invalid data: " << x.what();
-
1729 return nullptr;
-
1730 }
-
1731}
+
1710 // VFALCO TODO This is the only place that
+
1711 // constructor is used, try to remove it
+
1712 STLedgerEntry sle(*stp.object, uIndex);
+
1713
+
1714 if (!loadLedger->addSLE(sle))
+
1715 {
+
1716 JLOG(m_journal.fatal()) << "Couldn't add serialized ledger: " << uIndex;
+
1717 return nullptr;
+
1718 }
+
1719 }
+
1720
+
1721 loadLedger->stateMap().flushDirty(hotACCOUNT_NODE);
+
1722
+
1723 XRPL_ASSERT(
+
1724 loadLedger->header().seq < XRP_LEDGER_EARLIEST_FEES || loadLedger->read(keylet::fees()),
+
1725 "xrpl::ApplicationImp::loadLedgerFromFile : valid ledger fees");
+
1726 loadLedger->setAccepted(closeTime, closeTimeResolution, !closeTimeEstimated);
+
1727
+
1728 return loadLedger;
+
1729 }
+
1730 catch (std::exception const& x)
+
1731 {
+
1732 JLOG(m_journal.fatal()) << "Ledger contains invalid data: " << x.what();
+
1733 return nullptr;
+
1734 }
+
1735}
-
1732
-
1733bool
-
-
1734ApplicationImp::loadOldLedger(
-
1735 std::string const& ledgerID,
-
1736 bool replay,
-
1737 bool isFileName,
-
1738 std::optional<uint256> trapTxID)
-
1739{
-
1740 try
-
1741 {
-
1742 std::shared_ptr<Ledger const> loadLedger, replayLedger;
-
1743
-
1744 if (isFileName)
-
1745 {
-
1746 if (!ledgerID.empty())
-
1747 loadLedger = loadLedgerFromFile(ledgerID);
-
1748 }
-
1749 else if (ledgerID.length() == 64)
-
1750 {
-
1751 uint256 hash;
-
1752
-
1753 if (hash.parseHex(ledgerID))
-
1754 {
-
1755 loadLedger = loadByHash(hash, *this);
+
1736
+
1737bool
+
+
1738ApplicationImp::loadOldLedger(
+
1739 std::string const& ledgerID,
+
1740 bool replay,
+
1741 bool isFileName,
+
1742 std::optional<uint256> trapTxID)
+
1743{
+
1744 try
+
1745 {
+
1746 std::shared_ptr<Ledger const> loadLedger, replayLedger;
+
1747
+
1748 if (isFileName)
+
1749 {
+
1750 if (!ledgerID.empty())
+
1751 loadLedger = loadLedgerFromFile(ledgerID);
+
1752 }
+
1753 else if (ledgerID.length() == 64)
+
1754 {
+
1755 uint256 hash;
1756
-
1757 if (!loadLedger)
-
1758 {
-
1759 // Try to build the ledger from the back end
- -
1761 *this, hash, 0, InboundLedger::Reason::GENERIC, stopwatch(), make_DummyPeerSet(*this));
-
1762 if (il->checkLocal())
-
1763 loadLedger = il->getLedger();
-
1764 }
-
1765 }
-
1766 }
-
1767 else if (ledgerID.empty() || boost::iequals(ledgerID, "latest"))
-
1768 {
-
1769 loadLedger = getLastFullLedger();
+
1757 if (hash.parseHex(ledgerID))
+
1758 {
+
1759 loadLedger = loadByHash(hash, *this);
+
1760
+
1761 if (!loadLedger)
+
1762 {
+
1763 // Try to build the ledger from the back end
+ +
1765 *this, hash, 0, InboundLedger::Reason::GENERIC, stopwatch(), make_DummyPeerSet(*this));
+
1766 if (il->checkLocal())
+
1767 loadLedger = il->getLedger();
+
1768 }
+
1769 }
1770 }
-
1771 else
+
1771 else if (ledgerID.empty() || boost::iequals(ledgerID, "latest"))
1772 {
-
1773 // assume by sequence
-
1774 std::uint32_t index;
-
1775
-
1776 if (beast::lexicalCastChecked(index, ledgerID))
-
1777 loadLedger = loadByIndex(index, *this);
-
1778 }
+
1773 loadLedger = getLastFullLedger();
+
1774 }
+
1775 else
+
1776 {
+
1777 // assume by sequence
+
1778 std::uint32_t index;
1779
-
1780 if (!loadLedger)
-
1781 return false;
-
1782
-
1783 if (replay)
-
1784 {
-
1785 // Replay a ledger close with same prior ledger and transactions
+
1780 if (beast::lexicalCastChecked(index, ledgerID))
+
1781 loadLedger = loadByIndex(index, *this);
+
1782 }
+
1783
+
1784 if (!loadLedger)
+
1785 return false;
1786
-
1787 // this ledger holds the transactions we want to replay
-
1788 replayLedger = loadLedger;
-
1789
-
1790 JLOG(m_journal.info()) << "Loading parent ledger";
-
1791
-
1792 loadLedger = loadByHash(replayLedger->header().parentHash, *this);
-
1793 if (!loadLedger)
-
1794 {
-
1795 JLOG(m_journal.info()) << "Loading parent ledger from node store";
-
1796
-
1797 // Try to build the ledger from the back end
- -
1799 *this,
-
1800 replayLedger->header().parentHash,
-
1801 0,
-
1802 InboundLedger::Reason::GENERIC,
-
1803 stopwatch(),
-
1804 make_DummyPeerSet(*this));
-
1805
-
1806 if (il->checkLocal())
-
1807 loadLedger = il->getLedger();
-
1808
-
1809 if (!loadLedger)
-
1810 {
-
1811 // LCOV_EXCL_START
-
1812 JLOG(m_journal.fatal()) << "Replay ledger missing/damaged";
-
1813 UNREACHABLE(
-
1814 "xrpl::ApplicationImp::loadOldLedger : replay ledger "
-
1815 "missing/damaged");
-
1816 return false;
-
1817 // LCOV_EXCL_STOP
-
1818 }
-
1819 }
-
1820 }
-
1821 using namespace std::chrono_literals;
-
1822 using namespace date;
-
1823 static constexpr NetClock::time_point ledgerWarnTimePoint{
-
1824 sys_days{January / 1 / 2018} - sys_days{January / 1 / 2000}};
-
1825 if (loadLedger->header().closeTime < ledgerWarnTimePoint)
-
1826 {
-
1827 JLOG(m_journal.fatal()) << "\n\n*** WARNING ***\n"
-
1828 "You are replaying a ledger from before "
-
1829 << to_string(ledgerWarnTimePoint)
-
1830 << " UTC.\n"
-
1831 "This replay will not handle your ledger as it was "
-
1832 "originally "
-
1833 "handled.\nConsider running an earlier version of rippled "
-
1834 "to "
-
1835 "get the older rules.\n*** CONTINUING ***\n";
-
1836 }
-
1837
-
1838 JLOG(m_journal.info()) << "Loading ledger " << loadLedger->header().hash << " seq:" << loadLedger->header().seq;
-
1839
-
1840 if (loadLedger->header().accountHash.isZero())
-
1841 {
-
1842 // LCOV_EXCL_START
-
1843 JLOG(m_journal.fatal()) << "Ledger is empty.";
-
1844 UNREACHABLE("xrpl::ApplicationImp::loadOldLedger : ledger is empty");
-
1845 return false;
-
1846 // LCOV_EXCL_STOP
-
1847 }
-
1848
-
1849 if (!loadLedger->walkLedger(journal("Ledger"), true))
-
1850 {
-
1851 // LCOV_EXCL_START
-
1852 JLOG(m_journal.fatal()) << "Ledger is missing nodes.";
-
1853 UNREACHABLE(
-
1854 "xrpl::ApplicationImp::loadOldLedger : ledger is missing "
-
1855 "nodes");
-
1856 return false;
-
1857 // LCOV_EXCL_STOP
-
1858 }
-
1859
-
1860 if (!loadLedger->assertSensible(journal("Ledger")))
-
1861 {
-
1862 // LCOV_EXCL_START
-
1863 JLOG(m_journal.fatal()) << "Ledger is not sensible.";
-
1864 UNREACHABLE(
-
1865 "xrpl::ApplicationImp::loadOldLedger : ledger is not "
-
1866 "sensible");
-
1867 return false;
-
1868 // LCOV_EXCL_STOP
-
1869 }
-
1870
-
1871 m_ledgerMaster->setLedgerRangePresent(loadLedger->header().seq, loadLedger->header().seq);
-
1872
-
1873 m_ledgerMaster->switchLCL(loadLedger);
-
1874 loadLedger->setValidated();
-
1875 m_ledgerMaster->setFullLedger(loadLedger, true, false);
-
1876 openLedger_.emplace(loadLedger, cachedSLEs_, logs_->journal("OpenLedger"));
-
1877
-
1878 if (replay)
-
1879 {
-
1880 // inject transaction(s) from the replayLedger into our open ledger
-
1881 // and build replay structure
-
1882 auto replayData = std::make_unique<LedgerReplay>(loadLedger, replayLedger);
-
1883
-
1884 for (auto const& [_, tx] : replayData->orderedTxns())
-
1885 {
-
1886 (void)_;
-
1887 auto txID = tx->getTransactionID();
-
1888 if (trapTxID == txID)
-
1889 {
-
1890 trapTxID_ = txID;
-
1891 JLOG(m_journal.debug()) << "Trap transaction set: " << txID;
-
1892 }
-
1893
- -
1895 tx->add(*s);
-
1896
-
1897 forceValidity(getHashRouter(), txID, Validity::SigGoodOnly);
-
1898
-
1899 openLedger_->modify([&txID, &s](OpenView& view, beast::Journal j) {
-
1900 view.rawTxInsert(txID, std::move(s), nullptr);
-
1901 return true;
-
1902 });
-
1903 }
-
1904
-
1905 m_ledgerMaster->takeReplay(std::move(replayData));
-
1906
-
1907 if (trapTxID && !trapTxID_)
-
1908 {
-
1909 JLOG(m_journal.fatal()) << "Ledger " << replayLedger->header().seq
-
1910 << " does not contain the transaction hash " << *trapTxID;
-
1911 return false;
-
1912 }
-
1913 }
-
1914 }
-
1915 catch (SHAMapMissingNode const& mn)
-
1916 {
-
1917 JLOG(m_journal.fatal()) << "While loading specified ledger: " << mn.what();
-
1918 return false;
-
1919 }
-
1920 catch (boost::bad_lexical_cast&)
-
1921 {
-
1922 JLOG(m_journal.fatal()) << "Ledger specified '" << ledgerID << "' is not valid";
-
1923 return false;
-
1924 }
-
1925
-
1926 return true;
-
1927}
+
1787 if (replay)
+
1788 {
+
1789 // Replay a ledger close with same prior ledger and transactions
+
1790
+
1791 // this ledger holds the transactions we want to replay
+
1792 replayLedger = loadLedger;
+
1793
+
1794 JLOG(m_journal.info()) << "Loading parent ledger";
+
1795
+
1796 loadLedger = loadByHash(replayLedger->header().parentHash, *this);
+
1797 if (!loadLedger)
+
1798 {
+
1799 JLOG(m_journal.info()) << "Loading parent ledger from node store";
+
1800
+
1801 // Try to build the ledger from the back end
+ +
1803 *this,
+
1804 replayLedger->header().parentHash,
+
1805 0,
+
1806 InboundLedger::Reason::GENERIC,
+
1807 stopwatch(),
+
1808 make_DummyPeerSet(*this));
+
1809
+
1810 if (il->checkLocal())
+
1811 loadLedger = il->getLedger();
+
1812
+
1813 if (!loadLedger)
+
1814 {
+
1815 // LCOV_EXCL_START
+
1816 JLOG(m_journal.fatal()) << "Replay ledger missing/damaged";
+
1817 UNREACHABLE(
+
1818 "xrpl::ApplicationImp::loadOldLedger : replay ledger "
+
1819 "missing/damaged");
+
1820 return false;
+
1821 // LCOV_EXCL_STOP
+
1822 }
+
1823 }
+
1824 }
+
1825 using namespace std::chrono_literals;
+
1826 using namespace date;
+
1827 static constexpr NetClock::time_point ledgerWarnTimePoint{
+
1828 sys_days{January / 1 / 2018} - sys_days{January / 1 / 2000}};
+
1829 if (loadLedger->header().closeTime < ledgerWarnTimePoint)
+
1830 {
+
1831 JLOG(m_journal.fatal()) << "\n\n*** WARNING ***\n"
+
1832 "You are replaying a ledger from before "
+
1833 << to_string(ledgerWarnTimePoint)
+
1834 << " UTC.\n"
+
1835 "This replay will not handle your ledger as it was "
+
1836 "originally "
+
1837 "handled.\nConsider running an earlier version of rippled "
+
1838 "to "
+
1839 "get the older rules.\n*** CONTINUING ***\n";
+
1840 }
+
1841
+
1842 JLOG(m_journal.info()) << "Loading ledger " << loadLedger->header().hash << " seq:" << loadLedger->header().seq;
+
1843
+
1844 if (loadLedger->header().accountHash.isZero())
+
1845 {
+
1846 // LCOV_EXCL_START
+
1847 JLOG(m_journal.fatal()) << "Ledger is empty.";
+
1848 UNREACHABLE("xrpl::ApplicationImp::loadOldLedger : ledger is empty");
+
1849 return false;
+
1850 // LCOV_EXCL_STOP
+
1851 }
+
1852
+
1853 if (!loadLedger->walkLedger(journal("Ledger"), true))
+
1854 {
+
1855 // LCOV_EXCL_START
+
1856 JLOG(m_journal.fatal()) << "Ledger is missing nodes.";
+
1857 UNREACHABLE(
+
1858 "xrpl::ApplicationImp::loadOldLedger : ledger is missing "
+
1859 "nodes");
+
1860 return false;
+
1861 // LCOV_EXCL_STOP
+
1862 }
+
1863
+
1864 if (!loadLedger->assertSensible(journal("Ledger")))
+
1865 {
+
1866 // LCOV_EXCL_START
+
1867 JLOG(m_journal.fatal()) << "Ledger is not sensible.";
+
1868 UNREACHABLE(
+
1869 "xrpl::ApplicationImp::loadOldLedger : ledger is not "
+
1870 "sensible");
+
1871 return false;
+
1872 // LCOV_EXCL_STOP
+
1873 }
+
1874
+
1875 m_ledgerMaster->setLedgerRangePresent(loadLedger->header().seq, loadLedger->header().seq);
+
1876
+
1877 m_ledgerMaster->switchLCL(loadLedger);
+
1878 loadLedger->setValidated();
+
1879 m_ledgerMaster->setFullLedger(loadLedger, true, false);
+
1880 openLedger_.emplace(loadLedger, cachedSLEs_, logs_->journal("OpenLedger"));
+
1881
+
1882 if (replay)
+
1883 {
+
1884 // inject transaction(s) from the replayLedger into our open ledger
+
1885 // and build replay structure
+
1886 auto replayData = std::make_unique<LedgerReplay>(loadLedger, replayLedger);
+
1887
+
1888 for (auto const& [_, tx] : replayData->orderedTxns())
+
1889 {
+
1890 (void)_;
+
1891 auto txID = tx->getTransactionID();
+
1892 if (trapTxID == txID)
+
1893 {
+
1894 trapTxID_ = txID;
+
1895 JLOG(m_journal.debug()) << "Trap transaction set: " << txID;
+
1896 }
+
1897
+ +
1899 tx->add(*s);
+
1900
+
1901 forceValidity(getHashRouter(), txID, Validity::SigGoodOnly);
+
1902
+
1903 openLedger_->modify([&txID, &s](OpenView& view, beast::Journal j) {
+
1904 view.rawTxInsert(txID, std::move(s), nullptr);
+
1905 return true;
+
1906 });
+
1907 }
+
1908
+
1909 m_ledgerMaster->takeReplay(std::move(replayData));
+
1910
+
1911 if (trapTxID && !trapTxID_)
+
1912 {
+
1913 JLOG(m_journal.fatal()) << "Ledger " << replayLedger->header().seq
+
1914 << " does not contain the transaction hash " << *trapTxID;
+
1915 return false;
+
1916 }
+
1917 }
+
1918 }
+
1919 catch (SHAMapMissingNode const& mn)
+
1920 {
+
1921 JLOG(m_journal.fatal()) << "While loading specified ledger: " << mn.what();
+
1922 return false;
+
1923 }
+
1924 catch (boost::bad_lexical_cast&)
+
1925 {
+
1926 JLOG(m_journal.fatal()) << "Ledger specified '" << ledgerID << "' is not valid";
+
1927 return false;
+
1928 }
+
1929
+
1930 return true;
+
1931}
-
1928
-
1929bool
-
-
1930ApplicationImp::serverOkay(std::string& reason)
-
1931{
-
1932 if (!config().ELB_SUPPORT)
-
1933 return true;
-
1934
-
1935 if (isStopping())
-
1936 {
-
1937 reason = "Server is shutting down";
-
1938 return false;
-
1939 }
-
1940
-
1941 if (getOPs().isNeedNetworkLedger())
-
1942 {
-
1943 reason = "Not synchronized with network yet";
-
1944 return false;
-
1945 }
-
1946
-
1947 if (getOPs().isAmendmentBlocked())
-
1948 {
-
1949 reason = "Server version too old";
-
1950 return false;
-
1951 }
-
1952
-
1953 if (getOPs().isUNLBlocked())
-
1954 {
-
1955 reason = "No valid validator list available";
-
1956 return false;
-
1957 }
-
1958
-
1959 if (getOPs().getOperatingMode() < OperatingMode::SYNCING)
-
1960 {
-
1961 reason = "Not synchronized with network";
-
1962 return false;
-
1963 }
-
1964
-
1965 if (!getLedgerMaster().isCaughtUp(reason))
+
1932
+
1933bool
+
+
1934ApplicationImp::serverOkay(std::string& reason)
+
1935{
+
1936 if (!config().ELB_SUPPORT)
+
1937 return true;
+
1938
+
1939 if (isStopping())
+
1940 {
+
1941 reason = "Server is shutting down";
+
1942 return false;
+
1943 }
+
1944
+
1945 if (getOPs().isNeedNetworkLedger())
+
1946 {
+
1947 reason = "Not synchronized with network yet";
+
1948 return false;
+
1949 }
+
1950
+
1951 if (getOPs().isAmendmentBlocked())
+
1952 {
+
1953 reason = "Server version too old";
+
1954 return false;
+
1955 }
+
1956
+
1957 if (getOPs().isUNLBlocked())
+
1958 {
+
1959 reason = "No valid validator list available";
+
1960 return false;
+
1961 }
+
1962
+
1963 if (getOPs().getOperatingMode() < OperatingMode::SYNCING)
+
1964 {
+
1965 reason = "Not synchronized with network";
1966 return false;
-
1967
-
1968 if (getFeeTrack().isLoadedLocal())
-
1969 {
-
1970 reason = "Too much load";
-
1971 return false;
-
1972 }
-
1973
-
1974 return true;
-
1975}
+
1967 }
+
1968
+
1969 if (!getLedgerMaster().isCaughtUp(reason))
+
1970 return false;
+
1971
+
1972 if (getFeeTrack().isLoadedLocal())
+
1973 {
+
1974 reason = "Too much load";
+
1975 return false;
+
1976 }
+
1977
+
1978 return true;
+
1979}
-
1976
- -
-
1978ApplicationImp::journal(std::string const& name)
-
1979{
-
1980 return logs_->journal(name);
-
1981}
+
1980
+ +
+
1982ApplicationImp::journal(std::string const& name)
+
1983{
+
1984 return logs_->journal(name);
+
1985}
-
1982
-
1983void
-
-
1984ApplicationImp::setMaxDisallowedLedger()
-
1985{
-
1986 auto seq = getRelationalDatabase().getMaxLedgerSeq();
-
1987 if (seq)
-
1988 maxDisallowedLedger_ = *seq;
-
1989
-
1990 JLOG(m_journal.trace()) << "Max persisted ledger is " << maxDisallowedLedger_;
-
1991}
-
-
1992
-
1993//------------------------------------------------------------------------------
-
1994
-
-
1995Application::Application() : beast::PropertyStream::Source("app")
-
1996{
-
1997}
+
1986
+
1987void
+
+
1988ApplicationImp::setMaxDisallowedLedger()
+
1989{
+
1990 auto seq = getRelationalDatabase().getMaxLedgerSeq();
+
1991 if (seq)
+
1992 maxDisallowedLedger_ = *seq;
+
1993
+
1994 JLOG(m_journal.trace()) << "Max persisted ledger is " << maxDisallowedLedger_;
+
1995}
+
1996
+
1997//------------------------------------------------------------------------------
1998
-
1999//------------------------------------------------------------------------------
-
2000
- -
- -
2003{
-
2004 return std::make_unique<ApplicationImp>(std::move(config), std::move(logs), std::move(timeKeeper));
-
2005}
+
+
1999Application::Application() : beast::PropertyStream::Source("app")
+
2000{
+
2001}
-
2006
-
2007void
-
-
2008fixConfigPorts(Config& config, Endpoints const& endpoints)
-
2009{
-
2010 for (auto const& [name, ep] : endpoints)
-
2011 {
-
2012 if (!config.exists(name))
-
2013 continue;
-
2014
-
2015 auto& section = config[name];
-
2016 auto const optPort = section.get("port");
-
2017 if (optPort)
-
2018 {
-
2019 std::uint16_t const port = beast::lexicalCast<std::uint16_t>(*optPort);
-
2020 if (!port)
-
2021 section.set("port", std::to_string(ep.port()));
-
2022 }
-
2023 }
-
2024}
+
2002
+
2003//------------------------------------------------------------------------------
+
2004
+ +
+ +
2007{
+
2008 return std::make_unique<ApplicationImp>(std::move(config), std::move(logs), std::move(timeKeeper));
+
2009}
-
2025
-
2026} // namespace xrpl
+
2010
+
2011void
+
+
2012fixConfigPorts(Config& config, Endpoints const& endpoints)
+
2013{
+
2014 for (auto const& [name, ep] : endpoints)
+
2015 {
+
2016 if (!config.exists(name))
+
2017 continue;
+
2018
+
2019 auto& section = config[name];
+
2020 auto const optPort = section.get("port");
+
2021 if (optPort)
+
2022 {
+
2023 std::uint16_t const port = beast::lexicalCast<std::uint16_t>(*optPort);
+
2024 if (!port)
+
2025 section.set("port", std::to_string(ep.port()));
+
2026 }
+
2027 }
+
2028}
+
+
2029
+
2030} // namespace xrpl
@@ -2288,8 +2292,8 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
bool isObjectOrNull() const
-
Value removeMember(char const *key)
Remove and return the named member.
+
bool isObjectOrNull() const
+
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:324
@@ -2318,128 +2322,128 @@ $(document).ready(function() { init_codefold(0); }); -
LedgerReplayer & getLedgerReplayer() override
-
Application::MutexType & getMasterMutex() override
+
LedgerReplayer & getLedgerReplayer() override
+
Application::MutexType & getMasterMutex() override
std::optional< std::pair< PublicKey, SecretKey > > nodeIdentity_
-
InboundLedgers & getInboundLedgers() override
+
InboundLedgers & getInboundLedgers() override
std::unique_ptr< LedgerCleaner > ledgerCleaner_
std::unique_ptr< LoadManager > m_loadManager
-
void start(bool withTimers) override
-
LoadFeeTrack & getFeeTrack() override
-
Cluster & cluster() override
+
void start(bool withTimers) override
+
LoadFeeTrack & getFeeTrack() override
+
Cluster & cluster() override
std::unique_ptr< perf::PerfLog > perfLog_
std::unique_ptr< HashRouter > hashRouter_
std::optional< OpenLedger > openLedger_
-
RCLValidations & getValidations() override
-
void run() override
-
OpenLedger & openLedger() override
-
Resource::Manager & getResourceManager() override
+
RCLValidations & getValidations() override
+
void run() override
+
OpenLedger & openLedger() override
+
Resource::Manager & getResourceManager() override
NodeStoreScheduler m_nodeStoreScheduler
ClosureCounter< void, boost::system::error_code const & > waitHandlerCounter_
beast::Journal m_journal
-
RelationalDatabase & getRelationalDatabase() override
-
TransactionMaster & getMasterTransaction() override
-
std::chrono::milliseconds getIOLatency() override
+
RelationalDatabase & getRelationalDatabase() override
+
TransactionMaster & getMasterTransaction() override
+
std::chrono::milliseconds getIOLatency() override
std::unique_ptr< CollectorManager > m_collectorManager
-
boost::asio::io_context & getIOContext() override
-
std::optional< PublicKey const > getValidationPublicKey() const override
-
HashRouter & getHashRouter() override
-
LoadManager & getLoadManager() override
- +
boost::asio::io_context & getIOContext() override
+
std::optional< PublicKey const > getValidationPublicKey() const override
+
HashRouter & getHashRouter() override
+
LoadManager & getLoadManager() override
+
PendingSaves pendingSaves_
std::atomic< bool > checkSigs_
-
bool checkSigs() const override
-
bool serverOkay(std::string &reason) override
+
bool checkSigs() const override
+
bool serverOkay(std::string &reason) override
std::unique_ptr< SHAMapStore > m_shaMapStore
Application::MutexType m_masterMutex
-
InboundTransactions & getInboundTransactions() override
+
InboundTransactions & getInboundTransactions() override
io_latency_sampler m_io_latency_sampler
std::unique_ptr< AmendmentTable > m_amendmentTable
std::unique_ptr< InboundTransactions > m_inboundTransactions
-
SHAMapStore & getSHAMapStore() override
+
SHAMapStore & getSHAMapStore() override
boost::asio::steady_timer sweepTimer_
std::unique_ptr< NodeStore::Database > m_nodeStore
std::unique_ptr< LoadFeeTrack > mFeeTrack
boost::asio::steady_timer entropyTimer_
std::unique_ptr< Overlay > overlay_
-
bool setup(boost::program_options::variables_map const &cmdline) override
- -
Overlay & overlay() override
+
bool setup(boost::program_options::variables_map const &cmdline) override
+ +
Overlay & overlay() override
std::unique_ptr< Config > config_
-
ManifestCache & publisherManifests() override
+
ManifestCache & publisherManifests() override
std::unique_ptr< ManifestCache > validatorManifests_
-
CollectorManager & getCollectorManager() override
+
CollectorManager & getCollectorManager() override
std::optional< uint256 > trapTxID_
std::unique_ptr< ResolverAsio > m_resolver
-
NetworkOPs & getOPs() override
-
TimeKeeper & timeKeeper() override
+
NetworkOPs & getOPs() override
+
TimeKeeper & timeKeeper() override
std::unique_ptr< ValidatorList > validators_
std::unique_ptr< TxQ > txQ_
static std::size_t numberOfThreads(Config const &config)
-
OpenLedger const & openLedger() const override
- - +
OpenLedger const & openLedger() const override
+ +
std::unique_ptr< ManifestCache > publisherManifests_
-
LedgerIndex getMaxDisallowedLedger() override
Ensure that a newly-started validator does not sign proposals older than the last ledger it persisted...
-
NodeCache & getTempNodeCache() override
-
Application & app() override
-
Logs & logs() override
-
ValidatorList & validators() override
-
PeerReservationTable & peerReservations() override
+
LedgerIndex getMaxDisallowedLedger() override
Ensure that a newly-started validator does not sign proposals older than the last ledger it persisted...
+
NodeCache & getTempNodeCache() override
+
Application & app() override
+
Logs & logs() override
+
ValidatorList & validators() override
+
PeerReservationTable & peerReservations() override
ApplicationImp(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
std::unique_ptr< PeerReservationTable > peerReservations_
std::unique_ptr< Resource::Manager > m_resourceManager
-
std::pair< PublicKey, SecretKey > const & nodeIdentity() override
+
std::pair< PublicKey, SecretKey > const & nodeIdentity() override
std::unique_ptr< Logs > logs_
std::unique_ptr< DatabaseCon > mWalletDB
-
CachedSLEs & cachedSLEs() override
-
ValidatorSite & validatorSites() override
-
virtual std::optional< uint256 > const & trapTxID() const override
+
CachedSLEs & cachedSLEs() override
+
ValidatorSite & validatorSites() override
+
virtual std::optional< uint256 > const & trapTxID() const override
std::atomic_flag isTimeToStop
std::optional< SQLiteDatabase > relationalDatabase_
std::unique_ptr< LedgerMaster > m_ledgerMaster
std::unique_ptr< GRPCServer > grpcServer_
std::unique_ptr< ServerHandler > serverHandler_
ValidatorKeys const validatorKeys_
-
std::uint64_t instanceID() const override
Returns a 64-bit instance identifier, generated at startup.
-
OrderBookDB & getOrderBookDB() override
-
Family & getNodeFamily() override
-
void gotTXSet(std::shared_ptr< SHAMap > const &set, bool fromAcquire)
-
Config & config() override
- -
DatabaseCon & getWalletDB() override
Retrieve the "wallet database".
-
PathRequests & getPathRequests() override
-
bool isStopping() const override
+
std::uint64_t instanceID() const override
Returns a 64-bit instance identifier, generated at startup.
+
OrderBookDB & getOrderBookDB() override
+
Family & getNodeFamily() override
+
void gotTXSet(std::shared_ptr< SHAMap > const &set, bool fromAcquire)
+
Config & config() override
+ +
DatabaseCon & getWalletDB() override
Retrieve the "wallet database".
+
PathRequests & getPathRequests() override
+
bool isStopping() const override
std::unique_ptr< TimeKeeper > timeKeeper_
-
beast::Journal journal(std::string const &name) override
+
beast::Journal journal(std::string const &name) override
std::unique_ptr< ValidatorSite > validatorSites_
RCLValidations mValidations
-
void signalStop(std::string msg) override
+
void signalStop(std::string msg) override
std::uint64_t const instanceCookie_
-
LedgerCleaner & getLedgerCleaner() override
-
ManifestCache & validatorManifests() override
-
AmendmentTable & getAmendmentTable() override
-
int fdRequired() const override
+
LedgerCleaner & getLedgerCleaner() override
+
ManifestCache & validatorManifests() override
+
AmendmentTable & getAmendmentTable() override
+
int fdRequired() const override
TaggedCache< uint256, AcceptedLedger > m_acceptedLedgerCache
std::unique_ptr< PathRequests > m_pathRequests
std::unique_ptr< JobQueue > m_jobQueue
-
LedgerMaster & getLedgerMaster() override
-
NodeStore::Database & getNodeStore() override
+
LedgerMaster & getLedgerMaster() override
+
NodeStore::Database & getNodeStore() override
std::unique_ptr< OrderBookDB > m_orderBookDB
TransactionMaster m_txMaster
std::unique_ptr< Cluster > cluster_
std::unique_ptr< NetworkOPs > m_networkOPs
-
virtual ServerHandler & getServerHandler() override
-
JobQueue & getJobQueue() override
-
PendingSaves & pendingSaves() override
-
void onWrite(beast::PropertyStream::Map &stream) override
Subclass override.
+
virtual ServerHandler & getServerHandler() override
+
JobQueue & getJobQueue() override
+
PendingSaves & pendingSaves() override
+
void onWrite(beast::PropertyStream::Map &stream) override
Subclass override.
std::unique_ptr< InboundLedgers > m_inboundLedgers
-
TaggedCache< uint256, AcceptedLedger > & getAcceptedLedgerCache() override
-
perf::PerfLog & getPerfLog() override
-
TxQ & getTxQ() override
+
TaggedCache< uint256, AcceptedLedger > & getAcceptedLedgerCache() override
+
perf::PerfLog & getPerfLog() override
+
TxQ & getTxQ() override
boost::asio::signal_set m_signals
std::unique_ptr< LedgerReplayer > m_ledgerReplayer
@@ -2553,27 +2557,27 @@ $(document).ready(function() { init_codefold(0); });
std::unique_ptr< NetworkOPs > make_NetworkOPs(ServiceRegistry &registry, NetworkOPs::clock_type &clock, bool standalone, std::size_t minPeerCount, bool start_valid, JobQueue &job_queue, LedgerMaster &ledgerMaster, ValidatorKeys const &validatorKeys, boost::asio::io_context &io_svc, beast::Journal journal, beast::insight::Collector::ptr const &collector)
-
std::unique_ptr< Application > make_Application(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
+
std::unique_ptr< Application > make_Application(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
ServerHandler::Setup setup_ServerHandler(Config const &config, std::ostream &&log)
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Application &app, bool acquire)
Definition Ledger.cpp:1023
std::unique_ptr< CollectorManager > make_CollectorManager(Section const &params, beast::Journal journal)
std::unique_ptr< InboundLedgers > make_InboundLedgers(Application &app, InboundLedgers::clock_type &clock, beast::insight::Collector::ptr const &collector)
std::unique_ptr< ServerHandler > make_ServerHandler(Application &app, boost::asio::io_context &io_context, JobQueue &jobQueue, NetworkOPs &networkOPs, Resource::Manager &resourceManager, CollectorManager &cm)
constexpr auto megabytes(T value) noexcept
-
static void fixConfigPorts(Config &config, Endpoints const &endpoints)
+
static void fixConfigPorts(Config &config, Endpoints const &endpoints)
DatabaseCon::Setup setup_DatabaseCon(Config const &c, std::optional< beast::Journal > j=std::nullopt)
Definition Config.cpp:1043
std::unique_ptr< DatabaseCon > makeWalletDB(DatabaseCon::Setup const &setup, beast::Journal j)
makeWalletDB Opens the wallet database and returns it.
Definition Wallet.cpp:9
void initAccountIdCache(std::size_t count)
Initialize the global cache used to map AccountID to base58 conversions.
Definition AccountID.cpp:85
std::unique_ptr< InboundTransactions > make_InboundTransactions(Application &app, beast::insight::Collector::ptr const &collector, std::function< void(std::shared_ptr< SHAMap > const &, bool)> gotSet)
void addJson(Json::Value &json, LedgerFill const &fill)
Given a Ledger and options, fill a Json::Value with a description of the ledger.
-
Overlay::Setup setup_Overlay(BasicConfig const &config)
+
Overlay::Setup setup_Overlay(BasicConfig const &config)
void forceValidity(HashRouter &router, uint256 const &txid, Validity validity)
Sets the validity of a given transaction in the cache.
Definition apply.cpp:90
std::unordered_map< std::string, boost::asio::ip::tcp::endpoint > Endpoints
Definition ServerImpl.h:20
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition Feature.cpp:336
std::unique_ptr< OrderBookDB > make_OrderBookDB(ServiceRegistry &registry, OrderBookDBConfig const &config)
Create an OrderBookDB instance.
SQLiteDatabase setup_RelationalDatabase(ServiceRegistry &registry, Config const &config, JobQueue &jobQueue)
setup_RelationalDatabase Creates and returns a SQLiteDatabase instance based on configuration.
std::unique_ptr< PeerSetBuilder > make_PeerSetBuilder(Application &app)
Definition PeerSet.cpp:121
-
std::unique_ptr< Overlay > make_Overlay(Application &app, Overlay::Setup const &setup, ServerHandler &serverHandler, Resource::Manager &resourceManager, Resolver &resolver, boost::asio::io_context &io_context, BasicConfig const &config, beast::insight::Collector::ptr const &collector)
Creates the implementation of Overlay.
+
std::unique_ptr< Overlay > make_Overlay(Application &app, Overlay::Setup const &setup, ServerHandler &serverHandler, Resource::Manager &resourceManager, Resolver &resolver, boost::asio::io_context &io_context, BasicConfig const &config, beast::insight::Collector::ptr const &collector)
Creates the implementation of Overlay.
diff --git a/Application_8h_source.html b/Application_8h_source.html index 7bf3fd7089..4f22fc24f4 100644 --- a/Application_8h_source.html +++ b/Application_8h_source.html @@ -259,7 +259,7 @@ $(document).ready(function() { init_codefold(0); });
virtual int fdRequired() const =0
virtual LedgerIndex getMaxDisallowedLedger()=0
Ensure that a newly-started validator does not sign proposals older than the last ledger it persisted...
virtual void run()=0
- +
virtual std::optional< PublicKey const > getValidationPublicKey() const =0
virtual bool serverOkay(std::string &reason)=0
virtual void checkSigs(bool)=0
@@ -274,7 +274,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TaggedCache< uint256, SLE const > CachedSLEs
Definition CachedSLEs.h:8
-
std::unique_ptr< Application > make_Application(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
+
std::unique_ptr< Application > make_Application(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
STLedgerEntry SLE
TaggedCache< SHAMapHash, Blob > NodeCache
Validations< RCLValidationsAdaptor > RCLValidations
Alias for RCL-specific instantiation of generic Validations.
diff --git a/ApplyContext_8cpp_source.html b/ApplyContext_8cpp_source.html index 9385e44e01..f4c8689dc7 100644 --- a/ApplyContext_8cpp_source.html +++ b/ApplyContext_8cpp_source.html @@ -231,16 +231,16 @@ $(document).ready(function() { init_codefold(0); });
std::size_t size()
Get the number of unapplied changes.
STTx const & tx
TER failInvariantCheck(TER const result)
- +
void discard()
Discard changes and start fresh.
ApplyContext(Application &app, OpenView &base, std::optional< uint256 const > const &parentBatchId, STTx const &tx, TER preclaimResult, XRPAmount baseFee, ApplyFlags flags, beast::Journal journal=beast::Journal{beast::Journal::getNullSink()})
- +
std::optional< TxMeta > apply(TER)
Apply the transaction result to the base.
-
std::optional< ApplyViewImpl > view_
+
std::optional< ApplyViewImpl > view_
beast::Journal const journal
TER checkInvariantsHelper(TER const result, XRPAmount const fee, std::index_sequence< Is... >)
TER checkInvariants(TER const result, XRPAmount const fee)
Applies all invariant checkers one by one.
-
std::optional< uint256 const > parentBatchId_
+
std::optional< uint256 const > parentBatchId_
void visit(std::function< void(uint256 const &key, bool isDelete, std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after)> const &func)
Visit unapplied changes.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:45
@@ -260,9 +260,9 @@ $(document).ready(function() { init_codefold(0); });
ApplyFlags
Definition ApplyView.h:10
@ tapDRY_RUN
Definition ApplyView.h:29
@ tapBATCH
Definition ApplyView.h:25
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecINVARIANT_FAILED
Definition TER.h:294
-
bool isTecClaim(TER x) noexcept
Definition TER.h:656
+
bool isTecClaim(TER x) noexcept
Definition TER.h:650
T has_value(T... args)
diff --git a/ApplyContext_8h_source.html b/ApplyContext_8h_source.html index c4d493a95d..7f7700e8a3 100644 --- a/ApplyContext_8h_source.html +++ b/ApplyContext_8h_source.html @@ -180,41 +180,42 @@ $(document).ready(function() { init_codefold(0); });
89 size();
90
92 void
- -
94 uint256 const& key,
-
95 bool isDelete,
-
96 std::shared_ptr<SLE const> const& before,
-
97 std::shared_ptr<SLE const> const& after)> const& func);
-
98
-
99 void
-
- -
101 {
-
102 view_->rawDestroyXRP(fee);
-
103 }
+
93 visit(
+
94 std::function<void(
+
95 uint256 const& key,
+
96 bool isDelete,
+
97 std::shared_ptr<SLE const> const& before,
+
98 std::shared_ptr<SLE const> const& after)> const& func);
+
99
+
100 void
+
+ +
102 {
+
103 view_->rawDestroyXRP(fee);
+
104 }
-
104
-
111 TER
-
112 checkInvariants(TER const result, XRPAmount const fee);
-
113
-
114private:
-
115 TER
-
116 failInvariantCheck(TER const result);
-
117
-
118 template <std::size_t... Is>
-
119 TER
- -
121
- - - -
125
-
126 // The ID of the batch transaction we are executing under, if seated.
- -
128};
+
105
+
112 TER
+
113 checkInvariants(TER const result, XRPAmount const fee);
+
114
+
115private:
+
116 TER
+
117 failInvariantCheck(TER const result);
+
118
+
119 template <std::size_t... Is>
+
120 TER
+ +
122
+ + + +
126
+
127 // The ID of the batch transaction we are executing under, if seated.
+ +
129};
-
129
-
130} // namespace xrpl
+
130
+
131} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
static Sink & getNullSink()
Returns a Sink which does nothing.
@@ -223,22 +224,22 @@ $(document).ready(function() { init_codefold(0); });
std::size_t size()
Get the number of unapplied changes.
STTx const & tx
TER failInvariantCheck(TER const result)
- -
void destroyXRP(XRPAmount const &fee)
+ +
void destroyXRP(XRPAmount const &fee)
XRPAmount const baseFee
ApplyFlags const & flags() const
void discard()
Discard changes and start fresh.
- +
void deliver(STAmount const &amount)
Sets the DeliveredAmount field in the metadata.
std::optional< TxMeta > apply(TER)
Apply the transaction result to the base.
-
std::optional< ApplyViewImpl > view_
+
std::optional< ApplyViewImpl > view_
beast::Journal const journal
ApplyView const & view() const
RawView & rawView()
TER checkInvariantsHelper(TER const result, XRPAmount const fee, std::index_sequence< Is... >)
ApplyView & view()
TER checkInvariants(TER const result, XRPAmount const fee)
Applies all invariant checkers one by one.
-
std::optional< uint256 const > parentBatchId_
+
std::optional< uint256 const > parentBatchId_
TER const preclaimResult
Application & app
void visit(std::function< void(uint256 const &key, bool isDelete, std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after)> const &func)
Visit unapplied changes.
diff --git a/ApplyStateTable_8cpp_source.html b/ApplyStateTable_8cpp_source.html index 27d945177d..ffcbf683a3 100644 --- a/ApplyStateTable_8cpp_source.html +++ b/ApplyStateTable_8cpp_source.html @@ -773,8 +773,8 @@ $(document).ready(function() { init_codefold(0); }); -
bool empty() const
Definition STObject.h:939
-
std::size_t emplace_back(Args &&... args)
Definition STObject.h:975
+
bool empty() const
Definition STObject.h:945
+
std::size_t emplace_back(Args &&... args)
Definition STObject.h:981
void add(Serializer &s) const override
Definition STObject.cpp:117
uint256 getTransactionID() const
Definition STTx.h:192
diff --git a/ApplyView_8cpp_source.html b/ApplyView_8cpp_source.html index 3ed6350951..3c2531d621 100644 --- a/ApplyView_8cpp_source.html +++ b/ApplyView_8cpp_source.html @@ -532,10 +532,10 @@ $(document).ready(function() { init_codefold(0); });
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:118
-
std::vector< uint256 >::iterator insert(std::vector< uint256 >::const_iterator pos, uint256 const &value)
-
std::vector< uint256 >::iterator begin()
-
void push_back(uint256 const &v)
-
std::vector< uint256 >::iterator end()
+
std::vector< uint256 >::iterator insert(std::vector< uint256 >::const_iterator pos, uint256 const &value)
+
std::vector< uint256 >::iterator begin()
+
void push_back(uint256 const &v)
+
std::vector< uint256 >::iterator end()
T find(T... args)
@@ -554,7 +554,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
std::size_t constexpr dirNodeMaxEntries
The maximum number of entries per directory page.
Definition Protocol.h:37
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
std::uint64_t constexpr dirNodeMaxPages
The maximum number of pages allowed in a directory.
Definition Protocol.h:43
diff --git a/Asset_8cpp_source.html b/Asset_8cpp_source.html index afda47658c..91c22a67e2 100644 --- a/Asset_8cpp_source.html +++ b/Asset_8cpp_source.html @@ -162,7 +162,7 @@ $(document).ready(function() { init_codefold(0); });
65} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
STAmount operator()(Number const &) const
Definition Asset.cpp:35
std::string getText() const
Definition Asset.cpp:23
diff --git a/BaseHTTPPeer_8h_source.html b/BaseHTTPPeer_8h_source.html index 544a8c7695..8e636edc04 100644 --- a/BaseHTTPPeer_8h_source.html +++ b/BaseHTTPPeer_8h_source.html @@ -337,7 +337,7 @@ $(document).ready(function() { init_codefold(0); });
219{
220 if (!strand_.running_in_this_thread())
-
221 return post(strand_, std::bind((void(BaseHTTPPeer::*)(void)) & BaseHTTPPeer::close, impl().shared_from_this()));
+
221 return post(strand_, std::bind((void (BaseHTTPPeer::*)(void))&BaseHTTPPeer::close, impl().shared_from_this()));
222 boost::beast::get_lowest_layer(impl().stream_).close();
223}
@@ -579,7 +579,7 @@ $(document).ready(function() { init_codefold(0); });
436 return post(
437 strand_,
438 std::bind(
- +
440 impl().shared_from_this(),
441 graceful));
442
diff --git a/BaseWSPeer_8h_source.html b/BaseWSPeer_8h_source.html index 8ce41dddc0..1ad33f403c 100644 --- a/BaseWSPeer_8h_source.html +++ b/BaseWSPeer_8h_source.html @@ -288,312 +288,313 @@ $(document).ready(function() { init_codefold(0); });
178 impl().ws_.control_callback(control_callback_);
179 start_timer();
180 close_on_timer_ = true;
-
181 impl().ws_.set_option(boost::beast::websocket::stream_base::decorator(
-
182 [](auto& res) { res.set(boost::beast::http::field::server, BuildInfo::getFullVersionString()); }));
-
183 impl().ws_.async_accept(
-
184 request_,
-
185 bind_executor(
-
186 strand_, std::bind(&BaseWSPeer::on_ws_handshake, impl().shared_from_this(), std::placeholders::_1)));
-
187}
+
181 impl().ws_.set_option(boost::beast::websocket::stream_base::decorator([](auto& res) {
+
182 res.set(boost::beast::http::field::server, BuildInfo::getFullVersionString());
+
183 }));
+
184 impl().ws_.async_accept(
+
185 request_,
+
186 bind_executor(
+
187 strand_, std::bind(&BaseWSPeer::on_ws_handshake, impl().shared_from_this(), std::placeholders::_1)));
+
188}
-
188
-
189template <class Handler, class Impl>
-
190void
-
- -
192{
-
193 if (!strand_.running_in_this_thread())
-
194 return post(strand_, std::bind(&BaseWSPeer::send, impl().shared_from_this(), std::move(w)));
-
195 if (do_close_)
-
196 return;
-
197 if (wq_.size() > port().ws_queue_limit)
-
198 {
-
199 cr_.code = safe_cast<decltype(cr_.code)>(boost::beast::websocket::close_code::policy_error);
-
200 cr_.reason = "Policy error: client is too slow.";
-
201 JLOG(this->j_.info()) << cr_.reason;
-
202 wq_.erase(std::next(wq_.begin()), wq_.end());
-
203 close(cr_);
-
204 return;
-
205 }
-
206 wq_.emplace_back(std::move(w));
-
207 if (wq_.size() == 1)
-
208 on_write({});
-
209}
+
189
+
190template <class Handler, class Impl>
+
191void
+
+ +
193{
+
194 if (!strand_.running_in_this_thread())
+
195 return post(strand_, std::bind(&BaseWSPeer::send, impl().shared_from_this(), std::move(w)));
+
196 if (do_close_)
+
197 return;
+
198 if (wq_.size() > port().ws_queue_limit)
+
199 {
+
200 cr_.code = safe_cast<decltype(cr_.code)>(boost::beast::websocket::close_code::policy_error);
+
201 cr_.reason = "Policy error: client is too slow.";
+
202 JLOG(this->j_.info()) << cr_.reason;
+
203 wq_.erase(std::next(wq_.begin()), wq_.end());
+
204 close(cr_);
+
205 return;
+
206 }
+
207 wq_.emplace_back(std::move(w));
+
208 if (wq_.size() == 1)
+
209 on_write({});
+
210}
-
210
-
211template <class Handler, class Impl>
-
212void
-
- -
214{
-
215 close(boost::beast::websocket::close_reason{});
-
216}
+
211
+
212template <class Handler, class Impl>
+
213void
+
+ +
215{
+
216 close(boost::beast::websocket::close_reason{});
+
217}
-
217
-
218template <class Handler, class Impl>
-
219void
-
-
220BaseWSPeer<Handler, Impl>::close(boost::beast::websocket::close_reason const& reason)
-
221{
-
222 if (!strand_.running_in_this_thread())
-
223 return post(strand_, [self = impl().shared_from_this(), reason] { self->close(reason); });
-
224 if (do_close_)
-
225 return;
-
226 do_close_ = true;
-
227 if (wq_.empty())
-
228 {
-
229 impl().ws_.async_close(
-
230 reason, bind_executor(strand_, [self = impl().shared_from_this()](boost::beast::error_code const& ec) {
-
231 self->on_close(ec);
-
232 }));
-
233 }
-
234 else
-
235 {
-
236 cr_ = reason;
-
237 }
-
238}
+
218
+
219template <class Handler, class Impl>
+
220void
+
+
221BaseWSPeer<Handler, Impl>::close(boost::beast::websocket::close_reason const& reason)
+
222{
+
223 if (!strand_.running_in_this_thread())
+
224 return post(strand_, [self = impl().shared_from_this(), reason] { self->close(reason); });
+
225 if (do_close_)
+
226 return;
+
227 do_close_ = true;
+
228 if (wq_.empty())
+
229 {
+
230 impl().ws_.async_close(
+
231 reason, bind_executor(strand_, [self = impl().shared_from_this()](boost::beast::error_code const& ec) {
+
232 self->on_close(ec);
+
233 }));
+
234 }
+
235 else
+
236 {
+
237 cr_ = reason;
+
238 }
+
239}
-
239
-
240template <class Handler, class Impl>
-
241void
-
- -
243{
-
244 if (!strand_.running_in_this_thread())
-
245 return post(strand_, std::bind(&BaseWSPeer::complete, impl().shared_from_this()));
-
246 do_read();
-
247}
+
240
+
241template <class Handler, class Impl>
+
242void
+
+ +
244{
+
245 if (!strand_.running_in_this_thread())
+
246 return post(strand_, std::bind(&BaseWSPeer::complete, impl().shared_from_this()));
+
247 do_read();
+
248}
-
248
-
249template <class Handler, class Impl>
-
250void
-
- -
252{
-
253 if (ec)
-
254 return fail(ec, "on_ws_handshake");
-
255 close_on_timer_ = false;
-
256 do_read();
-
257}
+
249
+
250template <class Handler, class Impl>
+
251void
+
+ +
253{
+
254 if (ec)
+
255 return fail(ec, "on_ws_handshake");
+
256 close_on_timer_ = false;
+
257 do_read();
+
258}
-
258
-
259template <class Handler, class Impl>
-
260void
-
- -
262{
-
263 if (!strand_.running_in_this_thread())
-
264 return post(strand_, std::bind(&BaseWSPeer::do_write, impl().shared_from_this()));
-
265 on_write({});
-
266}
+
259
+
260template <class Handler, class Impl>
+
261void
+
+ +
263{
+
264 if (!strand_.running_in_this_thread())
+
265 return post(strand_, std::bind(&BaseWSPeer::do_write, impl().shared_from_this()));
+
266 on_write({});
+
267}
-
267
-
268template <class Handler, class Impl>
-
269void
-
- -
271{
-
272 if (ec)
-
273 return fail(ec, "write");
-
274 auto& w = *wq_.front();
-
275 auto const result = w.prepare(65536, std::bind(&BaseWSPeer::do_write, impl().shared_from_this()));
-
276 if (boost::indeterminate(result.first))
-
277 return;
-
278 start_timer();
-
279 if (!result.first)
-
280 impl().ws_.async_write_some(
-
281 static_cast<bool>(result.first),
-
282 result.second,
-
283 bind_executor(strand_, std::bind(&BaseWSPeer::on_write, impl().shared_from_this(), std::placeholders::_1)));
-
284 else
-
285 impl().ws_.async_write_some(
-
286 static_cast<bool>(result.first),
-
287 result.second,
-
288 bind_executor(
-
289 strand_, std::bind(&BaseWSPeer::on_write_fin, impl().shared_from_this(), std::placeholders::_1)));
-
290}
+
268
+
269template <class Handler, class Impl>
+
270void
+
+ +
272{
+
273 if (ec)
+
274 return fail(ec, "write");
+
275 auto& w = *wq_.front();
+
276 auto const result = w.prepare(65536, std::bind(&BaseWSPeer::do_write, impl().shared_from_this()));
+
277 if (boost::indeterminate(result.first))
+
278 return;
+
279 start_timer();
+
280 if (!result.first)
+
281 impl().ws_.async_write_some(
+
282 static_cast<bool>(result.first),
+
283 result.second,
+
284 bind_executor(strand_, std::bind(&BaseWSPeer::on_write, impl().shared_from_this(), std::placeholders::_1)));
+
285 else
+
286 impl().ws_.async_write_some(
+
287 static_cast<bool>(result.first),
+
288 result.second,
+
289 bind_executor(
+
290 strand_, std::bind(&BaseWSPeer::on_write_fin, impl().shared_from_this(), std::placeholders::_1)));
+
291}
-
291
-
292template <class Handler, class Impl>
-
293void
-
- -
295{
-
296 if (ec)
-
297 return fail(ec, "write_fin");
-
298 wq_.pop_front();
-
299 if (do_close_)
-
300 {
-
301 impl().ws_.async_close(
-
302 cr_,
-
303 bind_executor(strand_, std::bind(&BaseWSPeer::on_close, impl().shared_from_this(), std::placeholders::_1)));
-
304 }
-
305 else if (!wq_.empty())
-
306 on_write({});
-
307}
+
292
+
293template <class Handler, class Impl>
+
294void
+
+ +
296{
+
297 if (ec)
+
298 return fail(ec, "write_fin");
+
299 wq_.pop_front();
+
300 if (do_close_)
+
301 {
+
302 impl().ws_.async_close(
+
303 cr_,
+
304 bind_executor(strand_, std::bind(&BaseWSPeer::on_close, impl().shared_from_this(), std::placeholders::_1)));
+
305 }
+
306 else if (!wq_.empty())
+
307 on_write({});
+
308}
-
308
-
309template <class Handler, class Impl>
-
310void
-
- -
312{
-
313 if (!strand_.running_in_this_thread())
-
314 return post(strand_, std::bind(&BaseWSPeer::do_read, impl().shared_from_this()));
-
315 impl().ws_.async_read(
-
316 rb_, bind_executor(strand_, std::bind(&BaseWSPeer::on_read, impl().shared_from_this(), std::placeholders::_1)));
-
317}
+
309
+
310template <class Handler, class Impl>
+
311void
+
+ +
313{
+
314 if (!strand_.running_in_this_thread())
+
315 return post(strand_, std::bind(&BaseWSPeer::do_read, impl().shared_from_this()));
+
316 impl().ws_.async_read(
+
317 rb_, bind_executor(strand_, std::bind(&BaseWSPeer::on_read, impl().shared_from_this(), std::placeholders::_1)));
+
318}
-
318
-
319template <class Handler, class Impl>
-
320void
-
- -
322{
-
323 if (ec == boost::beast::websocket::error::closed)
-
324 return on_close({});
-
325 if (ec)
-
326 return fail(ec, "read");
-
327 auto const& data = rb_.data();
- -
329 b.reserve(std::distance(data.begin(), data.end()));
-
330 std::copy(data.begin(), data.end(), std::back_inserter(b));
-
331 this->handler_.onWSMessage(impl().shared_from_this(), b);
-
332 rb_.consume(rb_.size());
-
333}
+
319
+
320template <class Handler, class Impl>
+
321void
+
+ +
323{
+
324 if (ec == boost::beast::websocket::error::closed)
+
325 return on_close({});
+
326 if (ec)
+
327 return fail(ec, "read");
+
328 auto const& data = rb_.data();
+ +
330 b.reserve(std::distance(data.begin(), data.end()));
+
331 std::copy(data.begin(), data.end(), std::back_inserter(b));
+
332 this->handler_.onWSMessage(impl().shared_from_this(), b);
+
333 rb_.consume(rb_.size());
+
334}
-
334
-
335template <class Handler, class Impl>
-
336void
-
- -
338{
-
339 cancel_timer();
-
340}
+
335
+
336template <class Handler, class Impl>
+
337void
+
+ +
339{
+
340 cancel_timer();
+
341}
-
341
-
342template <class Handler, class Impl>
-
343void
-
- -
345{
-
346 // Max seconds without completing a message
-
347 static constexpr std::chrono::seconds timeout{30};
-
348 static constexpr std::chrono::seconds timeoutLocal{3};
-
349
-
350 try
-
351 {
-
352 timer_.expires_after(remote_endpoint().address().is_loopback() ? timeoutLocal : timeout);
-
353 }
-
354 catch (boost::system::system_error const& e)
-
355 {
-
356 return fail(e.code(), "start_timer");
-
357 }
-
358
-
359 timer_.async_wait(bind_executor(
-
360 strand_, std::bind(&BaseWSPeer<Handler, Impl>::on_timer, impl().shared_from_this(), std::placeholders::_1)));
-
361}
+
342
+
343template <class Handler, class Impl>
+
344void
+
+ +
346{
+
347 // Max seconds without completing a message
+
348 static constexpr std::chrono::seconds timeout{30};
+
349 static constexpr std::chrono::seconds timeoutLocal{3};
+
350
+
351 try
+
352 {
+
353 timer_.expires_after(remote_endpoint().address().is_loopback() ? timeoutLocal : timeout);
+
354 }
+
355 catch (boost::system::system_error const& e)
+
356 {
+
357 return fail(e.code(), "start_timer");
+
358 }
+
359
+
360 timer_.async_wait(bind_executor(
+
361 strand_, std::bind(&BaseWSPeer<Handler, Impl>::on_timer, impl().shared_from_this(), std::placeholders::_1)));
+
362}
-
362
-
363// Convenience for discarding the error code
-
364template <class Handler, class Impl>
-
365void
-
- -
367{
-
368 try
-
369 {
-
370 timer_.cancel();
-
371 }
-
372 catch (boost::system::system_error const&)
-
373 {
-
374 // ignored
-
375 }
-
376}
+
363
+
364// Convenience for discarding the error code
+
365template <class Handler, class Impl>
+
366void
+
+ +
368{
+
369 try
+
370 {
+
371 timer_.cancel();
+
372 }
+
373 catch (boost::system::system_error const&)
+
374 {
+
375 // ignored
+
376 }
+
377}
-
377
-
378template <class Handler, class Impl>
-
379void
-
- -
381{
-
382 if (ec == boost::asio::error::operation_aborted)
-
383 return;
-
384 ping_active_ = false;
-
385 if (!ec)
-
386 return;
-
387 fail(ec, "on_ping");
-
388}
+
378
+
379template <class Handler, class Impl>
+
380void
+
+ +
382{
+
383 if (ec == boost::asio::error::operation_aborted)
+
384 return;
+
385 ping_active_ = false;
+
386 if (!ec)
+
387 return;
+
388 fail(ec, "on_ping");
+
389}
-
389
-
390template <class Handler, class Impl>
-
391void
-
-
392BaseWSPeer<Handler, Impl>::on_ping_pong(boost::beast::websocket::frame_type kind, boost::beast::string_view payload)
-
393{
-
394 if (kind == boost::beast::websocket::frame_type::pong)
-
395 {
-
396 boost::beast::string_view p(payload_.begin());
-
397 if (payload == p)
-
398 {
-
399 close_on_timer_ = false;
-
400 JLOG(this->j_.trace()) << "got matching pong";
-
401 }
-
402 else
-
403 {
-
404 JLOG(this->j_.trace()) << "got pong";
-
405 }
-
406 }
-
407}
+
390
+
391template <class Handler, class Impl>
+
392void
+
+
393BaseWSPeer<Handler, Impl>::on_ping_pong(boost::beast::websocket::frame_type kind, boost::beast::string_view payload)
+
394{
+
395 if (kind == boost::beast::websocket::frame_type::pong)
+
396 {
+
397 boost::beast::string_view p(payload_.begin());
+
398 if (payload == p)
+
399 {
+
400 close_on_timer_ = false;
+
401 JLOG(this->j_.trace()) << "got matching pong";
+
402 }
+
403 else
+
404 {
+
405 JLOG(this->j_.trace()) << "got pong";
+
406 }
+
407 }
+
408}
-
408
-
409template <class Handler, class Impl>
-
410void
-
- -
412{
-
413 if (ec == boost::asio::error::operation_aborted)
-
414 return;
-
415 if (!ec)
-
416 {
-
417 if (!close_on_timer_ || !ping_active_)
-
418 {
-
419 start_timer();
-
420 close_on_timer_ = true;
-
421 ping_active_ = true;
-
422 // cryptographic is probably overkill..
-
423 beast::rngfill(payload_.begin(), payload_.size(), crypto_prng());
-
424 impl().ws_.async_ping(
-
425 payload_,
-
426 bind_executor(
-
427 strand_, std::bind(&BaseWSPeer::on_ping, impl().shared_from_this(), std::placeholders::_1)));
-
428 JLOG(this->j_.trace()) << "sent ping";
-
429 return;
-
430 }
-
431 ec = boost::system::errc::make_error_code(boost::system::errc::timed_out);
-
432 }
-
433 fail(ec, "timer");
-
434}
+
409
+
410template <class Handler, class Impl>
+
411void
+
+ +
413{
+
414 if (ec == boost::asio::error::operation_aborted)
+
415 return;
+
416 if (!ec)
+
417 {
+
418 if (!close_on_timer_ || !ping_active_)
+
419 {
+
420 start_timer();
+
421 close_on_timer_ = true;
+
422 ping_active_ = true;
+
423 // cryptographic is probably overkill..
+
424 beast::rngfill(payload_.begin(), payload_.size(), crypto_prng());
+
425 impl().ws_.async_ping(
+
426 payload_,
+
427 bind_executor(
+
428 strand_, std::bind(&BaseWSPeer::on_ping, impl().shared_from_this(), std::placeholders::_1)));
+
429 JLOG(this->j_.trace()) << "sent ping";
+
430 return;
+
431 }
+
432 ec = boost::system::errc::make_error_code(boost::system::errc::timed_out);
+
433 }
+
434 fail(ec, "timer");
+
435}
-
435
-
436template <class Handler, class Impl>
-
437template <class String>
-
438void
- -
440{
-
441 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::BaseWSPeer::fail : strand in this thread");
-
442
-
443 cancel_timer();
-
444 if (!ec_ && ec != boost::asio::error::operation_aborted)
-
445 {
-
446 ec_ = ec;
-
447 JLOG(this->j_.trace()) << what << ": " << ec.message();
-
448 xrpl::get_lowest_layer(impl().ws_).socket().close(ec);
-
449 }
-
450}
+
436
+
437template <class Handler, class Impl>
+
438template <class String>
+
439void
+ +
441{
+
442 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::BaseWSPeer::fail : strand in this thread");
+
443
+
444 cancel_timer();
+
445 if (!ec_ && ec != boost::asio::error::operation_aborted)
+
446 {
+
447 ec_ = ec;
+
448 JLOG(this->j_.trace()) << what << ": " << ec.message();
+
449 xrpl::get_lowest_layer(impl().ws_).socket().close(ec);
+
450 }
+
451}
-
451
-
452} // namespace xrpl
+
452
+
453} // namespace xrpl
@@ -623,44 +624,44 @@ $(document).ready(function() { init_codefold(0); });
Port const & port_
Definition BasePeer.h:27
endpoint_type remote_address_
Definition BasePeer.h:29
Represents an active WebSocket connection.
Definition BaseWSPeer.h:26
-
void on_ping_pong(boost::beast::websocket::frame_type kind, boost::beast::string_view payload)
Definition BaseWSPeer.h:392
+
void on_ping_pong(boost::beast::websocket::frame_type kind, boost::beast::string_view payload)
Definition BaseWSPeer.h:393
boost::beast::websocket::close_reason cr_
Definition BaseWSPeer.h:45
-
void on_write_fin(error_code const &ec)
Definition BaseWSPeer.h:294
+
void on_write_fin(error_code const &ec)
Definition BaseWSPeer.h:295
boost::beast::multi_buffer rb_
Definition BaseWSPeer.h:38
- +
boost::asio::ip::tcp::endpoint const & remote_endpoint() const override
Definition BaseWSPeer.h:84
BaseWSPeer(Port const &port, Handler &handler, boost::asio::executor const &executor, waitable_timer timer, endpoint_type remote_address, boost::beast::http::request< Body, Headers > &&request, beast::Journal journal)
Definition BaseWSPeer.h:153
-
void on_ping(error_code const &ec)
Definition BaseWSPeer.h:380
+
void on_ping(error_code const &ec)
Definition BaseWSPeer.h:381
error_code ec_
Definition BaseWSPeer.h:50
boost::asio::ip::tcp::endpoint endpoint_type
Definition BaseWSPeer.h:30
boost::beast::multi_buffer wb_
Definition BaseWSPeer.h:39
-
void fail(error_code ec, String const &what)
Definition BaseWSPeer.h:439
-
void on_close(error_code const &ec)
Definition BaseWSPeer.h:337
-
void close(boost::beast::websocket::close_reason const &reason) override
Definition BaseWSPeer.h:220
+
void fail(error_code ec, String const &what)
Definition BaseWSPeer.h:440
+
void on_close(error_code const &ec)
Definition BaseWSPeer.h:338
+
void close(boost::beast::websocket::close_reason const &reason) override
Definition BaseWSPeer.h:221
Port const & port() const override
Definition BaseWSPeer.h:72
bool do_close_
The socket has been closed, or will close after the next write finishes.
Definition BaseWSPeer.h:44
-
void on_read(error_code const &ec)
Definition BaseWSPeer.h:321
- - +
void on_read(error_code const &ec)
Definition BaseWSPeer.h:322
+ +
boost::system::error_code error_code
Definition BaseWSPeer.h:29
boost::asio::basic_waitable_timer< clock_type > waitable_timer
Definition BaseWSPeer.h:31
- +
boost::beast::websocket::ping_data payload_
Definition BaseWSPeer.h:49
-
void on_ws_handshake(error_code const &ec)
Definition BaseWSPeer.h:251
+
void on_ws_handshake(error_code const &ec)
Definition BaseWSPeer.h:252
void run() override
Definition BaseWSPeer.h:170
-
void close() override
Definition BaseWSPeer.h:213
+
void close() override
Definition BaseWSPeer.h:214
-
void send(std::shared_ptr< WSMsg > w) override
Send a WebSockets message.
Definition BaseWSPeer.h:191
+
void send(std::shared_ptr< WSMsg > w) override
Send a WebSockets message.
Definition BaseWSPeer.h:192
std::function< void(boost::beast::websocket::frame_type, boost::beast::string_view)> control_callback_
Definition BaseWSPeer.h:51
http_request_type request_
Definition BaseWSPeer.h:37
waitable_timer timer_
Definition BaseWSPeer.h:46
-
void on_write(error_code const &ec)
Definition BaseWSPeer.h:270
+
void on_write(error_code const &ec)
Definition BaseWSPeer.h:271
std::list< std::shared_ptr< WSMsg > > wq_
Definition BaseWSPeer.h:40
-
void complete() override
Indicate that the response is complete.
Definition BaseWSPeer.h:242
+
void complete() override
Indicate that the response is complete.
Definition BaseWSPeer.h:243
http_request_type const & request() const override
Definition BaseWSPeer.h:78
-
void on_timer(error_code ec)
Definition BaseWSPeer.h:411
+
void on_timer(error_code ec)
Definition BaseWSPeer.h:412
T copy(T... args)
T distance(T... args)
diff --git a/Batch_8cpp_source.html b/Batch_8cpp_source.html index b06cf68235..f99a7f69ea 100644 --- a/Batch_8cpp_source.html +++ b/Batch_8cpp_source.html @@ -565,7 +565,7 @@ $(document).ready(function() { init_codefold(0); });
@ temINVALID_INNER_BATCH
Definition TER.h:123
@ temREDUNDANT
Definition TER.h:92
@ temBAD_SIGNER
Definition TER.h:95
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
constexpr XRPAmount INITIAL_XRP
Configure the native currency.
constexpr std::uint32_t tfUntilFailure
Definition TxFlags.h:258
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
diff --git a/Batch__test_8cpp_source.html b/Batch__test_8cpp_source.html index 1ad53cb1fc..1b93c8aafb 100644 --- a/Batch__test_8cpp_source.html +++ b/Batch__test_8cpp_source.html @@ -4594,7 +4594,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:792
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:319
-
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:126
+
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:127
Adds a new Batch Txn on a JTx and autofills.
Definition batch.h:34
Set a batch nested multi-signature on a JTx.
Definition batch.h:107
Set a batch signature on a JTx.
Definition batch.h:84
@@ -4622,7 +4622,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value outer(jtx::Account const &account, uint32_t seq, STAmount const &fee, std::uint32_t flags)
Batch.
Definition batch.cpp:27
XRPAmount calcBatchFee(jtx::Env const &env, uint32_t const &numSigners, uint32_t const &txns=0)
Calculate Batch Fee.
Definition batch.cpp:19
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
void checkMetrics(Suite &test, jtx::Env &env, std::size_t expectedCount, std::optional< std::size_t > expectedMaxCount, std::size_t expectedInLedger, std::size_t expectedPerLedger, std::uint64_t expectedMinFeeLevel=baseFeeLevel.fee(), std::uint64_t expectedMedFeeLevel=minEscalationFeeLevel.fee(), std::source_location const location=std::source_location::current())
void incLgrSeqForAccDel(jtx::Env &env, jtx::Account const &acc, std::uint32_t margin=0)
@@ -4664,7 +4664,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:128
base_uint< 256 > uint256
Definition base_uint.h:526
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:99
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:156
bool passesLocalChecks(STObject const &st, std::string &)
Definition STTx.cpp:737
diff --git a/BlackList_8cpp_source.html b/BlackList_8cpp_source.html index 4a23107547..b81d4d0ad4 100644 --- a/BlackList_8cpp_source.html +++ b/BlackList_8cpp_source.html @@ -104,7 +104,7 @@ $(document).ready(function() { init_codefold(0); });
19} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
Int asInt() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Json::Value getJson()=0
Extract consumer information for reporting.
virtual Resource::Manager & getResourceManager()=0
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/BookChanges_8h_source.html b/BookChanges_8h_source.html index 83ec0763a4..77ded41251 100644 --- a/BookChanges_8h_source.html +++ b/BookChanges_8h_source.html @@ -281,7 +281,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
Json::UInt UInt
Definition json_value.h:137
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Identifies fields.
Definition SField.h:126
Issue const & issue() const
Definition STAmount.h:454
diff --git a/BookChanges__test_8cpp_source.html b/BookChanges__test_8cpp_source.html index a6b67d064c..501268bfd7 100644 --- a/BookChanges__test_8cpp_source.html +++ b/BookChanges__test_8cpp_source.html @@ -234,7 +234,7 @@ $(document).ready(function() { init_codefold(0); });
Add a path.
Definition paths.h:37
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
Sets the SendMax on a JTx.
Definition sendmax.h:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
diff --git a/BookDirs_8h_source.html b/BookDirs_8h_source.html index 3f043bb4f9..2cba072830 100644 --- a/BookDirs_8h_source.html +++ b/BookDirs_8h_source.html @@ -216,7 +216,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
diff --git a/BookDirs__test_8cpp_source.html b/BookDirs__test_8cpp_source.html index 48b1c504f2..25d0c9665d 100644 --- a/BookDirs__test_8cpp_source.html +++ b/BookDirs__test_8cpp_source.html @@ -190,7 +190,7 @@ $(document).ready(function() { init_codefold(0); });
T distance(T... args)
T end(T... args)
T is_same_v
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
diff --git a/BookOffers_8cpp_source.html b/BookOffers_8cpp_source.html index eac591ab7a..8150081a35 100644 --- a/BookOffers_8cpp_source.html +++ b/BookOffers_8cpp_source.html @@ -282,10 +282,10 @@ $(document).ready(function() { init_codefold(0); });
195
196} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isObjectOrNull() const
-
bool isString() const
+
bool isObjectOrNull() const
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
Stream info() const
Definition Journal.h:306
int getJobCountGE(JobType t) const
All waiting jobs at or greater than this priority.
Definition JobQueue.cpp:126
virtual void getBookPage(std::shared_ptr< ReadView const > &lpLedger, Book const &book, AccountID const &uTakerID, bool const bProof, unsigned int iLimit, Json::Value const &jvMarker, Json::Value &jvResult)=0
diff --git a/Book__test_8cpp_source.html b/Book__test_8cpp_source.html index bdba23c5b1..5bd596ea69 100644 --- a/Book__test_8cpp_source.html +++ b/Book__test_8cpp_source.html @@ -1871,7 +1871,7 @@ $(document).ready(function() { init_codefold(0); });
1750} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
virtual Config & config()=0
@@ -1932,7 +1932,7 @@ $(document).ready(function() { init_codefold(0); });
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:331
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
diff --git a/CanDelete_8cpp_source.html b/CanDelete_8cpp_source.html index b80ff18e06..340010a764 100644 --- a/CanDelete_8cpp_source.html +++ b/CanDelete_8cpp_source.html @@ -167,9 +167,9 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
-
bool isUInt() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
bool isUInt() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
std::shared_ptr< Ledger const > getLedgerByHash(uint256 const &hash)
virtual LedgerIndex getCanDelete()=0
Highest ledger that may be deleted.
virtual bool advisoryDelete() const =0
Whether advisory delete is enabled.
diff --git a/CancelCheck_8cpp_source.html b/CancelCheck_8cpp_source.html index e5f0ae7c05..fd58712c7e 100644 --- a/CancelCheck_8cpp_source.html +++ b/CancelCheck_8cpp_source.html @@ -201,11 +201,11 @@ $(document).ready(function() { init_codefold(0); });
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition Indexes.cpp:293
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ tefBAD_LEDGER
Definition TER.h:150
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
@ tecNO_ENTRY
Definition TER.h:287
@ tecNO_PERMISSION
Definition TER.h:286
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
uint256 key
Definition Keylet.h:20
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
diff --git a/CancelOffer_8cpp_source.html b/CancelOffer_8cpp_source.html index 61347478e9..005b1b7ddb 100644 --- a/CancelOffer_8cpp_source.html +++ b/CancelOffer_8cpp_source.html @@ -172,7 +172,7 @@ $(document).ready(function() { init_codefold(0); });
@ tefINTERNAL
Definition TER.h:153
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1672
@ temBAD_SEQUENCE
Definition TER.h:84
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
ReadView const & view
Definition Transactor.h:56
diff --git a/CanonicalTXSet_8cpp_source.html b/CanonicalTXSet_8cpp_source.html index 41d62e24c6..9072a601af 100644 --- a/CanonicalTXSet_8cpp_source.html +++ b/CanonicalTXSet_8cpp_source.html @@ -120,44 +120,45 @@ $(document).ready(function() { init_codefold(0); });
34{
-
35 map_.insert(std::make_pair(
-
36 Key(accountKey(txn->getAccountID(sfAccount)), txn->getSeqProxy(), txn->getTransactionID()), txn));
-
37}
+
35 map_.insert(
+ +
37 Key(accountKey(txn->getAccountID(sfAccount)), txn->getSeqProxy(), txn->getTransactionID()), txn));
+
38}
-
38
- -
- -
41{
-
42 // Determining the next viable transaction for an account with Tickets:
-
43 //
-
44 // 1. Prioritize transactions with Sequences over transactions with
-
45 // Tickets.
-
46 //
-
47 // 2. For transactions not using Tickets, look for consecutive Sequence
-
48 // numbers. For transactions using Tickets, don't worry about
-
49 // consecutive Sequence numbers. Tickets can process out of order.
-
50 //
-
51 // 3. After handling all transactions with Sequences, return Tickets
-
52 // with the lowest Ticket ID first.
- -
54 uint256 const effectiveAccount{accountKey(tx->getAccountID(sfAccount))};
-
55
-
56 auto const seqProxy = tx->getSeqProxy();
-
57 Key const after(effectiveAccount, seqProxy, beast::zero);
-
58 auto const itrNext{map_.lower_bound(after)};
-
59 if (itrNext != map_.end() && itrNext->first.getAccount() == effectiveAccount &&
-
60 (!itrNext->second->getSeqProxy().isSeq() || itrNext->second->getSeqProxy().value() == seqProxy.value() + 1))
-
61 {
-
62 result = std::move(itrNext->second);
-
63 map_.erase(itrNext);
-
64 }
-
65
-
66 return result;
-
67}
+
39
+ +
+ +
42{
+
43 // Determining the next viable transaction for an account with Tickets:
+
44 //
+
45 // 1. Prioritize transactions with Sequences over transactions with
+
46 // Tickets.
+
47 //
+
48 // 2. For transactions not using Tickets, look for consecutive Sequence
+
49 // numbers. For transactions using Tickets, don't worry about
+
50 // consecutive Sequence numbers. Tickets can process out of order.
+
51 //
+
52 // 3. After handling all transactions with Sequences, return Tickets
+
53 // with the lowest Ticket ID first.
+ +
55 uint256 const effectiveAccount{accountKey(tx->getAccountID(sfAccount))};
+
56
+
57 auto const seqProxy = tx->getSeqProxy();
+
58 Key const after(effectiveAccount, seqProxy, beast::zero);
+
59 auto const itrNext{map_.lower_bound(after)};
+
60 if (itrNext != map_.end() && itrNext->first.getAccount() == effectiveAccount &&
+
61 (!itrNext->second->getSeqProxy().isSeq() || itrNext->second->getSeqProxy().value() == seqProxy.value() + 1))
+
62 {
+
63 result = std::move(itrNext->second);
+
64 map_.erase(itrNext);
+
65 }
+
66
+
67 return result;
+
68}
-
68
-
69} // namespace xrpl
+
69
+
70} // namespace xrpl
@@ -165,7 +166,7 @@ $(document).ready(function() { init_codefold(0); });
void insert(std::shared_ptr< STTx const > const &txn)
std::map< Key, std::shared_ptr< STTx const > > map_
-
std::shared_ptr< STTx const > popAcctTransaction(std::shared_ptr< STTx const > const &tx)
+
std::shared_ptr< STTx const > popAcctTransaction(std::shared_ptr< STTx const > const &tx)
uint256 accountKey(AccountID const &account)
iterator begin()
Definition base_uint.h:112
diff --git a/CanonicalTXSet_8h_source.html b/CanonicalTXSet_8h_source.html index b262fffbf0..cbe2db4119 100644 --- a/CanonicalTXSet_8h_source.html +++ b/CanonicalTXSet_8h_source.html @@ -291,7 +291,7 @@ $(document).ready(function() { init_codefold(0); });
friend bool operator<(Key const &lhs, Key const &rhs)
const_iterator end() const
uint256 const & key() const
-
std::shared_ptr< STTx const > popAcctTransaction(std::shared_ptr< STTx const > const &tx)
+
std::shared_ptr< STTx const > popAcctTransaction(std::shared_ptr< STTx const > const &tx)
uint256 accountKey(AccountID const &account)
CanonicalTXSet(LedgerHash const &saltHash)
diff --git a/CashCheck_8cpp_source.html b/CashCheck_8cpp_source.html index ac9c1f3192..c10ec656f6 100644 --- a/CashCheck_8cpp_source.html +++ b/CashCheck_8cpp_source.html @@ -550,7 +550,7 @@ $(document).ready(function() { init_codefold(0); });
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
-
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:554
+
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:556
static int const cMaxOffset
Definition STAmount.h:46
Currency const & getCurrency() const
Definition STAmount.h:460
bool native() const noexcept
Definition STAmount.h:416
@@ -587,7 +587,7 @@ $(document).ready(function() { init_codefold(0); });
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:129
@ tefBAD_LEDGER
Definition TER.h:150
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition View.cpp:515
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
Definition StrandFlow.h:81
@@ -597,7 +597,7 @@ $(document).ready(function() { init_codefold(0); });
@ temBAD_CURRENCY
Definition TER.h:70
@ temMALFORMED
Definition TER.h:67
@ temBAD_AMOUNT
Definition TER.h:69
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:273
@ tecPATH_PARTIAL
Definition TER.h:263
@ tecUNFUNDED_PAYMENT
Definition TER.h:266
@@ -618,7 +618,7 @@ $(document).ready(function() { init_codefold(0); });
@ lsfHighAuth
@ no
Definition Steps.h:25
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
XRPAmount increment
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
diff --git a/Check__test_8cpp_source.html b/Check__test_8cpp_source.html index a270aa36da..afa21e370c 100644 --- a/Check__test_8cpp_source.html +++ b/Check__test_8cpp_source.html @@ -2609,8 +2609,8 @@ $(document).ready(function() { init_codefold(0); });
2469} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value removeMember(char const *key)
Remove and return the named member.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value removeMember(char const *key)
Remove and return the named member.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:221
diff --git a/Clawback_8cpp_source.html b/Clawback_8cpp_source.html index c38234e704..66ae21d842 100644 --- a/Clawback_8cpp_source.html +++ b/Clawback_8cpp_source.html @@ -354,7 +354,7 @@ $(document).ready(function() { init_codefold(0); });
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:118
constexpr TIss const & get() const
-
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:554
+
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:556
Currency const & getCurrency() const
Definition STAmount.h:460
AccountID const & getIssuer() const
Definition STAmount.h:466
@@ -379,7 +379,7 @@ $(document).ready(function() { init_codefold(0); });
static NotTEC preflightHelper(PreflightContext const &ctx)
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Definition View.cpp:1020
static TER preclaimHelper(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
TER preclaimHelper< Issue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition Clawback.cpp:86
@ ahIGNORE_AUTH
Definition View.h:61
NotTEC preflightHelper< MPTIssue >(PreflightContext const &ctx)
Definition Clawback.cpp:37
@@ -387,7 +387,7 @@ $(document).ready(function() { init_codefold(0); });
@ temDISABLED
Definition TER.h:94
@ temBAD_AMOUNT
Definition TER.h:69
NotTEC preflightHelper< Issue >(PreflightContext const &ctx)
Definition Clawback.cpp:18
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecPSEUDO_ACCOUNT
Definition TER.h:343
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecINTERNAL
Definition TER.h:291
@@ -400,7 +400,7 @@ $(document).ready(function() { init_codefold(0); });
@ lsfNoFreeze
@ lsfMPTCanClawback
TER applyHelper< Issue >(ApplyContext &ctx)
Definition Clawback.cpp:189
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
TER preclaimHelper< MPTIssue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition Clawback.cpp:131
@ tesSUCCESS
Definition TER.h:225
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:3083
diff --git a/Config__test_8cpp_source.html b/Config__test_8cpp_source.html index 18f786a17e..01ad4dbffd 100644 --- a/Config__test_8cpp_source.html +++ b/Config__test_8cpp_source.html @@ -1241,429 +1241,431 @@ $(document).ready(function() { init_codefold(0); });
1116 Config cfg;
1117 /* NOTE: this string includes some explicit
1118 * space chars in order to verify proper trimming */
-
1119 std::string toLoad(R"(
-
1120[port_rpc])"
-
1121 "\x20"
-
1122 R"(
-
1123# comment
-
1124 # indented comment
-
1125)"
-
1126 "\x20\x20"
-
1127 R"(
-
1128[ips])"
-
1129 "\x20"
-
1130 R"(
-
1131r.ripple.com 51235
-
1132
-
1133 [ips_fixed])"
-
1134 "\x20\x20"
-
1135 R"(
-
1136 # COMMENT
-
1137 s1.ripple.com 51235
-
1138 s2.ripple.com 51235
-
1139
+
1119 std::string toLoad(
+
1120 R"(
+
1121[port_rpc])"
+
1122 "\x20"
+
1123 R"(
+
1124# comment
+
1125 # indented comment
+
1126)"
+
1127 "\x20\x20"
+
1128 R"(
+
1129[ips])"
+
1130 "\x20"
+
1131 R"(
+
1132r.ripple.com 51235
+
1133
+
1134 [ips_fixed])"
+
1135 "\x20\x20"
+
1136 R"(
+
1137 # COMMENT
+
1138 s1.ripple.com 51235
+
1139 s2.ripple.com 51235
-
1140)");
-
1141 cfg.loadFromString(toLoad);
+
1140
+
1141)");
-
1142 BEAST_EXPECT(
-
1143 cfg.exists("port_rpc") && cfg.section("port_rpc").lines().empty() &&
-
1144 cfg.section("port_rpc").values().empty());
-
1145 BEAST_EXPECT(
-
1146 cfg.exists(SECTION_IPS) && cfg.section(SECTION_IPS).lines().size() == 1 &&
-
1147 cfg.section(SECTION_IPS).values().size() == 1);
-
1148 BEAST_EXPECT(
-
1149 cfg.exists(SECTION_IPS_FIXED) && cfg.section(SECTION_IPS_FIXED).lines().size() == 2 &&
-
1150 cfg.section(SECTION_IPS_FIXED).values().size() == 2);
-
1151 }
-
1152
-
1153 void
-
1154 testColons()
-
1155 {
-
1156 Config cfg;
-
1157 /* NOTE: this string includes some explicit
-
1158 * space chars in order to verify proper trimming */
-
1159 std::string toLoad(R"(
-
1160[port_rpc])"
-
1161 "\x20"
-
1162 R"(
-
1163# comment
-
1164 # indented comment
-
1165)"
-
1166 "\x20\x20"
-
1167 R"(
-
1168[ips])"
-
1169 "\x20"
-
1170 R"(
-
1171r.ripple.com:51235
-
1172
-
1173 [ips_fixed])"
-
1174 "\x20\x20"
-
1175 R"(
-
1176 # COMMENT
-
1177 s1.ripple.com:51235
-
1178 s2.ripple.com 51235
-
1179 anotherserversansport
-
1180 anotherserverwithport:12
-
1181 1.1.1.1:1
-
1182 1.1.1.1 1
-
1183 12.34.12.123:12345
-
1184 12.34.12.123 12345
-
1185 ::
-
1186 2001:db8::
+
1142 cfg.loadFromString(toLoad);
+
1143 BEAST_EXPECT(
+
1144 cfg.exists("port_rpc") && cfg.section("port_rpc").lines().empty() &&
+
1145 cfg.section("port_rpc").values().empty());
+
1146 BEAST_EXPECT(
+
1147 cfg.exists(SECTION_IPS) && cfg.section(SECTION_IPS).lines().size() == 1 &&
+
1148 cfg.section(SECTION_IPS).values().size() == 1);
+
1149 BEAST_EXPECT(
+
1150 cfg.exists(SECTION_IPS_FIXED) && cfg.section(SECTION_IPS_FIXED).lines().size() == 2 &&
+
1151 cfg.section(SECTION_IPS_FIXED).values().size() == 2);
+
1152 }
+
1153
+
1154 void
+
1155 testColons()
+
1156 {
+
1157 Config cfg;
+
1158 /* NOTE: this string includes some explicit
+
1159 * space chars in order to verify proper trimming */
+
1160 std::string toLoad(
+
1161 R"(
+
1162[port_rpc])"
+
1163 "\x20"
+
1164 R"(
+
1165# comment
+
1166 # indented comment
+
1167)"
+
1168 "\x20\x20"
+
1169 R"(
+
1170[ips])"
+
1171 "\x20"
+
1172 R"(
+
1173r.ripple.com:51235
+
1174
+
1175 [ips_fixed])"
+
1176 "\x20\x20"
+
1177 R"(
+
1178 # COMMENT
+
1179 s1.ripple.com:51235
+
1180 s2.ripple.com 51235
+
1181 anotherserversansport
+
1182 anotherserverwithport:12
+
1183 1.1.1.1:1
+
1184 1.1.1.1 1
+
1185 12.34.12.123:12345
+
1186 12.34.12.123 12345
-
1187 ::1
-
1188 ::1:12345
+
1187 ::
+
1188 2001:db8::
-
1189 [::1]:12345
-
1190 2001:db8:3333:4444:5555:6666:7777:8888:12345
-
1191 [2001:db8:3333:4444:5555:6666:7777:8888]:1
-
1192
-
1193
-
1194)");
-
1195 cfg.loadFromString(toLoad);
-
1196 BEAST_EXPECT(
-
1197 cfg.exists("port_rpc") && cfg.section("port_rpc").lines().empty() &&
-
1198 cfg.section("port_rpc").values().empty());
-
1199 BEAST_EXPECT(
-
1200 cfg.exists(SECTION_IPS) && cfg.section(SECTION_IPS).lines().size() == 1 &&
-
1201 cfg.section(SECTION_IPS).values().size() == 1);
-
1202 BEAST_EXPECT(
-
1203 cfg.exists(SECTION_IPS_FIXED) && cfg.section(SECTION_IPS_FIXED).lines().size() == 15 &&
-
1204 cfg.section(SECTION_IPS_FIXED).values().size() == 15);
-
1205 BEAST_EXPECT(cfg.IPS[0] == "r.ripple.com 51235");
-
1206
-
1207 BEAST_EXPECT(cfg.IPS_FIXED[0] == "s1.ripple.com 51235");
-
1208 BEAST_EXPECT(cfg.IPS_FIXED[1] == "s2.ripple.com 51235");
-
1209 BEAST_EXPECT(cfg.IPS_FIXED[2] == "anotherserversansport");
-
1210 BEAST_EXPECT(cfg.IPS_FIXED[3] == "anotherserverwithport 12");
-
1211 BEAST_EXPECT(cfg.IPS_FIXED[4] == "1.1.1.1 1");
-
1212 BEAST_EXPECT(cfg.IPS_FIXED[5] == "1.1.1.1 1");
-
1213 BEAST_EXPECT(cfg.IPS_FIXED[6] == "12.34.12.123 12345");
-
1214 BEAST_EXPECT(cfg.IPS_FIXED[7] == "12.34.12.123 12345");
-
1215
-
1216 // all ipv6 should be ignored by colon replacer, howsoever formatted
-
1217 BEAST_EXPECT(cfg.IPS_FIXED[8] == "::");
-
1218 BEAST_EXPECT(cfg.IPS_FIXED[9] == "2001:db8::");
-
1219 BEAST_EXPECT(cfg.IPS_FIXED[10] == "::1");
-
1220 BEAST_EXPECT(cfg.IPS_FIXED[11] == "::1:12345");
-
1221 BEAST_EXPECT(cfg.IPS_FIXED[12] == "[::1]:12345");
-
1222 BEAST_EXPECT(cfg.IPS_FIXED[13] == "2001:db8:3333:4444:5555:6666:7777:8888:12345");
-
1223 BEAST_EXPECT(cfg.IPS_FIXED[14] == "[2001:db8:3333:4444:5555:6666:7777:8888]:1");
-
1224 }
-
1225
-
1226 void
-
1227 testComments()
-
1228 {
-
1229 struct TestCommentData
-
1230 {
-
1231 std::string_view line;
-
1232 std::string_view field;
-
1233 std::string_view expect;
-
1234 bool had_comment;
-
1235 };
-
1236
- -
1238 {{"password = aaaa\\#bbbb", "password", "aaaa#bbbb", false},
-
1239 {"password = aaaa#bbbb", "password", "aaaa", true},
-
1240 {"password = aaaa #bbbb", "password", "aaaa", true},
-
1241 // since the value is all comment, this doesn't parse as k=v :
-
1242 {"password = #aaaa #bbbb", "", "password =", true},
-
1243 {"password = aaaa\\# #bbbb", "password", "aaaa#", true},
-
1244 {"password = aaaa\\##bbbb", "password", "aaaa#", true},
-
1245 {"aaaa#bbbb", "", "aaaa", true},
-
1246 {"aaaa\\#bbbb", "", "aaaa#bbbb", false},
-
1247 {"aaaa\\##bbbb", "", "aaaa#", true},
+
1189 ::1
+
1190 ::1:12345
+
1191 [::1]:12345
+
1192 2001:db8:3333:4444:5555:6666:7777:8888:12345
+
1193 [2001:db8:3333:4444:5555:6666:7777:8888]:1
+
1194
+
1195
+
1196)");
+
1197 cfg.loadFromString(toLoad);
+
1198 BEAST_EXPECT(
+
1199 cfg.exists("port_rpc") && cfg.section("port_rpc").lines().empty() &&
+
1200 cfg.section("port_rpc").values().empty());
+
1201 BEAST_EXPECT(
+
1202 cfg.exists(SECTION_IPS) && cfg.section(SECTION_IPS).lines().size() == 1 &&
+
1203 cfg.section(SECTION_IPS).values().size() == 1);
+
1204 BEAST_EXPECT(
+
1205 cfg.exists(SECTION_IPS_FIXED) && cfg.section(SECTION_IPS_FIXED).lines().size() == 15 &&
+
1206 cfg.section(SECTION_IPS_FIXED).values().size() == 15);
+
1207 BEAST_EXPECT(cfg.IPS[0] == "r.ripple.com 51235");
+
1208
+
1209 BEAST_EXPECT(cfg.IPS_FIXED[0] == "s1.ripple.com 51235");
+
1210 BEAST_EXPECT(cfg.IPS_FIXED[1] == "s2.ripple.com 51235");
+
1211 BEAST_EXPECT(cfg.IPS_FIXED[2] == "anotherserversansport");
+
1212 BEAST_EXPECT(cfg.IPS_FIXED[3] == "anotherserverwithport 12");
+
1213 BEAST_EXPECT(cfg.IPS_FIXED[4] == "1.1.1.1 1");
+
1214 BEAST_EXPECT(cfg.IPS_FIXED[5] == "1.1.1.1 1");
+
1215 BEAST_EXPECT(cfg.IPS_FIXED[6] == "12.34.12.123 12345");
+
1216 BEAST_EXPECT(cfg.IPS_FIXED[7] == "12.34.12.123 12345");
+
1217
+
1218 // all ipv6 should be ignored by colon replacer, howsoever formatted
+
1219 BEAST_EXPECT(cfg.IPS_FIXED[8] == "::");
+
1220 BEAST_EXPECT(cfg.IPS_FIXED[9] == "2001:db8::");
+
1221 BEAST_EXPECT(cfg.IPS_FIXED[10] == "::1");
+
1222 BEAST_EXPECT(cfg.IPS_FIXED[11] == "::1:12345");
+
1223 BEAST_EXPECT(cfg.IPS_FIXED[12] == "[::1]:12345");
+
1224 BEAST_EXPECT(cfg.IPS_FIXED[13] == "2001:db8:3333:4444:5555:6666:7777:8888:12345");
+
1225 BEAST_EXPECT(cfg.IPS_FIXED[14] == "[2001:db8:3333:4444:5555:6666:7777:8888]:1");
+
1226 }
+
1227
+
1228 void
+
1229 testComments()
+
1230 {
+
1231 struct TestCommentData
+
1232 {
+
1233 std::string_view line;
+
1234 std::string_view field;
+
1235 std::string_view expect;
+
1236 bool had_comment;
+
1237 };
+
1238
+ +
1240 {{"password = aaaa\\#bbbb", "password", "aaaa#bbbb", false},
+
1241 {"password = aaaa#bbbb", "password", "aaaa", true},
+
1242 {"password = aaaa #bbbb", "password", "aaaa", true},
+
1243 // since the value is all comment, this doesn't parse as k=v :
+
1244 {"password = #aaaa #bbbb", "", "password =", true},
+
1245 {"password = aaaa\\# #bbbb", "password", "aaaa#", true},
+
1246 {"password = aaaa\\##bbbb", "password", "aaaa#", true},
+
1247 {"aaaa#bbbb", "", "aaaa", true},
-
1248 {"aaaa #bbbb", "", "aaaa", true},
-
1249 {"1 #comment", "", "1", true},
+
1248 {"aaaa\\#bbbb", "", "aaaa#bbbb", false},
+
1249 {"aaaa\\##bbbb", "", "aaaa#", true},
-
1250 {"#whole thing is comment", "", "", false},
-
1251 {" #whole comment with space", "", "", false}}};
-
1252
-
1253 for (auto const& t : tests)
-
1254 {
-
1255 Section s;
-
1256 s.append(t.line.data());
-
1257 BEAST_EXPECT(s.had_trailing_comments() == t.had_comment);
-
1258 if (t.field.empty())
-
1259 {
-
1260 BEAST_EXPECTS(s.legacy() == t.expect, s.legacy());
-
1261 }
-
1262 else
-
1263 {
- -
1265 BEAST_EXPECTS(set(field, t.field.data(), s), t.line);
-
1266 BEAST_EXPECTS(field == t.expect, t.line);
-
1267 }
-
1268 }
-
1269
-
1270 {
-
1271 Section s;
-
1272 s.append("online_delete = 3000");
-
1273 std::uint32_t od = 0;
-
1274 BEAST_EXPECT(set(od, "online_delete", s));
-
1275 BEAST_EXPECTS(od == 3000, *(s.get<std::string>("online_delete")));
-
1276 }
-
1277
-
1278 {
-
1279 Section s;
-
1280 s.append("online_delete = 2000 #my comment on this");
-
1281 std::uint32_t od = 0;
-
1282 BEAST_EXPECT(set(od, "online_delete", s));
-
1283 BEAST_EXPECTS(od == 2000, *(s.get<std::string>("online_delete")));
-
1284 }
-
1285 }
-
1286
-
1287 void
-
1288 testGetters()
-
1289 {
-
1290 using namespace std::string_literals;
-
1291 Section s{"MySection"};
-
1292 s.append("a_string = mystring");
-
1293 s.append("positive_int = 2");
-
1294 s.append("negative_int = -3");
-
1295 s.append("bool_ish = 1");
-
1296
-
1297 {
-
1298 auto val_1 = "value 1"s;
-
1299 BEAST_EXPECT(set(val_1, "a_string", s));
-
1300 BEAST_EXPECT(val_1 == "mystring");
-
1301
-
1302 auto val_2 = "value 2"s;
-
1303 BEAST_EXPECT(!set(val_2, "not_a_key", s));
-
1304 BEAST_EXPECT(val_2 == "value 2");
-
1305 BEAST_EXPECT(!set(val_2, "default"s, "not_a_key", s));
-
1306 BEAST_EXPECT(val_2 == "default");
-
1307
-
1308 auto val_3 = get<std::string>(s, "a_string");
-
1309 BEAST_EXPECT(val_3 == "mystring");
-
1310 auto val_4 = get<std::string>(s, "not_a_key");
-
1311 BEAST_EXPECT(val_4 == "");
-
1312 auto val_5 = get<std::string>(s, "not_a_key", "default");
-
1313 BEAST_EXPECT(val_5 == "default");
-
1314
-
1315 auto val_6 = "value 6"s;
-
1316 BEAST_EXPECT(get_if_exists(s, "a_string", val_6));
-
1317 BEAST_EXPECT(val_6 == "mystring");
-
1318
-
1319 auto val_7 = "value 7"s;
-
1320 BEAST_EXPECT(!get_if_exists(s, "not_a_key", val_7));
-
1321 BEAST_EXPECT(val_7 == "value 7");
-
1322 }
-
1323
-
1324 {
-
1325 int val_1 = 1;
-
1326 BEAST_EXPECT(set(val_1, "positive_int", s));
-
1327 BEAST_EXPECT(val_1 == 2);
-
1328
-
1329 int val_2 = 2;
-
1330 BEAST_EXPECT(set(val_2, "negative_int", s));
-
1331 BEAST_EXPECT(val_2 == -3);
-
1332
-
1333 int val_3 = 3;
-
1334 BEAST_EXPECT(!set(val_3, "a_string", s));
-
1335 BEAST_EXPECT(val_3 == 3);
-
1336
-
1337 auto val_4 = get<int>(s, "positive_int");
-
1338 BEAST_EXPECT(val_4 == 2);
-
1339 auto val_5 = get<int>(s, "not_a_key");
-
1340 BEAST_EXPECT(val_5 == 0);
-
1341 auto val_6 = get<int>(s, "not_a_key", 5);
+
1250 {"aaaa #bbbb", "", "aaaa", true},
+
1251 {"1 #comment", "", "1", true},
+
1252 {"#whole thing is comment", "", "", false},
+
1253 {" #whole comment with space", "", "", false}}};
+
1254
+
1255 for (auto const& t : tests)
+
1256 {
+
1257 Section s;
+
1258 s.append(t.line.data());
+
1259 BEAST_EXPECT(s.had_trailing_comments() == t.had_comment);
+
1260 if (t.field.empty())
+
1261 {
+
1262 BEAST_EXPECTS(s.legacy() == t.expect, s.legacy());
+
1263 }
+
1264 else
+
1265 {
+ +
1267 BEAST_EXPECTS(set(field, t.field.data(), s), t.line);
+
1268 BEAST_EXPECTS(field == t.expect, t.line);
+
1269 }
+
1270 }
+
1271
+
1272 {
+
1273 Section s;
+
1274 s.append("online_delete = 3000");
+
1275 std::uint32_t od = 0;
+
1276 BEAST_EXPECT(set(od, "online_delete", s));
+
1277 BEAST_EXPECTS(od == 3000, *(s.get<std::string>("online_delete")));
+
1278 }
+
1279
+
1280 {
+
1281 Section s;
+
1282 s.append("online_delete = 2000 #my comment on this");
+
1283 std::uint32_t od = 0;
+
1284 BEAST_EXPECT(set(od, "online_delete", s));
+
1285 BEAST_EXPECTS(od == 2000, *(s.get<std::string>("online_delete")));
+
1286 }
+
1287 }
+
1288
+
1289 void
+
1290 testGetters()
+
1291 {
+
1292 using namespace std::string_literals;
+
1293 Section s{"MySection"};
+
1294 s.append("a_string = mystring");
+
1295 s.append("positive_int = 2");
+
1296 s.append("negative_int = -3");
+
1297 s.append("bool_ish = 1");
+
1298
+
1299 {
+
1300 auto val_1 = "value 1"s;
+
1301 BEAST_EXPECT(set(val_1, "a_string", s));
+
1302 BEAST_EXPECT(val_1 == "mystring");
+
1303
+
1304 auto val_2 = "value 2"s;
+
1305 BEAST_EXPECT(!set(val_2, "not_a_key", s));
+
1306 BEAST_EXPECT(val_2 == "value 2");
+
1307 BEAST_EXPECT(!set(val_2, "default"s, "not_a_key", s));
+
1308 BEAST_EXPECT(val_2 == "default");
+
1309
+
1310 auto val_3 = get<std::string>(s, "a_string");
+
1311 BEAST_EXPECT(val_3 == "mystring");
+
1312 auto val_4 = get<std::string>(s, "not_a_key");
+
1313 BEAST_EXPECT(val_4 == "");
+
1314 auto val_5 = get<std::string>(s, "not_a_key", "default");
+
1315 BEAST_EXPECT(val_5 == "default");
+
1316
+
1317 auto val_6 = "value 6"s;
+
1318 BEAST_EXPECT(get_if_exists(s, "a_string", val_6));
+
1319 BEAST_EXPECT(val_6 == "mystring");
+
1320
+
1321 auto val_7 = "value 7"s;
+
1322 BEAST_EXPECT(!get_if_exists(s, "not_a_key", val_7));
+
1323 BEAST_EXPECT(val_7 == "value 7");
+
1324 }
+
1325
+
1326 {
+
1327 int val_1 = 1;
+
1328 BEAST_EXPECT(set(val_1, "positive_int", s));
+
1329 BEAST_EXPECT(val_1 == 2);
+
1330
+
1331 int val_2 = 2;
+
1332 BEAST_EXPECT(set(val_2, "negative_int", s));
+
1333 BEAST_EXPECT(val_2 == -3);
+
1334
+
1335 int val_3 = 3;
+
1336 BEAST_EXPECT(!set(val_3, "a_string", s));
+
1337 BEAST_EXPECT(val_3 == 3);
+
1338
+
1339 auto val_4 = get<int>(s, "positive_int");
+
1340 BEAST_EXPECT(val_4 == 2);
+
1341 auto val_5 = get<int>(s, "not_a_key");
-
1342 BEAST_EXPECT(val_6 == 5);
-
1343 auto val_7 = get<int>(s, "a_string", 6);
+
1342 BEAST_EXPECT(val_5 == 0);
+
1343 auto val_6 = get<int>(s, "not_a_key", 5);
-
1344 BEAST_EXPECT(val_7 == 6);
-
1345
-
1346 int val_8 = 8;
-
1347 BEAST_EXPECT(get_if_exists(s, "positive_int", val_8));
-
1348 BEAST_EXPECT(val_8 == 2);
-
1349
-
1350 auto val_9 = 9;
-
1351 BEAST_EXPECT(!get_if_exists(s, "not_a_key", val_9));
-
1352 BEAST_EXPECT(val_9 == 9);
-
1353
-
1354 auto val_10 = 10;
-
1355 BEAST_EXPECT(!get_if_exists(s, "a_string", val_10));
-
1356 BEAST_EXPECT(val_10 == 10);
-
1357
-
1358 BEAST_EXPECT(s.get<int>("not_a_key") == std::nullopt);
-
1359 try
-
1360 {
-
1361 s.get<int>("a_string");
-
1362 fail();
-
1363 }
-
1364 catch (boost::bad_lexical_cast&)
-
1365 {
-
1366 pass();
-
1367 }
-
1368 }
-
1369
-
1370 {
-
1371 bool flag_1 = false;
-
1372 BEAST_EXPECT(get_if_exists(s, "bool_ish", flag_1));
-
1373 BEAST_EXPECT(flag_1 == true);
-
1374
-
1375 bool flag_2 = false;
-
1376 BEAST_EXPECT(!get_if_exists(s, "not_a_key", flag_2));
-
1377 BEAST_EXPECT(flag_2 == false);
-
1378 }
-
1379 }
-
1380
-
1381 void
- -
1383 {
-
1384 testcase("amendment");
-
1385 struct ConfigUnit
-
1386 {
-
1387 std::string unit;
-
1388 std::uint32_t numSeconds;
-
1389 std::uint32_t configVal;
-
1390 bool shouldPass;
-
1391 };
+
1344 BEAST_EXPECT(val_6 == 5);
+
1345 auto val_7 = get<int>(s, "a_string", 6);
+
1346 BEAST_EXPECT(val_7 == 6);
+
1347
+
1348 int val_8 = 8;
+
1349 BEAST_EXPECT(get_if_exists(s, "positive_int", val_8));
+
1350 BEAST_EXPECT(val_8 == 2);
+
1351
+
1352 auto val_9 = 9;
+
1353 BEAST_EXPECT(!get_if_exists(s, "not_a_key", val_9));
+
1354 BEAST_EXPECT(val_9 == 9);
+
1355
+
1356 auto val_10 = 10;
+
1357 BEAST_EXPECT(!get_if_exists(s, "a_string", val_10));
+
1358 BEAST_EXPECT(val_10 == 10);
+
1359
+
1360 BEAST_EXPECT(s.get<int>("not_a_key") == std::nullopt);
+
1361 try
+
1362 {
+
1363 s.get<int>("a_string");
+
1364 fail();
+
1365 }
+
1366 catch (boost::bad_lexical_cast&)
+
1367 {
+
1368 pass();
+
1369 }
+
1370 }
+
1371
+
1372 {
+
1373 bool flag_1 = false;
+
1374 BEAST_EXPECT(get_if_exists(s, "bool_ish", flag_1));
+
1375 BEAST_EXPECT(flag_1 == true);
+
1376
+
1377 bool flag_2 = false;
+
1378 BEAST_EXPECT(!get_if_exists(s, "not_a_key", flag_2));
+
1379 BEAST_EXPECT(flag_2 == false);
+
1380 }
+
1381 }
+
1382
+
1383 void
+ +
1385 {
+
1386 testcase("amendment");
+
1387 struct ConfigUnit
+
1388 {
+
1389 std::string unit;
+
1390 std::uint32_t numSeconds;
+
1391 std::uint32_t configVal;
-
1392
-
1393 std::vector<ConfigUnit> units = {
+
1392 bool shouldPass;
+
1393 };
-
1394 {"seconds", 1, 15 * 60, false},
-
1395 {"minutes", 60, 14, false},
-
1396 {"minutes", 60, 15, true},
-
1397 {"hours", 3600, 10, true},
-
1398 {"days", 86400, 10, true},
-
1399 {"weeks", 604800, 2, true},
-
1400 {"months", 2592000, 1, false},
-
1401 {"years", 31536000, 1, false}};
-
1402
-
1403 std::string space = "";
-
1404 for (auto& [unit, sec, val, shouldPass] : units)
-
1405 {
-
1406 Config c;
-
1407 std::string toLoad(R"xrpldConfig(
-
1408[amendment_majority_time]
-
1409)xrpldConfig");
-
1410 toLoad += std::to_string(val) + space + unit;
-
1411 space = space == "" ? " " : "";
-
1412
-
1413 try
-
1414 {
-
1415 c.loadFromString(toLoad);
-
1416 if (shouldPass)
-
1417 BEAST_EXPECT(c.AMENDMENT_MAJORITY_TIME.count() == val * sec);
-
1418 else
-
1419 fail();
-
1420 }
-
1421 catch (std::runtime_error&)
-
1422 {
-
1423 if (!shouldPass)
-
1424 pass();
-
1425 else
-
1426 fail();
-
1427 }
-
1428 }
-
1429 }
-
1430
-
1431 void
-
1432 testOverlay()
-
1433 {
-
1434 testcase("overlay: unknown time");
-
1435
-
1436 auto testUnknown = [](std::string value) -> std::optional<std::chrono::seconds> {
-
1437 try
-
1438 {
-
1439 Config c;
-
1440 c.loadFromString("[overlay]\nmax_unknown_time=" + value);
-
1441 return c.MAX_UNKNOWN_TIME;
-
1442 }
-
1443 catch (std::runtime_error&)
-
1444 {
-
1445 return {};
-
1446 }
-
1447 };
-
1448
-
1449 // Failures
-
1450 BEAST_EXPECT(!testUnknown("none"));
-
1451 BEAST_EXPECT(!testUnknown("0.5"));
-
1452 BEAST_EXPECT(!testUnknown("180 seconds"));
-
1453 BEAST_EXPECT(!testUnknown("9 minutes"));
-
1454
-
1455 // Below lower bound
-
1456 BEAST_EXPECT(!testUnknown("299"));
-
1457
-
1458 // In bounds
-
1459 BEAST_EXPECT(testUnknown("300") == std::chrono::seconds{300});
-
1460 BEAST_EXPECT(testUnknown("301") == std::chrono::seconds{301});
-
1461 BEAST_EXPECT(testUnknown("1799") == std::chrono::seconds{1799});
-
1462 BEAST_EXPECT(testUnknown("1800") == std::chrono::seconds{1800});
-
1463
+ +
1395 std::vector<ConfigUnit> units = {
+
1396 {"seconds", 1, 15 * 60, false},
+
1397 {"minutes", 60, 14, false},
+
1398 {"minutes", 60, 15, true},
+
1399 {"hours", 3600, 10, true},
+
1400 {"days", 86400, 10, true},
+
1401 {"weeks", 604800, 2, true},
+
1402 {"months", 2592000, 1, false},
+
1403 {"years", 31536000, 1, false}};
+
1404
+
1405 std::string space = "";
+
1406 for (auto& [unit, sec, val, shouldPass] : units)
+
1407 {
+
1408 Config c;
+
1409 std::string toLoad(R"xrpldConfig(
+
1410[amendment_majority_time]
+
1411)xrpldConfig");
+
1412 toLoad += std::to_string(val) + space + unit;
+
1413 space = space == "" ? " " : "";
+
1414
+
1415 try
+
1416 {
+
1417 c.loadFromString(toLoad);
+
1418 if (shouldPass)
+
1419 BEAST_EXPECT(c.AMENDMENT_MAJORITY_TIME.count() == val * sec);
+
1420 else
+
1421 fail();
+
1422 }
+
1423 catch (std::runtime_error&)
+
1424 {
+
1425 if (!shouldPass)
+
1426 pass();
+
1427 else
+
1428 fail();
+
1429 }
+
1430 }
+
1431 }
+
1432
+
1433 void
+
1434 testOverlay()
+
1435 {
+
1436 testcase("overlay: unknown time");
+
1437
+
1438 auto testUnknown = [](std::string value) -> std::optional<std::chrono::seconds> {
+
1439 try
+
1440 {
+
1441 Config c;
+
1442 c.loadFromString("[overlay]\nmax_unknown_time=" + value);
+
1443 return c.MAX_UNKNOWN_TIME;
+
1444 }
+
1445 catch (std::runtime_error&)
+
1446 {
+
1447 return {};
+
1448 }
+
1449 };
+
1450
+
1451 // Failures
+
1452 BEAST_EXPECT(!testUnknown("none"));
+
1453 BEAST_EXPECT(!testUnknown("0.5"));
+
1454 BEAST_EXPECT(!testUnknown("180 seconds"));
+
1455 BEAST_EXPECT(!testUnknown("9 minutes"));
+
1456
+
1457 // Below lower bound
+
1458 BEAST_EXPECT(!testUnknown("299"));
+
1459
+
1460 // In bounds
+
1461 BEAST_EXPECT(testUnknown("300") == std::chrono::seconds{300});
+
1462 BEAST_EXPECT(testUnknown("301") == std::chrono::seconds{301});
+
1463 BEAST_EXPECT(testUnknown("1799") == std::chrono::seconds{1799});
-
1464 // Above upper bound
-
1465 BEAST_EXPECT(!testUnknown("1801"));
+
1464 BEAST_EXPECT(testUnknown("1800") == std::chrono::seconds{1800});
+
1465
- -
1467 testcase("overlay: diverged time");
+
1466 // Above upper bound
+
1467 BEAST_EXPECT(!testUnknown("1801"));
1468
-
1469 // In bounds:
-
1470 auto testDiverged = [](std::string value) -> std::optional<std::chrono::seconds> {
-
1471 try
-
1472 {
-
1473 Config c;
-
1474 c.loadFromString("[overlay]\nmax_diverged_time=" + value);
-
1475 return c.MAX_DIVERGED_TIME;
-
1476 }
-
1477 catch (std::runtime_error&)
-
1478 {
-
1479 return {};
-
1480 }
-
1481 };
-
1482
-
1483 // Failures
-
1484 BEAST_EXPECT(!testDiverged("none"));
+
1469 testcase("overlay: diverged time");
+
1470
+
1471 // In bounds:
+
1472 auto testDiverged = [](std::string value) -> std::optional<std::chrono::seconds> {
+
1473 try
+
1474 {
+
1475 Config c;
+
1476 c.loadFromString("[overlay]\nmax_diverged_time=" + value);
+
1477 return c.MAX_DIVERGED_TIME;
+
1478 }
+
1479 catch (std::runtime_error&)
+
1480 {
+
1481 return {};
+
1482 }
+
1483 };
+
1484
-
1485 BEAST_EXPECT(!testDiverged("0.5"));
-
1486 BEAST_EXPECT(!testDiverged("180 seconds"));
-
1487 BEAST_EXPECT(!testDiverged("9 minutes"));
-
1488
-
1489 // Below lower bound
-
1490 BEAST_EXPECT(!testDiverged("0"));
-
1491 BEAST_EXPECT(!testDiverged("59"));
-
1492
-
1493 // In bounds
-
1494 BEAST_EXPECT(testDiverged("60") == std::chrono::seconds{60});
-
1495 BEAST_EXPECT(testDiverged("61") == std::chrono::seconds{61});
-
1496 BEAST_EXPECT(testDiverged("899") == std::chrono::seconds{899});
-
1497 BEAST_EXPECT(testDiverged("900") == std::chrono::seconds{900});
-
1498
-
1499 // Above upper bound
-
1500 BEAST_EXPECT(!testDiverged("901"));
-
1501 }
-
1502
-
1503 void
-
1504 run() override
-
1505 {
-
1506 testLegacy();
- -
1508 testDbPath();
- - -
1511 testSetup(false);
-
1512 testSetup(true);
-
1513 testPort();
-
1514 testZeroPort();
- -
1516 testColons();
-
1517 testComments();
-
1518 testGetters();
-
1519 testAmendment();
+
1485 // Failures
+
1486 BEAST_EXPECT(!testDiverged("none"));
+
1487 BEAST_EXPECT(!testDiverged("0.5"));
+
1488 BEAST_EXPECT(!testDiverged("180 seconds"));
+
1489 BEAST_EXPECT(!testDiverged("9 minutes"));
+
1490
+
1491 // Below lower bound
+
1492 BEAST_EXPECT(!testDiverged("0"));
+
1493 BEAST_EXPECT(!testDiverged("59"));
+
1494
+
1495 // In bounds
+
1496 BEAST_EXPECT(testDiverged("60") == std::chrono::seconds{60});
+
1497 BEAST_EXPECT(testDiverged("61") == std::chrono::seconds{61});
+
1498 BEAST_EXPECT(testDiverged("899") == std::chrono::seconds{899});
+
1499 BEAST_EXPECT(testDiverged("900") == std::chrono::seconds{900});
+
1500
+
1501 // Above upper bound
+
1502 BEAST_EXPECT(!testDiverged("901"));
+
1503 }
+
1504
+
1505 void
+
1506 run() override
+
1507 {
+
1508 testLegacy();
+ +
1510 testDbPath();
+ + +
1513 testSetup(false);
+
1514 testSetup(true);
+
1515 testPort();
+
1516 testZeroPort();
+ +
1518 testColons();
+
1519 testComments();
+
1520 testGetters();
+
1521 testAmendment();
-
1520 testOverlay();
- -
1522 }
-
1523};
-
1524
-
1525BEAST_DEFINE_TESTSUITE(Config, core, xrpl);
+
1522 testOverlay();
+ +
1524 }
+
1525};
1526
-
1527} // namespace xrpl
+
1527BEAST_DEFINE_TESTSUITE(Config, core, xrpl);
+
1528
+
1529} // namespace xrpl
@@ -1715,6 +1717,7 @@ $(document).ready(function() { init_codefold(0); });
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition Config.cpp:440
std::uint32_t LEDGER_HISTORY
Definition Config.h:188
std::vector< std::string > IPS_FIXED
Definition Config.h:127
+
std::chrono::seconds AMENDMENT_MAJORITY_TIME
Definition Config.h:212
static char const *const databaseDirName
Definition Config.h:73
std::vector< std::string > const & values() const
Returns all the values in the section.
Definition BasicConfig.h:58
std::vector< std::string > const & lines() const
Returns all the lines in the section.
Definition BasicConfig.h:49
@@ -1745,6 +1748,7 @@ $(document).ready(function() { init_codefold(0); });
ValidatorsTxtGuard(beast::unit_test::suite &test, path subDir, path const &validatorsFileName, bool useCounter=true)
T close(T... args)
+
T count(T... args)
T empty(T... args)
T endl(T... args)
@@ -1768,7 +1772,6 @@ $(document).ready(function() { init_codefold(0); });
T size(T... args)
-
T starts_with(T... args)
std::vector< boost::asio::ip::network_v4 > admin_nets_v4
Definition Port.h:97
diff --git a/ConnectAttempt_8cpp_source.html b/ConnectAttempt_8cpp_source.html index eadbf6c322..1ef4eee5f7 100644 --- a/ConnectAttempt_8cpp_source.html +++ b/ConnectAttempt_8cpp_source.html @@ -284,400 +284,402 @@ $(document).ready(function() { init_codefold(0); });
180 try
181 {
182 timer_.expires_after(connectTimeout);
-
183 timer_.async_wait(boost::asio::bind_executor(
-
184 strand_, std::bind(&ConnectAttempt::onTimer, shared_from_this(), std::placeholders::_1)));
-
185 }
-
186 catch (std::exception const& ex)
-
187 {
-
188 JLOG(journal_.error()) << "setTimer (global): " << ex.what();
-
189 return close();
-
190 }
-
191 }
-
192
-
193 // Set step-specific timer
-
194 try
-
195 {
-
196 std::chrono::seconds stepTimeout;
-
197 switch (step)
-
198 {
- -
200 stepTimeout = StepTimeouts::tcpConnect;
-
201 break;
- -
203 stepTimeout = StepTimeouts::tlsHandshake;
-
204 break;
- -
206 stepTimeout = StepTimeouts::httpWrite;
-
207 break;
- -
209 stepTimeout = StepTimeouts::httpRead;
-
210 break;
- -
212 stepTimeout = StepTimeouts::tlsShutdown;
-
213 break;
- - -
216 return; // No timer needed for init or complete step
-
217 }
-
218
-
219 // call to expires_after cancels previous timer
-
220 stepTimer_.expires_after(stepTimeout);
-
221 stepTimer_.async_wait(boost::asio::bind_executor(
-
222 strand_, std::bind(&ConnectAttempt::onTimer, shared_from_this(), std::placeholders::_1)));
-
223
-
224 JLOG(journal_.trace()) << "setTimer: " << stepToString(step) << " timeout=" << stepTimeout.count() << "s";
-
225 }
-
226 catch (std::exception const& ex)
-
227 {
-
228 JLOG(journal_.error()) << "setTimer (step " << stepToString(step) << "): " << ex.what();
-
229 return close();
-
230 }
-
231}
+
183 timer_.async_wait(
+
184 boost::asio::bind_executor(
+
185 strand_, std::bind(&ConnectAttempt::onTimer, shared_from_this(), std::placeholders::_1)));
+
186 }
+
187 catch (std::exception const& ex)
+
188 {
+
189 JLOG(journal_.error()) << "setTimer (global): " << ex.what();
+
190 return close();
+
191 }
+
192 }
+
193
+
194 // Set step-specific timer
+
195 try
+
196 {
+
197 std::chrono::seconds stepTimeout;
+
198 switch (step)
+
199 {
+ +
201 stepTimeout = StepTimeouts::tcpConnect;
+
202 break;
+ +
204 stepTimeout = StepTimeouts::tlsHandshake;
+
205 break;
+ +
207 stepTimeout = StepTimeouts::httpWrite;
+
208 break;
+ +
210 stepTimeout = StepTimeouts::httpRead;
+
211 break;
+ +
213 stepTimeout = StepTimeouts::tlsShutdown;
+
214 break;
+ + +
217 return; // No timer needed for init or complete step
+
218 }
+
219
+
220 // call to expires_after cancels previous timer
+
221 stepTimer_.expires_after(stepTimeout);
+
222 stepTimer_.async_wait(
+
223 boost::asio::bind_executor(
+
224 strand_, std::bind(&ConnectAttempt::onTimer, shared_from_this(), std::placeholders::_1)));
+
225
+
226 JLOG(journal_.trace()) << "setTimer: " << stepToString(step) << " timeout=" << stepTimeout.count() << "s";
+
227 }
+
228 catch (std::exception const& ex)
+
229 {
+
230 JLOG(journal_.error()) << "setTimer (step " << stepToString(step) << "): " << ex.what();
+
231 return close();
+
232 }
+
233}
-
232
-
233void
-
- -
235{
-
236 try
-
237 {
-
238 timer_.cancel();
-
239 stepTimer_.cancel();
-
240 }
-
241 catch (boost::system::system_error const&)
-
242 {
-
243 // ignored
-
244 }
-
245}
+
234
+
235void
+
+ +
237{
+
238 try
+
239 {
+
240 timer_.cancel();
+
241 stepTimer_.cancel();
+
242 }
+
243 catch (boost::system::system_error const&)
+
244 {
+
245 // ignored
+
246 }
+
247}
-
246
-
247void
-
- -
249{
-
250 if (!socket_.is_open())
-
251 return;
-
252
-
253 if (ec)
-
254 {
-
255 // do not initiate shutdown, timers are frequently cancelled
-
256 if (ec == boost::asio::error::operation_aborted)
-
257 return;
-
258
-
259 // This should never happen
-
260 JLOG(journal_.error()) << "onTimer: " << ec.message();
-
261 return close();
-
262 }
-
263
-
264 // Determine which timer expired by checking their expiry times
-
265 auto const now = std::chrono::steady_clock::now();
-
266 bool globalExpired = (timer_.expiry() <= now);
-
267 bool stepExpired = (stepTimer_.expiry() <= now);
-
268
-
269 if (globalExpired)
-
270 {
-
271 JLOG(journal_.debug()) << "onTimer: Global timeout; step: " << stepToString(currentStep_);
-
272 }
-
273 else if (stepExpired)
-
274 {
-
275 JLOG(journal_.debug()) << "onTimer: Step timeout; step: " << stepToString(currentStep_);
-
276 }
-
277 else
-
278 {
-
279 JLOG(journal_.warn()) << "onTimer: Unexpected timer callback";
-
280 }
-
281
-
282 close();
-
283}
+
248
+
249void
+
+ +
251{
+
252 if (!socket_.is_open())
+
253 return;
+
254
+
255 if (ec)
+
256 {
+
257 // do not initiate shutdown, timers are frequently cancelled
+
258 if (ec == boost::asio::error::operation_aborted)
+
259 return;
+
260
+
261 // This should never happen
+
262 JLOG(journal_.error()) << "onTimer: " << ec.message();
+
263 return close();
+
264 }
+
265
+
266 // Determine which timer expired by checking their expiry times
+
267 auto const now = std::chrono::steady_clock::now();
+
268 bool globalExpired = (timer_.expiry() <= now);
+
269 bool stepExpired = (stepTimer_.expiry() <= now);
+
270
+
271 if (globalExpired)
+
272 {
+
273 JLOG(journal_.debug()) << "onTimer: Global timeout; step: " << stepToString(currentStep_);
+
274 }
+
275 else if (stepExpired)
+
276 {
+
277 JLOG(journal_.debug()) << "onTimer: Step timeout; step: " << stepToString(currentStep_);
+
278 }
+
279 else
+
280 {
+
281 JLOG(journal_.warn()) << "onTimer: Unexpected timer callback";
+
282 }
+
283
+
284 close();
+
285}
-
284
-
285void
-
- -
287{
-
288 ioPending_ = false;
-
289
-
290 if (ec)
-
291 {
-
292 if (ec == boost::asio::error::operation_aborted)
-
293 return tryAsyncShutdown();
-
294
-
295 return fail("onConnect", ec);
-
296 }
-
297
-
298 if (!socket_.is_open())
-
299 return;
-
300
-
301 // check if connection has really been established
-
302 socket_.local_endpoint(ec);
-
303 if (ec)
-
304 return fail("onConnect", ec);
-
305
-
306 if (shutdown_)
-
307 return tryAsyncShutdown();
-
308
-
309 ioPending_ = true;
+
286
+
287void
+
+ +
289{
+
290 ioPending_ = false;
+
291
+
292 if (ec)
+
293 {
+
294 if (ec == boost::asio::error::operation_aborted)
+
295 return tryAsyncShutdown();
+
296
+
297 return fail("onConnect", ec);
+
298 }
+
299
+
300 if (!socket_.is_open())
+
301 return;
+
302
+
303 // check if connection has really been established
+
304 socket_.local_endpoint(ec);
+
305 if (ec)
+
306 return fail("onConnect", ec);
+
307
+
308 if (shutdown_)
+
309 return tryAsyncShutdown();
310
- +
311 ioPending_ = true;
312
-
313 stream_.set_verify_mode(boost::asio::ssl::verify_none);
-
314 stream_.async_handshake(
-
315 boost::asio::ssl::stream_base::client,
-
316 boost::asio::bind_executor(
-
317 strand_, std::bind(&ConnectAttempt::onHandshake, shared_from_this(), std::placeholders::_1)));
-
318}
+ +
314
+
315 stream_.set_verify_mode(boost::asio::ssl::verify_none);
+
316 stream_.async_handshake(
+
317 boost::asio::ssl::stream_base::client,
+
318 boost::asio::bind_executor(
+
319 strand_, std::bind(&ConnectAttempt::onHandshake, shared_from_this(), std::placeholders::_1)));
+
320}
-
319
-
320void
-
- -
322{
-
323 ioPending_ = false;
-
324
-
325 if (ec)
-
326 {
-
327 if (ec == boost::asio::error::operation_aborted)
-
328 return tryAsyncShutdown();
-
329
-
330 return fail("onHandshake", ec);
-
331 }
-
332
-
333 auto const local_endpoint = socket_.local_endpoint(ec);
-
334 if (ec)
-
335 return fail("onHandshake", ec);
-
336
- +
321
+
322void
+
+ +
324{
+
325 ioPending_ = false;
+
326
+
327 if (ec)
+
328 {
+
329 if (ec == boost::asio::error::operation_aborted)
+
330 return tryAsyncShutdown();
+
331
+
332 return fail("onHandshake", ec);
+
333 }
+
334
+
335 auto const local_endpoint = socket_.local_endpoint(ec);
+
336 if (ec)
+
337 return fail("onHandshake", ec);
338
-
339 // check if we connected to ourselves
- -
341 return fail("Self connection");
-
342
-
343 auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
-
344 if (!sharedValue)
-
345 return shutdown(); // makeSharedValue logs
-
346
- - - - - - -
353
- -
355 req_, *sharedValue, overlay_.setup().networkID, overlay_.setup().public_ip, remote_endpoint_.address(), app_);
-
356
-
357 if (shutdown_)
-
358 return tryAsyncShutdown();
-
359
-
360 ioPending_ = true;
+ +
340
+
341 // check if we connected to ourselves
+ +
343 return fail("Self connection");
+
344
+
345 auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
+
346 if (!sharedValue)
+
347 return shutdown(); // makeSharedValue logs
+
348
+ + + + + + +
355
+ +
357 req_, *sharedValue, overlay_.setup().networkID, overlay_.setup().public_ip, remote_endpoint_.address(), app_);
+
358
+
359 if (shutdown_)
+
360 return tryAsyncShutdown();
361
-
362 boost::beast::http::async_write(
-
363 stream_,
-
364 req_,
-
365 boost::asio::bind_executor(
-
366 strand_, std::bind(&ConnectAttempt::onWrite, shared_from_this(), std::placeholders::_1)));
-
367}
+
362 ioPending_ = true;
+
363
+
364 boost::beast::http::async_write(
+
365 stream_,
+
366 req_,
+
367 boost::asio::bind_executor(
+
368 strand_, std::bind(&ConnectAttempt::onWrite, shared_from_this(), std::placeholders::_1)));
+
369}
-
368
-
369void
-
- -
371{
-
372 ioPending_ = false;
-
373
-
374 if (ec)
-
375 {
-
376 if (ec == boost::asio::error::operation_aborted)
-
377 return tryAsyncShutdown();
-
378
-
379 return fail("onWrite", ec);
-
380 }
-
381
-
382 if (shutdown_)
-
383 return tryAsyncShutdown();
-
384
-
385 ioPending_ = true;
+
370
+
371void
+
+ +
373{
+
374 ioPending_ = false;
+
375
+
376 if (ec)
+
377 {
+
378 if (ec == boost::asio::error::operation_aborted)
+
379 return tryAsyncShutdown();
+
380
+
381 return fail("onWrite", ec);
+
382 }
+
383
+
384 if (shutdown_)
+
385 return tryAsyncShutdown();
386
- +
387 ioPending_ = true;
388
-
389 boost::beast::http::async_read(
-
390 stream_,
-
391 read_buf_,
-
392 response_,
-
393 boost::asio::bind_executor(
-
394 strand_, std::bind(&ConnectAttempt::onRead, shared_from_this(), std::placeholders::_1)));
-
395}
+ +
390
+
391 boost::beast::http::async_read(
+
392 stream_,
+
393 read_buf_,
+
394 response_,
+
395 boost::asio::bind_executor(
+
396 strand_, std::bind(&ConnectAttempt::onRead, shared_from_this(), std::placeholders::_1)));
+
397}
-
396
-
397void
-
- -
399{
-
400 cancelTimer();
-
401 ioPending_ = false;
- -
403
-
404 if (ec)
-
405 {
-
406 if (ec == boost::asio::error::eof)
-
407 {
-
408 JLOG(journal_.debug()) << "EOF";
-
409 return shutdown();
-
410 }
-
411
-
412 if (ec == boost::asio::error::operation_aborted)
-
413 return tryAsyncShutdown();
-
414
-
415 return fail("onRead", ec);
-
416 }
-
417
-
418 if (shutdown_)
-
419 return tryAsyncShutdown();
-
420
- -
422}
+
398
+
399void
+
+ +
401{
+
402 cancelTimer();
+
403 ioPending_ = false;
+ +
405
+
406 if (ec)
+
407 {
+
408 if (ec == boost::asio::error::eof)
+
409 {
+
410 JLOG(journal_.debug()) << "EOF";
+
411 return shutdown();
+
412 }
+
413
+
414 if (ec == boost::asio::error::operation_aborted)
+
415 return tryAsyncShutdown();
+
416
+
417 return fail("onRead", ec);
+
418 }
+
419
+
420 if (shutdown_)
+
421 return tryAsyncShutdown();
+
422
+ +
424}
-
423
-
424//--------------------------------------------------------------------------
425
-
426void
-
- -
428{
- -
430 {
-
431 // A peer may respond with service_unavailable and a list of alternative
-
432 // peers to connect to, a differing status code is unexpected
-
433 if (response_.result() != boost::beast::http::status::service_unavailable)
-
434 {
-
435 JLOG(journal_.warn()) << "Unable to upgrade to peer protocol: " << response_.result() << " ("
-
436 << response_.reason() << ")";
-
437 return shutdown();
-
438 }
-
439
-
440 // Parse response body to determine if this is a redirect or other
-
441 // service unavailable
-
442 std::string responseBody;
-
443 responseBody.reserve(boost::asio::buffer_size(response_.body().data()));
-
444 for (auto const buffer : response_.body().data())
-
445 responseBody.append(static_cast<char const*>(buffer.data()), boost::asio::buffer_size(buffer));
-
446
-
447 Json::Value json;
-
448 Json::Reader reader;
-
449 auto const isValidJson = reader.parse(responseBody, json);
-
450
-
451 // Check if this is a redirect response (contains peer-ips field)
-
452 auto const isRedirect = isValidJson && json.isObject() && json.isMember("peer-ips");
-
453
-
454 if (!isRedirect)
-
455 {
-
456 JLOG(journal_.warn()) << "processResponse: " << remote_endpoint_
-
457 << " failed to upgrade to peer protocol: " << response_.result() << " ("
-
458 << response_.reason() << ")";
-
459
-
460 return shutdown();
-
461 }
-
462
-
463 Json::Value const& peerIps = json["peer-ips"];
-
464 if (!peerIps.isArray())
-
465 return fail("processResponse: invalid peer-ips format");
-
466
-
467 // Extract and validate peer endpoints
- -
469 redirectEndpoints.reserve(peerIps.size());
-
470
-
471 for (auto const& ipValue : peerIps)
-
472 {
-
473 if (!ipValue.isString())
-
474 continue;
-
475
-
476 error_code ec;
-
477 auto const endpoint = parse_endpoint(ipValue.asString(), ec);
-
478 if (!ec)
-
479 redirectEndpoints.push_back(endpoint);
-
480 }
-
481
-
482 // Notify PeerFinder about the redirect redirectEndpoints may be empty
-
483 overlay_.peerFinder().onRedirects(remote_endpoint_, redirectEndpoints);
-
484
-
485 return fail("processResponse: failed to connect to peer: redirected");
-
486 }
-
487
-
488 // Just because our peer selected a particular protocol version doesn't
-
489 // mean that it's acceptable to us. Check that it is:
-
490 std::optional<ProtocolVersion> negotiatedProtocol;
-
491
-
492 {
-
493 auto const pvs = parseProtocolVersions(response_["Upgrade"]);
-
494
-
495 if (pvs.size() == 1 && isProtocolSupported(pvs[0]))
-
496 negotiatedProtocol = pvs[0];
-
497
-
498 if (!negotiatedProtocol)
-
499 return fail("processResponse: Unable to negotiate protocol version");
-
500 }
-
501
-
502 auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
-
503 if (!sharedValue)
-
504 return shutdown(); // makeSharedValue logs
-
505
-
506 try
-
507 {
-
508 auto const publicKey = verifyHandshake(
-
509 response_,
-
510 *sharedValue,
- - -
513 remote_endpoint_.address(),
-
514 app_);
-
515
-
516 usage_.setPublicKey(publicKey);
+
426//--------------------------------------------------------------------------
+
427
+
428void
+
+ +
430{
+ +
432 {
+
433 // A peer may respond with service_unavailable and a list of alternative
+
434 // peers to connect to, a differing status code is unexpected
+
435 if (response_.result() != boost::beast::http::status::service_unavailable)
+
436 {
+
437 JLOG(journal_.warn()) << "Unable to upgrade to peer protocol: " << response_.result() << " ("
+
438 << response_.reason() << ")";
+
439 return shutdown();
+
440 }
+
441
+
442 // Parse response body to determine if this is a redirect or other
+
443 // service unavailable
+
444 std::string responseBody;
+
445 responseBody.reserve(boost::asio::buffer_size(response_.body().data()));
+
446 for (auto const buffer : response_.body().data())
+
447 responseBody.append(static_cast<char const*>(buffer.data()), boost::asio::buffer_size(buffer));
+
448
+
449 Json::Value json;
+
450 Json::Reader reader;
+
451 auto const isValidJson = reader.parse(responseBody, json);
+
452
+
453 // Check if this is a redirect response (contains peer-ips field)
+
454 auto const isRedirect = isValidJson && json.isObject() && json.isMember("peer-ips");
+
455
+
456 if (!isRedirect)
+
457 {
+
458 JLOG(journal_.warn()) << "processResponse: " << remote_endpoint_
+
459 << " failed to upgrade to peer protocol: " << response_.result() << " ("
+
460 << response_.reason() << ")";
+
461
+
462 return shutdown();
+
463 }
+
464
+
465 Json::Value const& peerIps = json["peer-ips"];
+
466 if (!peerIps.isArray())
+
467 return fail("processResponse: invalid peer-ips format");
+
468
+
469 // Extract and validate peer endpoints
+ +
471 redirectEndpoints.reserve(peerIps.size());
+
472
+
473 for (auto const& ipValue : peerIps)
+
474 {
+
475 if (!ipValue.isString())
+
476 continue;
+
477
+
478 error_code ec;
+
479 auto const endpoint = parse_endpoint(ipValue.asString(), ec);
+
480 if (!ec)
+
481 redirectEndpoints.push_back(endpoint);
+
482 }
+
483
+
484 // Notify PeerFinder about the redirect redirectEndpoints may be empty
+
485 overlay_.peerFinder().onRedirects(remote_endpoint_, redirectEndpoints);
+
486
+
487 return fail("processResponse: failed to connect to peer: redirected");
+
488 }
+
489
+
490 // Just because our peer selected a particular protocol version doesn't
+
491 // mean that it's acceptable to us. Check that it is:
+
492 std::optional<ProtocolVersion> negotiatedProtocol;
+
493
+
494 {
+
495 auto const pvs = parseProtocolVersions(response_["Upgrade"]);
+
496
+
497 if (pvs.size() == 1 && isProtocolSupported(pvs[0]))
+
498 negotiatedProtocol = pvs[0];
+
499
+
500 if (!negotiatedProtocol)
+
501 return fail("processResponse: Unable to negotiate protocol version");
+
502 }
+
503
+
504 auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
+
505 if (!sharedValue)
+
506 return shutdown(); // makeSharedValue logs
+
507
+
508 try
+
509 {
+
510 auto const publicKey = verifyHandshake(
+
511 response_,
+
512 *sharedValue,
+ + +
515 remote_endpoint_.address(),
+
516 app_);
517
-
518 JLOG(journal_.debug()) << "Protocol: " << to_string(*negotiatedProtocol);
-
519 JLOG(journal_.info()) << "Public Key: " << toBase58(TokenType::NodePublic, publicKey);
-
520
-
521 auto const member = app_.cluster().member(publicKey);
-
522 if (member)
-
523 {
-
524 JLOG(journal_.info()) << "Cluster name: " << *member;
-
525 }
-
526
-
527 auto const result = overlay_.peerFinder().activate(slot_, publicKey, member.has_value());
-
528 if (result != PeerFinder::Result::success)
-
529 {
- -
531 ss << "Outbound Connect Attempt " << remote_endpoint_ << " " << to_string(result);
-
532 return fail(ss.str());
-
533 }
-
534
-
535 if (!socket_.is_open())
-
536 return;
-
537
-
538 if (shutdown_)
-
539 return tryAsyncShutdown();
-
540
-
541 auto const peer = std::make_shared<PeerImp>(
-
542 app_,
-
543 std::move(stream_ptr_),
-
544 read_buf_.data(),
-
545 std::move(slot_),
-
546 std::move(response_),
-
547 usage_,
-
548 publicKey,
-
549 *negotiatedProtocol,
-
550 id_,
-
551 overlay_);
-
552
-
553 overlay_.add_active(peer);
-
554 }
-
555 catch (std::exception const& e)
-
556 {
-
557 return fail(std::string("Handshake failure (") + e.what() + ")");
-
558 }
-
559}
+
518 usage_.setPublicKey(publicKey);
+
519
+
520 JLOG(journal_.debug()) << "Protocol: " << to_string(*negotiatedProtocol);
+
521 JLOG(journal_.info()) << "Public Key: " << toBase58(TokenType::NodePublic, publicKey);
+
522
+
523 auto const member = app_.cluster().member(publicKey);
+
524 if (member)
+
525 {
+
526 JLOG(journal_.info()) << "Cluster name: " << *member;
+
527 }
+
528
+
529 auto const result = overlay_.peerFinder().activate(slot_, publicKey, member.has_value());
+
530 if (result != PeerFinder::Result::success)
+
531 {
+ +
533 ss << "Outbound Connect Attempt " << remote_endpoint_ << " " << to_string(result);
+
534 return fail(ss.str());
+
535 }
+
536
+
537 if (!socket_.is_open())
+
538 return;
+
539
+
540 if (shutdown_)
+
541 return tryAsyncShutdown();
+
542
+
543 auto const peer = std::make_shared<PeerImp>(
+
544 app_,
+
545 std::move(stream_ptr_),
+
546 read_buf_.data(),
+
547 std::move(slot_),
+
548 std::move(response_),
+
549 usage_,
+
550 publicKey,
+
551 *negotiatedProtocol,
+
552 id_,
+
553 overlay_);
+
554
+
555 overlay_.add_active(peer);
+
556 }
+
557 catch (std::exception const& e)
+
558 {
+
559 return fail(std::string("Handshake failure (") + e.what() + ")");
+
560 }
+
561}
-
560
-
561} // namespace xrpl
+
562
+
563} // namespace xrpl
T append(T... args)
@@ -685,10 +687,10 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
+
bool isArray() const
UInt size() const
Number of values in array or object.
-
bool isObject() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isObject() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:318
Stream debug() const
Definition Journal.h:300
@@ -708,16 +710,16 @@ $(document).ready(function() { init_codefold(0); });
boost::asio::basic_waitable_timer< std::chrono::steady_clock > timer_
void setTimer(ConnectionStep step)
Set timers for the specified connection step.
-
void processResponse()
Process the HTTP upgrade response from peer.
+
void processResponse()
Process the HTTP upgrade response from peer.
boost::asio::strand< boost::asio::io_context::executor_type > strand_
void stop() override
Stop the connection attempt.
ConnectionStep currentStep_
-
void onRead(error_code ec)
+
void onRead(error_code ec)
static std::string stepToString(ConnectionStep step)
std::unique_ptr< stream_type > stream_ptr_
-
void onConnect(error_code ec)
+
void onConnect(error_code ec)
void onShutdown(error_code ec)
std::shared_ptr< PeerFinder::Slot > slot_
@@ -741,7 +743,7 @@ $(document).ready(function() { init_codefold(0); });
boost::asio::ip::tcp::endpoint endpoint_type
endpoint_type remote_endpoint_
boost::asio::basic_waitable_timer< std::chrono::steady_clock > stepTimer_
-
void onHandshake(error_code ec)
+
void onHandshake(error_code ec)
static boost::asio::ip::tcp::endpoint parse_endpoint(std::string const &s, boost::system::error_code &ec)
ConnectAttempt(Application &app, boost::asio::io_context &io_context, endpoint_type const &remote_endpoint, Resource::Consumer usage, shared_context const &context, Peer::id_t id, std::shared_ptr< PeerFinder::Slot > const &slot, beast::Journal journal, OverlayImpl &overlay)
Construct a new ConnectAttempt object.
@@ -749,13 +751,13 @@ $(document).ready(function() { init_codefold(0); });
static constexpr std::chrono::seconds connectTimeout
Resource::Consumer usage_
beast::Journal const journal_
-
void onWrite(error_code ec)
-
void onTimer(error_code ec)
Handle timer expiration events.
-
void cancelTimer()
Cancel both global and step timers.
+
void onWrite(error_code ec)
+
void onTimer(error_code ec)
Handle timer expiration events.
+
void cancelTimer()
Cancel both global and step timers.
-
static bool isPeerUpgrade(http_request_type const &request)
-
void add_active(std::shared_ptr< PeerImp > const &peer)
+
static bool isPeerUpgrade(http_request_type const &request)
+
void add_active(std::shared_ptr< PeerImp > const &peer)
PeerFinder::Manager & peerFinder()
Setup const & setup() const
virtual void onRedirects(boost::asio::ip::tcp::endpoint const &remote_address, std::vector< boost::asio::ip::tcp::endpoint > const &eps)=0
Called when we received redirect IPs from a busy peer.
diff --git a/ConnectAttempt_8h_source.html b/ConnectAttempt_8h_source.html index 9b9cba91f6..2dc80bbd26 100644 --- a/ConnectAttempt_8h_source.html +++ b/ConnectAttempt_8h_source.html @@ -276,17 +276,17 @@ $(document).ready(function() { init_codefold(0); });
boost::beast::http::request< boost::beast::http::empty_body > request_type
void setTimer(ConnectionStep step)
Set timers for the specified connection step.
-
void processResponse()
Process the HTTP upgrade response from peer.
+
void processResponse()
Process the HTTP upgrade response from peer.
boost::asio::strand< boost::asio::io_context::executor_type > strand_
void stop() override
Stop the connection attempt.
boost::beast::tcp_stream middle_type
ConnectionStep currentStep_
-
void onRead(error_code ec)
+
void onRead(error_code ec)
static std::string stepToString(ConnectionStep step)
std::unique_ptr< stream_type > stream_ptr_
-
void onConnect(error_code ec)
+
void onConnect(error_code ec)
void onShutdown(error_code ec)
std::shared_ptr< PeerFinder::Slot > slot_
@@ -310,7 +310,7 @@ $(document).ready(function() { init_codefold(0); });
boost::asio::ip::tcp::endpoint endpoint_type
endpoint_type remote_endpoint_
boost::asio::basic_waitable_timer< std::chrono::steady_clock > stepTimer_
-
void onHandshake(error_code ec)
+
void onHandshake(error_code ec)
static boost::asio::ip::tcp::endpoint parse_endpoint(std::string const &s, boost::system::error_code &ec)
@@ -319,9 +319,9 @@ $(document).ready(function() { init_codefold(0); });
boost::beast::http::response< boost::beast::http::dynamic_body > response_type
beast::WrappedSink sink_
beast::Journal const journal_
-
void onWrite(error_code ec)
-
void onTimer(error_code ec)
Handle timer expiration events.
-
void cancelTimer()
Cancel both global and step timers.
+
void onWrite(error_code ec)
+
void onTimer(error_code ec)
Handle timer expiration events.
+
void cancelTimer()
Cancel both global and step timers.
An endpoint that consumes resources.
Definition Consumer.h:16
diff --git a/Connect_8cpp_source.html b/Connect_8cpp_source.html index f6ef6ef9dc..8461a2e2d7 100644 --- a/Connect_8cpp_source.html +++ b/Connect_8cpp_source.html @@ -137,7 +137,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
Int asInt() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
bool isConvertibleTo(ValueType other) const
static Endpoint from_string(std::string const &s)
virtual Config & config()=0
diff --git a/Consensus_8h_source.html b/Consensus_8h_source.html index 1b199e324d..7ee1214cdb 100644 --- a/Consensus_8h_source.html +++ b/Consensus_8h_source.html @@ -1583,7 +1583,7 @@ $(document).ready(function() { init_codefold(0); });
Decorator for streaming out compact json.
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Json::Int Int
Definition json_value.h:138
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:318
diff --git a/CreateCheck_8cpp_source.html b/CreateCheck_8cpp_source.html index 20142bd7fd..576fb18eef 100644 --- a/CreateCheck_8cpp_source.html +++ b/CreateCheck_8cpp_source.html @@ -322,7 +322,7 @@ $(document).ready(function() { init_codefold(0); });
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:129
@ tefINTERNAL
Definition TER.h:153
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Definition View.cpp:1020
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Definition View.cpp:138
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
@@ -343,7 +343,7 @@ $(document).ready(function() { init_codefold(0); });
@ lsfLowFreeze
@ lsfHighFreeze
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
diff --git a/CreateOffer_8cpp_source.html b/CreateOffer_8cpp_source.html index 25b7a76345..b9273686ae 100644 --- a/CreateOffer_8cpp_source.html +++ b/CreateOffer_8cpp_source.html @@ -999,7 +999,7 @@ $(document).ready(function() { init_codefold(0); });
bool native() const noexcept
Definition STAmount.h:416
static constexpr std::uint64_t cMaxNative
Definition STAmount.h:53
AccountID const & getIssuer() const
Definition STAmount.h:466
- +
void push_back(STObject const &object)
Definition STArray.h:187
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
@@ -1058,7 +1058,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfHybrid
Definition TxFlags.h:82
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:129
@ tefINTERNAL
Definition TER.h:153
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
constexpr std::uint32_t tfOfferCreateMask
Definition TxFlags.h:83
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
std::string transToken(TER code)
Definition TER.cpp:243
@@ -1083,7 +1083,7 @@ $(document).ready(function() { init_codefold(0); });
@ temBAD_AMOUNT
Definition TER.h:69
@ temBAD_OFFER
Definition TER.h:75
@ temREDUNDANT
Definition TER.h:92
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
STAmount divRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
@ tecINSUF_RESERVE_OFFER
Definition TER.h:270
@ tecDIR_FULL
Definition TER.h:268
@@ -1097,7 +1097,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecKILLED
Definition TER.h:297
@ tecNO_PERMISSION
Definition TER.h:286
@ tecNO_ISSUER
Definition TER.h:280
-
bool isTecClaim(TER x) noexcept
Definition TER.h:656
+
bool isTecClaim(TER x) noexcept
Definition TER.h:650
@ lsfRequireAuth
@ lsfLowDeepFreeze
diff --git a/CredentialHelpers_8cpp_source.html b/CredentialHelpers_8cpp_source.html index a3013c34fe..49d326be29 100644 --- a/CredentialHelpers_8cpp_source.html +++ b/CredentialHelpers_8cpp_source.html @@ -468,8 +468,8 @@ $(document).ready(function() { init_codefold(0); });
STVector256 const & getFieldV256(SField const &field) const
Definition STObject.cpp:646
-
std::size_t size() const
-
void push_back(uint256 const &v)
+
std::size_t size() const
+
void push_back(uint256 const &v)
T emplace(T... args)
@@ -496,7 +496,7 @@ $(document).ready(function() { init_codefold(0); });
@ tefBAD_LEDGER
Definition TER.h:150
@ tefINTERNAL
Definition TER.h:153
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst, beast::Journal j)
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition Protocol.h:223
@@ -504,7 +504,7 @@ $(document).ready(function() { init_codefold(0); });
@ temMALFORMED
Definition TER.h:67
@ temARRAY_EMPTY
Definition TER.h:120
@ temINVALID_ACCOUNT_ID
Definition TER.h:99
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecNO_ENTRY
Definition TER.h:287
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecNO_AUTH
Definition TER.h:281
diff --git a/Credentials_8cpp_source.html b/Credentials_8cpp_source.html index eb989914ca..77bf14d75e 100644 --- a/Credentials_8cpp_source.html +++ b/Credentials_8cpp_source.html @@ -492,7 +492,7 @@ $(document).ready(function() { init_codefold(0); });
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:955
@ temMALFORMED
Definition TER.h:67
@ temINVALID_ACCOUNT_ID
Definition TER.h:99
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecDIR_FULL
Definition TER.h:268
@ tecNO_ENTRY
Definition TER.h:287
@ tecNO_TARGET
Definition TER.h:285
diff --git a/Credentials__test_8cpp_source.html b/Credentials__test_8cpp_source.html index 9a91004aca..5a56227b7a 100644 --- a/Credentials__test_8cpp_source.html +++ b/Credentials__test_8cpp_source.html @@ -1105,7 +1105,7 @@ $(document).ready(function() { init_codefold(0); });
bool adjustOwnerNode(ApplyView &view, uint256 key, std::uint64_t page)
Implementation of adjust for the most common ledger entry, i.e.
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
std::uint32_t ownerCount(Env const &env, Account const &account)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
@@ -1117,7 +1117,7 @@ $(document).ready(function() { init_codefold(0); });
std::size_t constexpr maxCredentialURILength
The maximum length of a URI inside a Credential.
Definition Protocol.h:217
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
@ tapNONE
Definition ApplyView.h:11
@ temBAD_FEE
Definition TER.h:72
diff --git a/CrossingLimits__test_8cpp_source.html b/CrossingLimits__test_8cpp_source.html index abac8182b1..85fd1bd0a9 100644 --- a/CrossingLimits__test_8cpp_source.html +++ b/CrossingLimits__test_8cpp_source.html @@ -611,7 +611,7 @@ $(document).ready(function() { init_codefold(0); });
A balance matches.
Definition balance.h:19
Match the number of items in the account's owner directory.
Definition owners.h:48
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
static none_t const none
Definition tags.h:14
FeatureBitset testable_amendments()
Definition Env.h:76
diff --git a/DID_8cpp_source.html b/DID_8cpp_source.html index d5c40a25a0..8601eb4cc9 100644 --- a/DID_8cpp_source.html +++ b/DID_8cpp_source.html @@ -298,7 +298,7 @@ $(document).ready(function() { init_codefold(0); });
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:118
XRPAmount xrp() const
Definition STAmount.cpp:249
-
bool empty() const
Definition STObject.h:939
+
bool empty() const
Definition STObject.h:945
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
AccountID const account_
Definition Transactor.h:112
@@ -315,7 +315,7 @@ $(document).ready(function() { init_codefold(0); });
std::size_t constexpr maxDIDURILength
The maximum length of a URI inside a DID.
Definition Protocol.h:208
@ tefBAD_LEDGER
Definition TER.h:150
@ tefINTERNAL
Definition TER.h:153
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:955
TER addSLE(ApplyContext &ctx, std::shared_ptr< SLE > const &sle, AccountID const &owner)
Definition DID.cpp:50
@@ -327,7 +327,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecINTERNAL
Definition TER.h:291
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
std::size_t constexpr maxDIDAttestationLength
The maximum length of an Attestation inside a DID.
Definition Protocol.h:211
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
diff --git a/DID__test_8cpp_source.html b/DID__test_8cpp_source.html index c3c9102b1b..3fdbba367b 100644 --- a/DID__test_8cpp_source.html +++ b/DID__test_8cpp_source.html @@ -486,7 +486,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value setValid(jtx::Account const &account)
Definition dids.cpp:23
Json::Value set(jtx::Account const &account)
Definition dids.cpp:14
std::uint32_t ownerCount(Env const &env, Account const &account)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
diff --git a/Database_8cpp_source.html b/Database_8cpp_source.html index bc6e18ac30..117adcc800 100644 --- a/Database_8cpp_source.html +++ b/Database_8cpp_source.html @@ -346,7 +346,7 @@ $(document).ready(function() { init_codefold(0); });
247} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isObject() const
+
bool isObject() const
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:318
Stream debug() const
Definition Journal.h:300
diff --git a/DelegateSet_8cpp_source.html b/DelegateSet_8cpp_source.html index e1672673ee..3ced352877 100644 --- a/DelegateSet_8cpp_source.html +++ b/DelegateSet_8cpp_source.html @@ -261,7 +261,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecNO_TARGET
Definition TER.h:285
@ tecINTERNAL
Definition TER.h:291
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
std::size_t constexpr permissionMaxSize
The maximum number of delegate permissions an account can grant.
Definition Protocol.h:294
diff --git a/DelegateUtils_8cpp_source.html b/DelegateUtils_8cpp_source.html index 38ff7c70c7..99eff214f4 100644 --- a/DelegateUtils_8cpp_source.html +++ b/DelegateUtils_8cpp_source.html @@ -141,7 +141,7 @@ $(document).ready(function() { init_codefold(0); });
GranularPermissionType
We have both transaction type permissions and granular type permissions.
Definition Permissions.h:19
void loadGranularPermission(std::shared_ptr< SLE const > const &delegate, TxType const &type, std::unordered_set< GranularPermissionType > &granularPermissions)
Load the granular permissions granted to the delegate account for the specified transaction type.
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
diff --git a/DelegateUtils_8h_source.html b/DelegateUtils_8h_source.html index b299299d30..fa16019df0 100644 --- a/DelegateUtils_8h_source.html +++ b/DelegateUtils_8h_source.html @@ -104,7 +104,7 @@ $(document).ready(function() { init_codefold(0); });
TxType
Transaction type identifiers.
Definition TxFormats.h:37
void loadGranularPermission(std::shared_ptr< SLE const > const &delegate, TxType const &type, std::unordered_set< GranularPermissionType > &granularPermissions)
Load the granular permissions granted to the delegate account for the specified transaction type.
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
diff --git a/Delegate__test_8cpp_source.html b/Delegate__test_8cpp_source.html index 4d5b0e5b5b..0f04ec94f6 100644 --- a/Delegate__test_8cpp_source.html +++ b/Delegate__test_8cpp_source.html @@ -1735,7 +1735,7 @@ $(document).ready(function() { init_codefold(0); });
1612} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -1772,7 +1772,7 @@ $(document).ready(function() { init_codefold(0); });
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:533
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:319
-
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:126
+
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:127
Converts to MPT Issue or STAmount.
A balance matches.
Definition balance.h:19
Sets the optional URI on a DIDSet.
Definition did.h:40
@@ -1799,7 +1799,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
FeatureBitset testable_amendments()
Definition Env.h:76
diff --git a/DeleteAccount_8cpp_source.html b/DeleteAccount_8cpp_source.html index 8f7d0bc015..01fa12cdc8 100644 --- a/DeleteAccount_8cpp_source.html +++ b/DeleteAccount_8cpp_source.html @@ -509,7 +509,7 @@ $(document).ready(function() { init_codefold(0); });
static bool checkExtraFeatures(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static TER deleteOracle(ApplyView &view, std::shared_ptr< SLE > const &sle, AccountID const &account, beast::Journal j)
-
static TER removeFromLedger(ApplyView &view, uint256 const &delIndex, beast::Journal j)
+
static TER removeFromLedger(ApplyView &view, uint256 const &delIndex, beast::Journal j)
A view into a ledger.
Definition ReadView.h:31
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:97
@@ -555,11 +555,11 @@ $(document).ready(function() { init_codefold(0); });
bool cdirFirst(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the first entry in the directory, advancing the index.
Definition View.cpp:101
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst, beast::Journal j)
base_uint< 256 > uint256
Definition base_uint.h:526
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
static TER removeSignersFromLedger(Application &app, ApplyView &view, Keylet const &accountKeylet, Keylet const &ownerDirKeylet, Keylet const &signerListKeylet, beast::Journal j)
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1672
@ temDST_IS_SRC
Definition TER.h:88
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
LedgerEntryType
Identifiers for on-ledger objects.
@ tecTOO_SOON
Definition TER.h:299
@ tecNO_PERMISSION
Definition TER.h:286
diff --git a/DeleteOracle_8cpp_source.html b/DeleteOracle_8cpp_source.html index e66ea00e0b..fb792d765c 100644 --- a/DeleteOracle_8cpp_source.html +++ b/DeleteOracle_8cpp_source.html @@ -195,7 +195,7 @@ $(document).ready(function() { init_codefold(0); });
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
@ tecNO_ENTRY
Definition TER.h:287
@ tecINTERNAL
Definition TER.h:291
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
diff --git a/DeliverMax_8cpp_source.html b/DeliverMax_8cpp_source.html index 61e03000b2..1f832f9e93 100644 --- a/DeliverMax_8cpp_source.html +++ b/DeliverMax_8cpp_source.html @@ -107,8 +107,8 @@ $(document).ready(function() { init_codefold(0); });
22} // namespace RPC
23} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value removeMember(char const *key)
Remove and return the named member.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value removeMember(char const *key)
Remove and return the named member.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
void insertDeliverMax(Json::Value &tx_json, TxType txnType, unsigned int apiVersion)
Copy Amount field to DeliverMax field in transaction output JSON.
Definition DeliverMax.cpp:9
static constexpr std::integral_constant< unsigned, Version > apiVersion
Definition ApiVersion.h:38
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/DeliverMin__test_8cpp_source.html b/DeliverMin__test_8cpp_source.html index f5af50b028..5e56577878 100644 --- a/DeliverMin__test_8cpp_source.html +++ b/DeliverMin__test_8cpp_source.html @@ -229,7 +229,7 @@ $(document).ready(function() { init_codefold(0); });
Sets the SendMax on a JTx.
Definition sendmax.h:13
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
Set the flags on a JTx.
Definition txflags.h:11
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
diff --git a/DeliveredAmount__test_8cpp_source.html b/DeliveredAmount__test_8cpp_source.html index 25085ef977..3f9f8ccb49 100644 --- a/DeliveredAmount__test_8cpp_source.html +++ b/DeliveredAmount__test_8cpp_source.html @@ -487,7 +487,7 @@ $(document).ready(function() { init_codefold(0); });
378} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
virtual Config & config()=0
@@ -521,7 +521,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:792
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:533
-
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:126
+
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:127
Converts to MPT Issue or STAmount.
A balance matches.
Definition balance.h:19
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
@@ -529,7 +529,7 @@ $(document).ready(function() { init_codefold(0); });
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
Set the flags on a JTx.
Definition txflags.h:11
@ arrayValue
array value (ordered list)
Definition json_value.h:25
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
diff --git a/DepositAuth__test_8cpp_source.html b/DepositAuth__test_8cpp_source.html index 7fd9c51438..d06829295b 100644 --- a/DepositAuth__test_8cpp_source.html +++ b/DepositAuth__test_8cpp_source.html @@ -1449,8 +1449,8 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
-
Value removeMember(char const *key)
Remove and return the named member.
+
Value & append(Value const &value)
Append value to array at the end.
+
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -1508,7 +1508,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
std::uint32_t ownerCount(Env const &env, Account const &account)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
FeatureBitset testable_amendments()
Definition Env.h:76
@@ -1530,7 +1530,7 @@ $(document).ready(function() { init_codefold(0); });
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:96
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:65
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
@ temARRAY_TOO_LARGE
Definition TER.h:121
@ temBAD_FEE
Definition TER.h:72
@ temINVALID_FLAG
Definition TER.h:91
diff --git a/DepositAuthorized_8cpp_source.html b/DepositAuthorized_8cpp_source.html index 223e3bad76..5324631660 100644 --- a/DepositAuthorized_8cpp_source.html +++ b/DepositAuthorized_8cpp_source.html @@ -244,7 +244,7 @@ $(document).ready(function() { init_codefold(0); });
159} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:471
T emplace(T... args)
diff --git a/DepositAuthorized__test_8cpp_source.html b/DepositAuthorized__test_8cpp_source.html index e47975c6b8..226cca7954 100644 --- a/DepositAuthorized__test_8cpp_source.html +++ b/DepositAuthorized__test_8cpp_source.html @@ -594,8 +594,8 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
Value removeMember(char const *key)
Remove and return the named member.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value removeMember(char const *key)
Remove and return the named member.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -622,7 +622,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:13
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:35
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/DepositPreauth_8cpp_source.html b/DepositPreauth_8cpp_source.html index 632aebcd31..b64333204b 100644 --- a/DepositPreauth_8cpp_source.html +++ b/DepositPreauth_8cpp_source.html @@ -208,163 +208,164 @@ $(document).ready(function() { init_codefold(0); });
120 else if (ctx.tx.isFieldPresent(sfUnauthorizeCredentials))
121 {
122 // Verify that the Preauth entry is in the ledger.
- -
124 account, credentials::makeSorted(ctx.tx.getFieldArray(sfUnauthorizeCredentials)))))
-
125 return tecNO_ENTRY;
-
126 }
-
127 return tesSUCCESS;
-
128}
+
123 if (!ctx.view.exists(
+ +
125 account, credentials::makeSorted(ctx.tx.getFieldArray(sfUnauthorizeCredentials)))))
+
126 return tecNO_ENTRY;
+
127 }
+
128 return tesSUCCESS;
+
129}
-
129
-
130TER
-
- -
132{
-
133 if (ctx_.tx.isFieldPresent(sfAuthorize))
-
134 {
-
135 auto const sleOwner = view().peek(keylet::account(account_));
-
136 if (!sleOwner)
-
137 return {tefINTERNAL};
-
138
-
139 // A preauth counts against the reserve of the issuing account, but we
-
140 // check the starting balance because we want to allow dipping into the
-
141 // reserve to pay fees.
-
142 {
-
143 STAmount const reserve{view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)};
-
144
-
145 if (mPriorBalance < reserve)
- -
147 }
-
148
-
149 // Preclaim already verified that the Preauth entry does not yet exist.
-
150 // Create and populate the Preauth entry.
-
151 AccountID const auth{ctx_.tx[sfAuthorize]};
-
152 Keylet const preauthKeylet = keylet::depositPreauth(account_, auth);
-
153 auto slePreauth = std::make_shared<SLE>(preauthKeylet);
-
154
-
155 slePreauth->setAccountID(sfAccount, account_);
-
156 slePreauth->setAccountID(sfAuthorize, auth);
-
157 view().insert(slePreauth);
-
158
-
159 auto const page = view().dirInsert(keylet::ownerDir(account_), preauthKeylet, describeOwnerDir(account_));
-
160
-
161 JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKeylet.key) << ": "
-
162 << (page ? "success" : "failure");
-
163
-
164 if (!page)
-
165 return tecDIR_FULL; // LCOV_EXCL_LINE
-
166
-
167 slePreauth->setFieldU64(sfOwnerNode, *page);
-
168
-
169 // If we succeeded, the new entry counts against the creator's reserve.
-
170 adjustOwnerCount(view(), sleOwner, 1, j_);
-
171 }
-
172 else if (ctx_.tx.isFieldPresent(sfUnauthorize))
-
173 {
-
174 auto const preauth = keylet::depositPreauth(account_, ctx_.tx[sfUnauthorize]);
-
175
-
176 return DepositPreauth::removeFromLedger(view(), preauth.key, j_);
-
177 }
-
178 else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
-
179 {
-
180 auto const sleOwner = view().peek(keylet::account(account_));
-
181 if (!sleOwner)
-
182 return tefINTERNAL; // LCOV_EXCL_LINE
-
183
-
184 // A preauth counts against the reserve of the issuing account, but we
-
185 // check the starting balance because we want to allow dipping into the
-
186 // reserve to pay fees.
-
187 {
-
188 STAmount const reserve{view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)};
-
189
-
190 if (mPriorBalance < reserve)
- -
192 }
-
193
-
194 // Preclaim already verified that the Preauth entry does not yet exist.
-
195 // Create and populate the Preauth entry.
-
196
-
197 auto const sortedTX = credentials::makeSorted(ctx_.tx.getFieldArray(sfAuthorizeCredentials));
-
198 STArray sortedLE(sfAuthorizeCredentials, sortedTX.size());
-
199 for (auto const& p : sortedTX)
-
200 {
-
201 auto cred = STObject::makeInnerObject(sfCredential);
-
202 cred.setAccountID(sfIssuer, p.first);
-
203 cred.setFieldVL(sfCredentialType, p.second);
-
204 sortedLE.push_back(std::move(cred));
-
205 }
-
206
-
207 Keylet const preauthKey = keylet::depositPreauth(account_, sortedTX);
-
208 auto slePreauth = std::make_shared<SLE>(preauthKey);
-
209 if (!slePreauth)
-
210 return tefINTERNAL; // LCOV_EXCL_LINE
-
211
-
212 slePreauth->setAccountID(sfAccount, account_);
-
213 slePreauth->peekFieldArray(sfAuthorizeCredentials) = std::move(sortedLE);
-
214
-
215 view().insert(slePreauth);
-
216
-
217 auto const page = view().dirInsert(keylet::ownerDir(account_), preauthKey, describeOwnerDir(account_));
-
218
-
219 JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKey.key) << ": "
-
220 << (page ? "success" : "failure");
-
221
-
222 if (!page)
-
223 return tecDIR_FULL; // LCOV_EXCL_LINE
-
224
-
225 slePreauth->setFieldU64(sfOwnerNode, *page);
-
226
-
227 // If we succeeded, the new entry counts against the creator's reserve.
-
228 adjustOwnerCount(view(), sleOwner, 1, j_);
-
229 }
-
230 else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
-
231 {
-
232 auto const preauthKey =
- -
234 return DepositPreauth::removeFromLedger(view(), preauthKey.key, j_);
-
235 }
-
236
-
237 return tesSUCCESS;
-
238}
+
130
+
131TER
+
+ +
133{
+
134 if (ctx_.tx.isFieldPresent(sfAuthorize))
+
135 {
+
136 auto const sleOwner = view().peek(keylet::account(account_));
+
137 if (!sleOwner)
+
138 return {tefINTERNAL};
+
139
+
140 // A preauth counts against the reserve of the issuing account, but we
+
141 // check the starting balance because we want to allow dipping into the
+
142 // reserve to pay fees.
+
143 {
+
144 STAmount const reserve{view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)};
+
145
+
146 if (mPriorBalance < reserve)
+ +
148 }
+
149
+
150 // Preclaim already verified that the Preauth entry does not yet exist.
+
151 // Create and populate the Preauth entry.
+
152 AccountID const auth{ctx_.tx[sfAuthorize]};
+
153 Keylet const preauthKeylet = keylet::depositPreauth(account_, auth);
+
154 auto slePreauth = std::make_shared<SLE>(preauthKeylet);
+
155
+
156 slePreauth->setAccountID(sfAccount, account_);
+
157 slePreauth->setAccountID(sfAuthorize, auth);
+
158 view().insert(slePreauth);
+
159
+
160 auto const page = view().dirInsert(keylet::ownerDir(account_), preauthKeylet, describeOwnerDir(account_));
+
161
+
162 JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKeylet.key) << ": "
+
163 << (page ? "success" : "failure");
+
164
+
165 if (!page)
+
166 return tecDIR_FULL; // LCOV_EXCL_LINE
+
167
+
168 slePreauth->setFieldU64(sfOwnerNode, *page);
+
169
+
170 // If we succeeded, the new entry counts against the creator's reserve.
+
171 adjustOwnerCount(view(), sleOwner, 1, j_);
+
172 }
+
173 else if (ctx_.tx.isFieldPresent(sfUnauthorize))
+
174 {
+
175 auto const preauth = keylet::depositPreauth(account_, ctx_.tx[sfUnauthorize]);
+
176
+
177 return DepositPreauth::removeFromLedger(view(), preauth.key, j_);
+
178 }
+
179 else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
+
180 {
+
181 auto const sleOwner = view().peek(keylet::account(account_));
+
182 if (!sleOwner)
+
183 return tefINTERNAL; // LCOV_EXCL_LINE
+
184
+
185 // A preauth counts against the reserve of the issuing account, but we
+
186 // check the starting balance because we want to allow dipping into the
+
187 // reserve to pay fees.
+
188 {
+
189 STAmount const reserve{view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)};
+
190
+
191 if (mPriorBalance < reserve)
+ +
193 }
+
194
+
195 // Preclaim already verified that the Preauth entry does not yet exist.
+
196 // Create and populate the Preauth entry.
+
197
+
198 auto const sortedTX = credentials::makeSorted(ctx_.tx.getFieldArray(sfAuthorizeCredentials));
+
199 STArray sortedLE(sfAuthorizeCredentials, sortedTX.size());
+
200 for (auto const& p : sortedTX)
+
201 {
+
202 auto cred = STObject::makeInnerObject(sfCredential);
+
203 cred.setAccountID(sfIssuer, p.first);
+
204 cred.setFieldVL(sfCredentialType, p.second);
+
205 sortedLE.push_back(std::move(cred));
+
206 }
+
207
+
208 Keylet const preauthKey = keylet::depositPreauth(account_, sortedTX);
+
209 auto slePreauth = std::make_shared<SLE>(preauthKey);
+
210 if (!slePreauth)
+
211 return tefINTERNAL; // LCOV_EXCL_LINE
+
212
+
213 slePreauth->setAccountID(sfAccount, account_);
+
214 slePreauth->peekFieldArray(sfAuthorizeCredentials) = std::move(sortedLE);
+
215
+
216 view().insert(slePreauth);
+
217
+
218 auto const page = view().dirInsert(keylet::ownerDir(account_), preauthKey, describeOwnerDir(account_));
+
219
+
220 JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKey.key) << ": "
+
221 << (page ? "success" : "failure");
+
222
+
223 if (!page)
+
224 return tecDIR_FULL; // LCOV_EXCL_LINE
+
225
+
226 slePreauth->setFieldU64(sfOwnerNode, *page);
+
227
+
228 // If we succeeded, the new entry counts against the creator's reserve.
+
229 adjustOwnerCount(view(), sleOwner, 1, j_);
+
230 }
+
231 else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
+
232 {
+
233 auto const preauthKey =
+ +
235 return DepositPreauth::removeFromLedger(view(), preauthKey.key, j_);
+
236 }
+
237
+
238 return tesSUCCESS;
+
239}
-
239
-
240TER
-
- -
242{
-
243 // Existence already checked in preclaim and DeleteAccount
-
244 auto const slePreauth{view.peek(keylet::depositPreauth(preauthIndex))};
-
245 if (!slePreauth)
-
246 {
-
247 JLOG(j.warn()) << "Selected DepositPreauth does not exist.";
-
248 return tecNO_ENTRY;
-
249 }
-
250
-
251 AccountID const account{(*slePreauth)[sfAccount]};
-
252 std::uint64_t const page{(*slePreauth)[sfOwnerNode]};
-
253 if (!view.dirRemove(keylet::ownerDir(account), page, preauthIndex, false))
-
254 {
-
255 // LCOV_EXCL_START
-
256 JLOG(j.fatal()) << "Unable to delete DepositPreauth from owner.";
-
257 return tefBAD_LEDGER;
-
258 // LCOV_EXCL_STOP
-
259 }
-
260
-
261 // If we succeeded, update the DepositPreauth owner's reserve.
-
262 auto const sleOwner = view.peek(keylet::account(account));
-
263 if (!sleOwner)
-
264 return tefINTERNAL; // LCOV_EXCL_LINE
-
265
-
266 adjustOwnerCount(view, sleOwner, -1, j);
-
267
-
268 // Remove DepositPreauth from ledger.
-
269 view.erase(slePreauth);
-
270
-
271 return tesSUCCESS;
-
272}
+
240
+
241TER
+
+ +
243{
+
244 // Existence already checked in preclaim and DeleteAccount
+
245 auto const slePreauth{view.peek(keylet::depositPreauth(preauthIndex))};
+
246 if (!slePreauth)
+
247 {
+
248 JLOG(j.warn()) << "Selected DepositPreauth does not exist.";
+
249 return tecNO_ENTRY;
+
250 }
+
251
+
252 AccountID const account{(*slePreauth)[sfAccount]};
+
253 std::uint64_t const page{(*slePreauth)[sfOwnerNode]};
+
254 if (!view.dirRemove(keylet::ownerDir(account), page, preauthIndex, false))
+
255 {
+
256 // LCOV_EXCL_START
+
257 JLOG(j.fatal()) << "Unable to delete DepositPreauth from owner.";
+
258 return tefBAD_LEDGER;
+
259 // LCOV_EXCL_STOP
+
260 }
+
261
+
262 // If we succeeded, update the DepositPreauth owner's reserve.
+
263 auto const sleOwner = view.peek(keylet::account(account));
+
264 if (!sleOwner)
+
265 return tefINTERNAL; // LCOV_EXCL_LINE
+
266
+
267 adjustOwnerCount(view, sleOwner, -1, j);
+
268
+
269 // Remove DepositPreauth from ledger.
+
270 view.erase(slePreauth);
+
271
+
272 return tesSUCCESS;
+
273}
-
273
-
274} // namespace xrpl
+
274
+
275} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:324
Stream trace() const
Severity stream access functions.
Definition Journal.h:294
@@ -376,8 +377,8 @@ $(document).ready(function() { init_codefold(0); });
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition ApplyView.h:283
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
- -
static TER removeFromLedger(ApplyView &view, uint256 const &delIndex, beast::Journal j)
+ +
static TER removeFromLedger(ApplyView &view, uint256 const &delIndex, beast::Journal j)
static NotTEC preflight(PreflightContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
@@ -415,7 +416,7 @@ $(document).ready(function() { init_codefold(0); });
@ temCANNOT_PREAUTH_SELF
Definition TER.h:100
@ temMALFORMED
Definition TER.h:67
@ temINVALID_ACCOUNT_ID
Definition TER.h:99
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecDIR_FULL
Definition TER.h:268
@ tecNO_ENTRY
Definition TER.h:287
@ tecNO_TARGET
Definition TER.h:285
diff --git a/DepositPreauth_8h_source.html b/DepositPreauth_8h_source.html index 058f349865..7693c8567d 100644 --- a/DepositPreauth_8h_source.html +++ b/DepositPreauth_8h_source.html @@ -122,8 +122,8 @@ $(document).ready(function() { init_codefold(0); });
State information when applying a tx.
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:114
- -
static TER removeFromLedger(ApplyView &view, uint256 const &delIndex, beast::Journal j)
+ +
static TER removeFromLedger(ApplyView &view, uint256 const &delIndex, beast::Journal j)
static constexpr ConsequencesFactoryType ConsequencesFactory
static NotTEC preflight(PreflightContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
diff --git a/Dir_8cpp_source.html b/Dir_8cpp_source.html index 412a823d07..2f8855a843 100644 --- a/Dir_8cpp_source.html +++ b/Dir_8cpp_source.html @@ -240,8 +240,8 @@ $(document).ready(function() { init_codefold(0); });
Dir(ReadView const &, Keylet const &)
Definition Dir.cpp:7
A view into a ledger.
Definition ReadView.h:31
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
-
std::size_t size() const
-
bool empty() const
+
std::size_t size() const
+
bool empty() const
T end(T... args)
T is_same_v
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition Indexes.cpp:166
diff --git a/Dir_8h_source.html b/Dir_8h_source.html index 25a9cddc3e..79e6ca073a 100644 --- a/Dir_8h_source.html +++ b/Dir_8h_source.html @@ -227,7 +227,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
diff --git a/DirectStep_8cpp_source.html b/DirectStep_8cpp_source.html index e6ce6abdc2..0d79576b6e 100644 --- a/DirectStep_8cpp_source.html +++ b/DirectStep_8cpp_source.html @@ -1103,8 +1103,8 @@ $(document).ready(function() { init_codefold(0); });
friend bool operator!=(DirectStepI const &lhs, DirectStepI const &rhs)
std::optional< EitherAmount > cachedIn() const override
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
-
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:161
-
exponent_type exponent() const noexcept
Definition IOUAmount.h:155
+
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:163
+
exponent_type exponent() const noexcept
Definition IOUAmount.h:157
A currency issued by an account.
Definition Issue.h:13
A wrapper which makes credits unavailable to balances.
A view into a ledger.
Definition ReadView.h:31
diff --git a/Directory__test_8cpp_source.html b/Directory__test_8cpp_source.html index 4f1d9f097f..8da08f8fa0 100644 --- a/Directory__test_8cpp_source.html +++ b/Directory__test_8cpp_source.html @@ -671,7 +671,7 @@ $(document).ready(function() { init_codefold(0); });
Specifies an order book.
Definition Book.h:16
-
void push_back(uint256 const &v)
+
void push_back(uint256 const &v)
Discardable, editable view to a ledger.
Definition Sandbox.h:15
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
@@ -704,7 +704,7 @@ $(document).ready(function() { init_codefold(0); });
auto bumpLastPage(Env &env, std::uint64_t newLastPage, Keylet directory, std::function< bool(ApplyView &, uint256, std::uint64_t)> adjust) -> Expected< void, Error >
Move the position of the last page in the user's directory on open ledger to newLastPage.
Definition directory.cpp:11
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:23
static none_t const none
Definition tags.h:14
diff --git a/Discrepancy__test_8cpp_source.html b/Discrepancy__test_8cpp_source.html index c6dd63ed82..3a31ad52ba 100644 --- a/Discrepancy__test_8cpp_source.html +++ b/Discrepancy__test_8cpp_source.html @@ -217,7 +217,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
diff --git a/Env_8cpp_source.html b/Env_8cpp_source.html index 99a291a143..ec0b4f949b 100644 --- a/Env_8cpp_source.html +++ b/Env_8cpp_source.html @@ -782,10 +782,10 @@ $(document).ready(function() { init_codefold(0); });
constexpr char const * c_str() const
Definition json_value.h:57
Represents a JSON value.
Definition json_value.h:130
-
bool isObject() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
-
bool isNull() const
isNull() tests to see if this field is null.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isNull() const
isNull() tests to see if this field is null.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
Stream error() const
Definition Journal.h:318
A testsuite class.
Definition suite.h:51
log_os< char > log
Logging output stream.
Definition suite.h:144
@@ -894,7 +894,7 @@ $(document).ready(function() { init_codefold(0); });
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
void fill_seq(Json::Value &jv, ReadView const &view)
Set the sequence number automatically.
Definition utility.cpp:52
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
static autofill_t const autofill
Definition tags.h:22
void sign(Json::Value &jv, Account const &account, Json::Value &sigObject)
Sign automatically into a specific Json field of the jv object.
Definition utility.cpp:27
@@ -914,7 +914,7 @@ $(document).ready(function() { init_codefold(0); });
std::string transHuman(TER code)
Definition TER.cpp:252
std::pair< int, Json::Value > rpcClient(std::vector< std::string > const &args, Config const &config, Logs &logs, unsigned int apiVersion, std::unordered_map< std::string, std::string > const &headers)
Internal invocation of RPC client.
Definition RPCCall.cpp:1437
std::string transToken(TER code)
Definition TER.cpp:243
-
std::unique_ptr< Application > make_Application(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
+
std::unique_ptr< Application > make_Application(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
constexpr std::uint32_t asfDefaultRipple
Definition TxFlags.h:64
@ temMALFORMED
Definition TER.h:67
diff --git a/Env__test_8cpp_source.html b/Env__test_8cpp_source.html index d5dd61f0a3..cf48f49efc 100644 --- a/Env__test_8cpp_source.html +++ b/Env__test_8cpp_source.html @@ -1070,11 +1070,11 @@ $(document).ready(function() { init_codefold(0); });
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
-
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:111
+
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:112
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
static autofill_t const autofill
Definition tags.h:22
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
diff --git a/ErrorCodes_8cpp_source.html b/ErrorCodes_8cpp_source.html index 42868bdf37..459256c618 100644 --- a/ErrorCodes_8cpp_source.html +++ b/ErrorCodes_8cpp_source.html @@ -334,9 +334,9 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
bool isObject() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
constexpr auto sortErrorInfos(ErrorInfo const (&unordered)[N]) -> std::array< ErrorInfo, M >
constexpr auto sortedErrorInfos
diff --git a/EscrowToken__test_8cpp_source.html b/EscrowToken__test_8cpp_source.html index 2c4c19b053..3500664112 100644 --- a/EscrowToken__test_8cpp_source.html +++ b/EscrowToken__test_8cpp_source.html @@ -3877,7 +3877,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
Value removeMember(char const *key)
Remove and return the named member.
+
Value removeMember(char const *key)
Remove and return the named member.
A generic endpoint for log messages.
Definition Journal.h:40
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -3906,7 +3906,7 @@ $(document).ready(function() { init_codefold(0); });
NetClock::time_point now()
Returns the current network time.
Definition Env.h:274
Converts to IOU Issue or STAmount.
-
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:126
+
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:127
Converts to MPT Issue or STAmount.
xrpl::MPTID const & mpt() const
Set the fee on a JTx.
Definition fee.h:17
@@ -3938,7 +3938,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
std::uint32_t ownerCount(Env const &env, Account const &account)
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
FeatureBitset testable_amendments()
Definition Env.h:76
@@ -3954,7 +3954,7 @@ $(document).ready(function() { init_codefold(0); });
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:128
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:99
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:156
constexpr std::uint32_t tfClearDeepFreeze
Definition TxFlags.h:101
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:699
diff --git a/Escrow_8cpp_source.html b/Escrow_8cpp_source.html index f676b15489..cfc226f749 100644 --- a/Escrow_8cpp_source.html +++ b/Escrow_8cpp_source.html @@ -1386,8 +1386,8 @@ $(document).ready(function() { init_codefold(0); });
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:118
-
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:554
-
void negate()
Definition STAmount.h:530
+
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:556
+
void negate()
Definition STAmount.h:532
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
std::uint32_t getSeqValue() const
Returns the first non-zero value of (Sequence, TicketSequence).
Definition STTx.cpp:208
@@ -1464,7 +1464,7 @@ $(document).ready(function() { init_codefold(0); });
@ temMALFORMED
Definition TER.h:67
@ temDISABLED
Definition TER.h:94
@ temBAD_AMOUNT
Definition TER.h:69
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
TER escrowUnlockApplyHelper< Issue >(ApplyView &view, Rate lockedRate, std::shared_ptr< SLE > const &sleDest, STAmount const &xrpBalance, STAmount const &amount, AccountID const &issuer, AccountID const &sender, AccountID const &receiver, bool createAsset, beast::Journal journal)
Definition Escrow.cpp:682
static TER escrowCreatePreclaimHelper(PreclaimContext const &ctx, AccountID const &account, AccountID const &dest, STAmount const &amount)
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:3195
@@ -1495,7 +1495,7 @@ $(document).ready(function() { init_codefold(0); });
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
constexpr HashRouterFlags SF_CF_INVALID
Definition Escrow.cpp:23
STAmount divideRound(STAmount const &amount, Rate const &rate, bool roundUp)
Definition Rate2.cpp:80
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:3083
diff --git a/Escrow__test_8cpp_source.html b/Escrow__test_8cpp_source.html index 323109d008..523e47e136 100644 --- a/Escrow__test_8cpp_source.html +++ b/Escrow__test_8cpp_source.html @@ -1705,7 +1705,7 @@ $(document).ready(function() { init_codefold(0); });
std::array< std::uint8_t, 8 > const fb3
Definition escrow.h:65
std::array< std::uint8_t, 4 > const fb1
Definition escrow.h:49
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
owner_count< ltTICKET > tickets
Match the number of tickets on the account.
Definition ticket.h:44
diff --git a/Feature1_8cpp_source.html b/Feature1_8cpp_source.html index 0dc02d9649..decf1710e6 100644 --- a/Feature1_8cpp_source.html +++ b/Feature1_8cpp_source.html @@ -164,10 +164,10 @@ $(document).ready(function() { init_codefold(0); });
79
80} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isString() const
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
bool asBool() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
std::shared_ptr< Ledger const > getValidatedLedger()
virtual AmendmentTable & getAmendmentTable()=0
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:471
diff --git a/Feature_8cpp_source.html b/Feature_8cpp_source.html index bed2398d75..d22ce08ce2 100644 --- a/Feature_8cpp_source.html +++ b/Feature_8cpp_source.html @@ -174,7 +174,7 @@ $(document).ready(function() { init_codefold(0); });
89 };
90
91 // Intermediate types to help with readability
-
92 template <class tag, typename Type, Type Feature::*PtrToMember>
+
92 template <class tag, typename Type, Type Feature::* PtrToMember>
93 using feature_hashed_unique = boost::multi_index::
94 hashed_unique<boost::multi_index::tag<tag>, boost::multi_index::member<Feature, Type, PtrToMember>>;
95
diff --git a/Feature_8h_source.html b/Feature_8h_source.html index b6ff89b3a3..4c2bb86fbe 100644 --- a/Feature_8h_source.html +++ b/Feature_8h_source.html @@ -354,7 +354,7 @@ $(document).ready(function() { init_codefold(0); });
297 operator^(FeatureBitset const& lhs, uint256 const& rhs)
298 {
-
299 return lhs ^ FeatureBitset { rhs };
+
299 return lhs ^ FeatureBitset{rhs};
300 }
301
diff --git a/Feature__test_8cpp_source.html b/Feature__test_8cpp_source.html index e1155b1d05..27ff41ea77 100644 --- a/Feature__test_8cpp_source.html +++ b/Feature__test_8cpp_source.html @@ -253,9 +253,9 @@ $(document).ready(function() { init_codefold(0); });
162 BEAST_EXPECTS(jrr[jss::status] == jss::success, "status");
163 jrr.removeMember(jss::status);
164 BEAST_EXPECT(jrr.size() == 1);
-
165 BEAST_EXPECT(
-
166 jrr.isMember("12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E"
-
167 "2A87F1D8107"));
+
165 BEAST_EXPECT(jrr.isMember(
+
166 "12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E"
+
167 "2A87F1D8107"));
168 auto feature = *(jrr.begin());
169
170 BEAST_EXPECTS(feature[jss::name] == "fixAMMOverflowOffer", "name");
diff --git a/FetchInfo_8cpp_source.html b/FetchInfo_8cpp_source.html index 130c622bfc..245da5380a 100644 --- a/FetchInfo_8cpp_source.html +++ b/FetchInfo_8cpp_source.html @@ -111,7 +111,7 @@ $(document).ready(function() { init_codefold(0); });
26} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
bool asBool() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Json::Value getLedgerFetchInfo()=0
virtual void clearLedgerFetch()=0
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
diff --git a/FixNFTokenPageLinks__test_8cpp_source.html b/FixNFTokenPageLinks__test_8cpp_source.html index 22c78594a2..a57e48c935 100644 --- a/FixNFTokenPageLinks__test_8cpp_source.html +++ b/FixNFTokenPageLinks__test_8cpp_source.html @@ -691,9 +691,9 @@ $(document).ready(function() { init_codefold(0); });
T back(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
+
bool isArray() const
UInt size() const
Number of values in array or object.
-
Value removeMember(char const *key)
Remove and return the named member.
+
Value removeMember(char const *key)
Remove and return the named member.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
diff --git a/Flow__test_8cpp_source.html b/Flow__test_8cpp_source.html index 9debc31e6a..d488819aa0 100644 --- a/Flow__test_8cpp_source.html +++ b/Flow__test_8cpp_source.html @@ -1440,7 +1440,7 @@ $(document).ready(function() { init_codefold(0); });
PrettyAmount xrpMinusFee(Env const &env, std::int64_t xrpAmount)
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
STPathElement IPE(Issue const &iss)
FeatureBitset testable_amendments()
Definition Env.h:76
@@ -1455,7 +1455,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:96
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items in the given directory.
Definition View.cpp:598
constexpr std::uint32_t tfLimitQuality
Definition TxFlags.h:89
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
Definition StrandFlow.h:81
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:434
constexpr std::uint32_t tfNoRippleDirect
Definition TxFlags.h:87
diff --git a/GRPCHandlers_8h_source.html b/GRPCHandlers_8h_source.html index 696f0b75b4..56cfc86cf4 100644 --- a/GRPCHandlers_8h_source.html +++ b/GRPCHandlers_8h_source.html @@ -116,7 +116,7 @@ $(document).ready(function() { init_codefold(0); });
std::pair< org::xrpl::rpc::v1::GetLedgerDiffResponse, grpc::Status > doLedgerDiffGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerDiffRequest > &context)
Definition LedgerDiff.cpp:6
std::pair< org::xrpl::rpc::v1::GetLedgerDataResponse, grpc::Status > doLedgerDataGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerDataRequest > &context)
std::pair< org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status > doLedgerGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerRequest > &context)
-
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
+
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
diff --git a/GRPCServer_8cpp_source.html b/GRPCServer_8cpp_source.html index 09e015cc97..bcfa8c2ee5 100644 --- a/GRPCServer_8cpp_source.html +++ b/GRPCServer_8cpp_source.html @@ -546,148 +546,152 @@ $(document).ready(function() { init_codefold(0); });
430 {
432
-
433 addToRequests(std::make_shared<cd>(
-
434 service_,
-
435 *cq_,
-
436 app_,
-
437 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedger,
- -
439 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedger,
- - - -
443 }
-
444 {
- -
446
-
447 addToRequests(std::make_shared<cd>(
-
448 service_,
-
449 *cq_,
-
450 app_,
-
451 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerData,
- -
453 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerData,
- - - -
457 }
-
458 {
- -
460
-
461 addToRequests(std::make_shared<cd>(
-
462 service_,
-
463 *cq_,
-
464 app_,
-
465 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerDiff,
- -
467 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerDiff,
- - - -
471 }
-
472 {
- -
474
-
475 addToRequests(std::make_shared<cd>(
-
476 service_,
-
477 *cq_,
-
478 app_,
-
479 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerEntry,
- -
481 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerEntry,
- - - -
485 }
-
486 return requests;
-
487}
+
433 addToRequests(
+ +
435 service_,
+
436 *cq_,
+
437 app_,
+
438 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedger,
+ +
440 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedger,
+ + + +
444 }
+
445 {
+ +
447
+
448 addToRequests(
+ +
450 service_,
+
451 *cq_,
+
452 app_,
+
453 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerData,
+ +
455 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerData,
+ + + +
459 }
+
460 {
+ +
462
+
463 addToRequests(
+ +
465 service_,
+
466 *cq_,
+
467 app_,
+
468 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerDiff,
+ +
470 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerDiff,
+ + + +
474 }
+
475 {
+ +
477
+
478 addToRequests(
+ +
480 service_,
+
481 *cq_,
+
482 app_,
+
483 &org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetLedgerEntry,
+ +
485 &org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetLedgerEntry,
+ + + +
489 }
+
490 return requests;
+
491}
-
488
-
489bool
-
- -
491{
-
492 // if config does not specify a grpc server address, don't start
-
493 if (serverAddress_.empty())
-
494 return false;
-
495
-
496 JLOG(journal_.info()) << "Starting gRPC server at " << serverAddress_;
-
497
-
498 grpc::ServerBuilder builder;
+
492
+
493bool
+
+ +
495{
+
496 // if config does not specify a grpc server address, don't start
+
497 if (serverAddress_.empty())
+
498 return false;
499
-
500 // Listen on the given address without any authentication mechanism.
-
501 // Actually binded port will be returned into "port" variable.
-
502 int port = 0;
-
503 builder.AddListeningPort(serverAddress_, grpc::InsecureServerCredentials(), &port);
-
504 // Register "service_" as the instance through which we'll communicate with
-
505 // clients. In this case it corresponds to an *asynchronous* service.
-
506 builder.RegisterService(&service_);
-
507 // Get hold of the completion queue used for the asynchronous communication
-
508 // with the gRPC runtime.
-
509 cq_ = builder.AddCompletionQueue();
-
510 // Finally assemble the server.
-
511 server_ = builder.BuildAndStart();
-
512 serverPort_ = static_cast<std::uint16_t>(port);
-
513
-
514 return static_cast<bool>(serverPort_);
-
515}
+
500 JLOG(journal_.info()) << "Starting gRPC server at " << serverAddress_;
+
501
+
502 grpc::ServerBuilder builder;
+
503
+
504 // Listen on the given address without any authentication mechanism.
+
505 // Actually binded port will be returned into "port" variable.
+
506 int port = 0;
+
507 builder.AddListeningPort(serverAddress_, grpc::InsecureServerCredentials(), &port);
+
508 // Register "service_" as the instance through which we'll communicate with
+
509 // clients. In this case it corresponds to an *asynchronous* service.
+
510 builder.RegisterService(&service_);
+
511 // Get hold of the completion queue used for the asynchronous communication
+
512 // with the gRPC runtime.
+
513 cq_ = builder.AddCompletionQueue();
+
514 // Finally assemble the server.
+
515 server_ = builder.BuildAndStart();
+
516 serverPort_ = static_cast<std::uint16_t>(port);
+
517
+
518 return static_cast<bool>(serverPort_);
+
519}
-
516
-
517boost::asio::ip::tcp::endpoint
-
- -
519{
- -
521 return boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(addr), serverPort_);
-
522}
+
520
+
521boost::asio::ip::tcp::endpoint
+
+ +
523{
+ +
525 return boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(addr), serverPort_);
+
526}
-
523
-
524bool
-
- -
526{
-
527 // Start the server and setup listeners
-
528 if (running_ = impl_.start(); running_)
-
529 {
-
530 thread_ = std::thread([this]() {
-
531 // Start the event loop and begin handling requests
-
532 beast::setCurrentThreadName("rippled: grpc");
-
533 this->impl_.handleRpcs();
-
534 });
-
535 }
-
536 return running_;
-
537}
+
527
+
528bool
+
+ +
530{
+
531 // Start the server and setup listeners
+
532 if (running_ = impl_.start(); running_)
+
533 {
+
534 thread_ = std::thread([this]() {
+
535 // Start the event loop and begin handling requests
+
536 beast::setCurrentThreadName("rippled: grpc");
+
537 this->impl_.handleRpcs();
+
538 });
+
539 }
+
540 return running_;
+
541}
-
538
-
539void
-
- -
541{
-
542 if (running_)
-
543 {
-
544 impl_.shutdown();
-
545 thread_.join();
-
546 running_ = false;
-
547 }
-
548}
+
542
+
543void
+
+ +
545{
+
546 if (running_)
+
547 {
+
548 impl_.shutdown();
+
549 thread_.join();
+
550 running_ = false;
+
551 }
+
552}
-
549
-
- -
551{
-
552 XRPL_ASSERT(!running_, "xrpl::GRPCServer::~GRPCServer : is not running");
-
553}
+
553
+
+ +
555{
+
556 XRPL_ASSERT(!running_, "xrpl::GRPCServer::~GRPCServer : is not running");
+
557}
-
554
-
555boost::asio::ip::tcp::endpoint
-
- -
557{
-
558 return impl_.getEndpoint();
-
559}
+
558
+
559boost::asio::ip::tcp::endpoint
+
+ +
561{
+
562 return impl_.getEndpoint();
+
563}
-
560
-
561} // namespace xrpl
+
564
+
565} // namespace xrpl
T back(T... args)
@@ -726,23 +730,23 @@ $(document).ready(function() { init_codefold(0); });
Application & app_
Definition GRPCServer.h:62
- +
std::vector< boost::asio::ip::address > secureGatewayIPs_
Definition GRPCServer.h:67
std::unique_ptr< grpc::ServerCompletionQueue > cq_
Definition GRPCServer.h:53
std::vector< std::shared_ptr< Processor > > setupListeners()
GRPCServerImpl(Application &app)
beast::Journal journal_
Definition GRPCServer.h:69
std::unique_ptr< grpc::Server > server_
Definition GRPCServer.h:60
-
boost::asio::ip::tcp::endpoint getEndpoint() const
+
boost::asio::ip::tcp::endpoint getEndpoint() const
std::string serverAddress_
Definition GRPCServer.h:64
org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService service_
Definition GRPCServer.h:58
- +
std::thread thread_
Definition GRPCServer.h:298
-
boost::asio::ip::tcp::endpoint getEndpoint() const
+
boost::asio::ip::tcp::endpoint getEndpoint() const
- +
GRPCServerImpl impl_
Definition GRPCServer.h:297
- +
std::shared_ptr< InfoSub > pointer
Definition InfoSub.h:33
std::shared_ptr< Coro > postCoro(JobType t, std::string const &name, F &&f)
Creates a coroutine and adds a job to the queue which will run it.
Definition JobQueue.h:386
@@ -786,7 +790,7 @@ $(document).ready(function() { init_codefold(0); });
std::pair< org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status > doLedgerGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerRequest > &context)
@ jtRPC
Definition Job.h:31
-
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
+
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
void erase(STObject &st, TypedField< U > const &f)
Remove a field in an STObject.
Definition STExchange.h:148
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:96
error_code_i
Definition ErrorCodes.h:20
diff --git a/GRPCServer_8h_source.html b/GRPCServer_8h_source.html index 18df19a588..136edc1301 100644 --- a/GRPCServer_8h_source.html +++ b/GRPCServer_8h_source.html @@ -436,27 +436,27 @@ $(document).ready(function() { init_codefold(0); });
GRPCServerImpl(GRPCServerImpl const &)=delete
- +
std::vector< boost::asio::ip::address > secureGatewayIPs_
Definition GRPCServer.h:67
std::unique_ptr< grpc::ServerCompletionQueue > cq_
Definition GRPCServer.h:53
std::vector< std::shared_ptr< Processor > > setupListeners()
beast::Journal journal_
Definition GRPCServer.h:69
std::unique_ptr< grpc::Server > server_
Definition GRPCServer.h:60
-
boost::asio::ip::tcp::endpoint getEndpoint() const
+
boost::asio::ip::tcp::endpoint getEndpoint() const
std::vector< std::shared_ptr< Processor > > requests_
Definition GRPCServer.h:55
std::string serverAddress_
Definition GRPCServer.h:64
org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService service_
Definition GRPCServer.h:58
- +
std::thread thread_
Definition GRPCServer.h:298
-
boost::asio::ip::tcp::endpoint getEndpoint() const
+
boost::asio::ip::tcp::endpoint getEndpoint() const
- +
GRPCServerImpl impl_
Definition GRPCServer.h:297
GRPCServer & operator=(GRPCServer const &)=delete
GRPCServer(Application &app)
Definition GRPCServer.h:276
GRPCServer(GRPCServer const &)=delete
- +
Processor(Processor const &)=delete
Processor()=default
diff --git a/GRPCTestClientBase_8h_source.html b/GRPCTestClientBase_8h_source.html index a20ccc4299..a7f5a42f3f 100644 --- a/GRPCTestClientBase_8h_source.html +++ b/GRPCTestClientBase_8h_source.html @@ -97,21 +97,24 @@ $(document).ready(function() { init_codefold(0); });
13{
14 explicit GRPCTestClientBase(std::string const& port)
-
15 : stub_(org::xrpl::rpc::v1::XRPLedgerAPIService::NewStub(grpc::CreateChannel(
-
16 beast::IP::Endpoint(boost::asio::ip::make_address(getEnvLocalhostAddr()), std::stoi(port)).to_string(),
-
17 grpc::InsecureChannelCredentials())))
-
18 {
-
19 }
+
15 : stub_(
+
16 org::xrpl::rpc::v1::XRPLedgerAPIService::NewStub(
+
17 grpc::CreateChannel(
+
18 beast::IP::Endpoint(boost::asio::ip::make_address(getEnvLocalhostAddr()), std::stoi(port))
+
19 .to_string(),
+
20 grpc::InsecureChannelCredentials())))
+
21 {
+
22 }
-
20
-
21 grpc::Status status;
-
22 grpc::ClientContext context;
- -
24};
+
23
+
24 grpc::Status status;
+
25 grpc::ClientContext context;
+ +
27};
-
25
-
26} // namespace test
-
27} // namespace xrpl
+
28
+
29} // namespace test
+
30} // namespace xrpl
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
@@ -122,9 +125,9 @@ $(document).ready(function() { init_codefold(0); });
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
GRPCTestClientBase(std::string const &port)
- - -
std::unique_ptr< org::xrpl::rpc::v1::XRPLedgerAPIService::Stub > stub_
+ + +
std::unique_ptr< org::xrpl::rpc::v1::XRPLedgerAPIService::Stub > stub_
diff --git a/GatewayBalances_8cpp_source.html b/GatewayBalances_8cpp_source.html index 72bfd46d8c..f25b75727e 100644 --- a/GatewayBalances_8cpp_source.html +++ b/GatewayBalances_8cpp_source.html @@ -349,10 +349,10 @@ $(document).ready(function() { init_codefold(0); });
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
bool isString() const
-
bool isArrayOrNull() const
+
bool isString() const
+
bool isArrayOrNull() const
static std::optional< PathFindTrustLine > makeItem(AccountID const &accountID, std::shared_ptr< SLE const > const &sle)
Definition TrustLine.cpp:31
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
diff --git a/GatewayBalances__test_8cpp_source.html b/GatewayBalances__test_8cpp_source.html index e80a9a4c7e..bcda5e1c56 100644 --- a/GatewayBalances__test_8cpp_source.html +++ b/GatewayBalances__test_8cpp_source.html @@ -356,7 +356,7 @@ $(document).ready(function() { init_codefold(0); });
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:261
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition WSClient.cpp:285
diff --git a/GetAggregatePrice_8cpp_source.html b/GetAggregatePrice_8cpp_source.html index f870ca1d81..fe5a62b44f 100644 --- a/GetAggregatePrice_8cpp_source.html +++ b/GetAggregatePrice_8cpp_source.html @@ -416,10 +416,10 @@ $(document).ready(function() { init_codefold(0); });
T advance(T... args)
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
-
const_iterator begin() const
+
const_iterator begin() const
UInt size() const
Number of values in array or object.
UInt asUInt() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
Identifies fields.
Definition SField.h:126
@@ -451,7 +451,7 @@ $(document).ready(function() { init_codefold(0); });
static std::tuple< STAmount, Number, std::uint16_t > getStats(Prices::right_const_iterator const &begin, Prices::right_const_iterator const &end)
bimap< multiset_of< std::uint32_t, std::greater< std::uint32_t > >, multiset_of< STAmount > > Prices
Json::Value doGetAggregatePrice(RPC::JsonContext &context)
oracles: array of {account, oracle_document_id} base_asset: is the asset to be priced quote_asset: is...
-
Number root2(Number f)
Definition Number.cpp:1010
+
Number root2(Number f)
Definition Number.cpp:1011
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:105
@ rpcOBJECT_NOT_FOUND
Definition ErrorCodes.h:123
@ rpcORACLE_MALFORMED
Definition ErrorCodes.h:129
diff --git a/GetAggregatePrice__test_8cpp_source.html b/GetAggregatePrice__test_8cpp_source.html index 69e9c1bc12..2393004bf8 100644 --- a/GetAggregatePrice__test_8cpp_source.html +++ b/GetAggregatePrice__test_8cpp_source.html @@ -413,7 +413,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr char const * NoneTag
Definition Oracle.h:17
std::uint32_t asUInt(AnyValue const &v)
Definition Oracle.cpp:320
void toJson(Json::Value &jv, AnyValue const &v)
Definition Oracle.cpp:296
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
FeatureBitset testable_amendments()
Definition Env.h:76
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/GetCounts_8cpp_source.html b/GetCounts_8cpp_source.html index c540c0a7a8..3efa427f84 100644 --- a/GetCounts_8cpp_source.html +++ b/GetCounts_8cpp_source.html @@ -207,7 +207,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
UInt asUInt() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
bool useTxTables() const
Definition Config.h:318
diff --git a/GetCounts__test_8cpp_source.html b/GetCounts__test_8cpp_source.html index ae12466ee3..352512d4ca 100644 --- a/GetCounts__test_8cpp_source.html +++ b/GetCounts__test_8cpp_source.html @@ -188,7 +188,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
Int asInt() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
List getCounts(int minimumThreshold) const
static CountedObjects & getInstance() noexcept
diff --git a/Handler_8cpp_source.html b/Handler_8cpp_source.html index da50ee59af..0e60382a5d 100644 --- a/Handler_8cpp_source.html +++ b/Handler_8cpp_source.html @@ -428,7 +428,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value doNoRippleCheck(RPC::JsonContext &)
Json::Value doServerState(RPC::JsonContext &)
Json::Value doGatewayBalances(RPC::JsonContext &context)
-
Json::Value doLedgerEntry(RPC::JsonContext &)
+
Json::Value doLedgerEntry(RPC::JsonContext &)
diff --git a/Handler__test_8cpp_source.html b/Handler__test_8cpp_source.html index 163db9c4d5..434d45ee06 100644 --- a/Handler__test_8cpp_source.html +++ b/Handler__test_8cpp_source.html @@ -225,7 +225,7 @@ $(document).ready(function() { init_codefold(0); });
std::set< char const * > getHandlerNames()
Return names of all methods.
Definition Handler.cpp:242
Handler const * getHandler(unsigned version, bool betaEnabled, std::string const &name)
Definition Handler.cpp:236
auto make_vector(Input const &input)
-
std::ostream & operator<<(std::ostream &os, PrettyAmount const &amount)
Definition amount.cpp:54
+
std::ostream & operator<<(std::ostream &os, PrettyAmount const &amount)
Definition amount.cpp:55
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static auto sum(TCollection const &col)
Definition BookStep.cpp:872
diff --git a/Handlers_8h_source.html b/Handlers_8h_source.html index 751ee2f190..a1537e0906 100644 --- a/Handlers_8h_source.html +++ b/Handlers_8h_source.html @@ -305,7 +305,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value doNoRippleCheck(RPC::JsonContext &)
Json::Value doServerState(RPC::JsonContext &)
Json::Value doGatewayBalances(RPC::JsonContext &context)
-
Json::Value doLedgerEntry(RPC::JsonContext &)
+
Json::Value doLedgerEntry(RPC::JsonContext &)
diff --git a/HashRouter__test_8cpp_source.html b/HashRouter__test_8cpp_source.html index e78cf9a09d..cfbf0b0d34 100644 --- a/HashRouter__test_8cpp_source.html +++ b/HashRouter__test_8cpp_source.html @@ -543,7 +543,7 @@ $(document).ready(function() { init_codefold(0); }); -
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:111
+
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:112
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Stopwatch & stopwatch()
Returns an instance of a wall clock.
Definition chrono.h:93
HashRouter::Setup setup_HashRouter(Config const &config)
Create HashRouter setup from configuration.
diff --git a/IOUAmount_8cpp_source.html b/IOUAmount_8cpp_source.html index 522286af2b..f218862b57 100644 --- a/IOUAmount_8cpp_source.html +++ b/IOUAmount_8cpp_source.html @@ -414,10 +414,10 @@ $(document).ready(function() { init_codefold(0); });
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
IOUAmount & operator+=(IOUAmount const &other)
-
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:161
+
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:163
exponent_type exponent_
Definition IOUAmount.h:30
IOUAmount()=default
-
exponent_type exponent() const noexcept
Definition IOUAmount.h:155
+
exponent_type exponent() const noexcept
Definition IOUAmount.h:157
static IOUAmount fromNumber(Number const &number)
Definition IOUAmount.cpp:55
mantissa_type mantissa_
Definition IOUAmount.h:29
static IOUAmount minPositiveAmount()
Definition IOUAmount.cpp:65
diff --git a/IOUAmount_8h_source.html b/IOUAmount_8h_source.html index 557eb3a2dd..4030d34bd7 100644 --- a/IOUAmount_8h_source.html +++ b/IOUAmount_8h_source.html @@ -186,147 +186,149 @@ $(document).ready(function() { init_codefold(0); });
112
-
113inline IOUAmount::operator Number() const
-
114{
-
115 return Number{mantissa_, exponent_};
-
116}
+
113inline IOUAmount::
+
114operator Number() const
+
115{
+
116 return Number{mantissa_, exponent_};
+
117}
-
117
-
118inline IOUAmount&
-
- -
120{
-
121 *this += -other;
-
122 return *this;
-
123}
+
118
+
119inline IOUAmount&
+
+ +
121{
+
122 *this += -other;
+
123 return *this;
+
124}
-
124
-
125inline IOUAmount
-
- -
127{
-
128 return {-mantissa_, exponent_};
-
129}
+
125
+
126inline IOUAmount
+
+ +
128{
+
129 return {-mantissa_, exponent_};
+
130}
-
130
-
131inline bool
-
- -
133{
-
134 return exponent_ == other.exponent_ && mantissa_ == other.mantissa_;
-
135}
+
131
+
132inline bool
+
+ +
134{
+
135 return exponent_ == other.exponent_ && mantissa_ == other.mantissa_;
+
136}
-
136
-
-
137inline bool
-
138IOUAmount::operator<(IOUAmount const& other) const
-
139{
-
140 return Number{*this} < Number{other};
-
141}
+
137
+
+
138inline bool
+
139IOUAmount::operator<(IOUAmount const& other) const
+
140{
+
141 return Number{*this} < Number{other};
+
142}
-
142
-
-
143inline IOUAmount::operator bool() const noexcept
-
144{
-
145 return mantissa_ != 0;
-
146}
+
143
+
+
144inline IOUAmount::
+
145operator bool() const noexcept
+
146{
+
147 return mantissa_ != 0;
+
148}
-
147
-
148inline int
-
-
149IOUAmount::signum() const noexcept
-
150{
-
151 return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
-
152}
+
149
+
150inline int
+
+
151IOUAmount::signum() const noexcept
+
152{
+
153 return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
+
154}
-
153
- -
-
155IOUAmount::exponent() const noexcept
-
156{
-
157 return exponent_;
-
158}
+
155
+ +
+
157IOUAmount::exponent() const noexcept
+
158{
+
159 return exponent_;
+
160}
-
159
- -
-
161IOUAmount::mantissa() const noexcept
-
162{
-
163 return mantissa_;
-
164}
+
161
+ +
+
163IOUAmount::mantissa() const noexcept
+
164{
+
165 return mantissa_;
+
166}
-
165
- -
167to_string(IOUAmount const& amount);
-
168
-
169/* Return num*amt/den
-
170 This function keeps more precision than computing
-
171 num*amt, storing the result in an IOUAmount, then
-
172 dividing by den.
-
173*/
- -
175mulRatio(IOUAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp);
-
176
-
177// Since many uses of the number class do not have access to a ledger,
-
178// getSTNumberSwitchover needs to be globally accessible.
-
179
-
180bool
- -
182
-
183void
- -
185
-
- -
190{
-
191 bool saved_;
-
192
-
193public:
-
- -
195 {
- -
197 }
+
167
+ +
169to_string(IOUAmount const& amount);
+
170
+
171/* Return num*amt/den
+
172 This function keeps more precision than computing
+
173 num*amt, storing the result in an IOUAmount, then
+
174 dividing by den.
+
175*/
+ +
177mulRatio(IOUAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp);
+
178
+
179// Since many uses of the number class do not have access to a ledger,
+
180// getSTNumberSwitchover needs to be globally accessible.
+
181
+
182bool
+ +
184
+
185void
+ +
187
+
+ +
192{
+
193 bool saved_;
+
194
+
195public:
+
+ +
197 {
+ +
199 }
-
198
-
199 NumberSO(NumberSO const&) = delete;
-
200 NumberSO&
-
201 operator=(NumberSO const&) = delete;
-
202
-
- -
204 {
- -
206 }
+
200
+
201 NumberSO(NumberSO const&) = delete;
+
202 NumberSO&
+
203 operator=(NumberSO const&) = delete;
+
204
+
+ +
206 {
+ +
208 }
-
207};
+
209};
-
208
-
209} // namespace xrpl
+
210
+
211} // namespace xrpl
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
IOUAmount & operator+=(IOUAmount const &other)
-
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:161
+
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:163
exponent_type exponent_
Definition IOUAmount.h:30
-
bool operator==(IOUAmount const &other) const
Definition IOUAmount.h:132
+
bool operator==(IOUAmount const &other) const
Definition IOUAmount.h:133
IOUAmount()=default
-
exponent_type exponent() const noexcept
Definition IOUAmount.h:155
+
exponent_type exponent() const noexcept
Definition IOUAmount.h:157
static IOUAmount fromNumber(Number const &number)
Definition IOUAmount.cpp:55
-
bool operator<(IOUAmount const &other) const
Definition IOUAmount.h:138
+
bool operator<(IOUAmount const &other) const
Definition IOUAmount.h:139
IOUAmount & operator=(beast::Zero)
Definition IOUAmount.h:104
-
IOUAmount operator-() const
Definition IOUAmount.h:126
+
IOUAmount operator-() const
Definition IOUAmount.h:127
mantissa_type mantissa_
Definition IOUAmount.h:29
-
int signum() const noexcept
Return the sign of the amount.
Definition IOUAmount.h:149
+
int signum() const noexcept
Return the sign of the amount.
Definition IOUAmount.h:151
static IOUAmount minPositiveAmount()
Definition IOUAmount.cpp:65
void normalize()
Adjusts the mantissa and exponent to the proper range.
Definition IOUAmount.cpp:71
-
IOUAmount & operator-=(IOUAmount const &other)
Definition IOUAmount.h:119
-
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:190
+
IOUAmount & operator-=(IOUAmount const &other)
Definition IOUAmount.h:120
+
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:192
NumberSO & operator=(NumberSO const &)=delete
- -
NumberSO(bool v)
Definition IOUAmount.h:203
+ +
NumberSO(bool v)
Definition IOUAmount.h:205
NumberSO(NumberSO const &)=delete
- +
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
diff --git a/IOUAmount__test_8cpp_source.html b/IOUAmount__test_8cpp_source.html index a7a51f673b..61504be174 100644 --- a/IOUAmount__test_8cpp_source.html +++ b/IOUAmount__test_8cpp_source.html @@ -380,9 +380,9 @@ $(document).ready(function() { init_codefold(0); });
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
-
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:161
-
exponent_type exponent() const noexcept
Definition IOUAmount.h:155
-
int signum() const noexcept
Return the sign of the amount.
Definition IOUAmount.h:149
+
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:163
+
exponent_type exponent() const noexcept
Definition IOUAmount.h:157
+
int signum() const noexcept
Return the sign of the amount.
Definition IOUAmount.h:151
Sets the new scale and restores the old scale when it leaves scope.
Definition Number.h:800
T max(T... args)
diff --git a/IPEndpoint__test_8cpp_source.html b/IPEndpoint__test_8cpp_source.html index dc47539a65..654c211fc2 100644 --- a/IPEndpoint__test_8cpp_source.html +++ b/IPEndpoint__test_8cpp_source.html @@ -435,9 +435,9 @@ $(document).ready(function() { init_codefold(0); });
334
335#if BOOST_OS_WINDOWS
336 // windows asio bugs...false positives
-
337 shouldParseEPV4("255", {{ 0, 0, 0, 255 }}, 0, "0.0.0.255");
-
338 shouldParseEPV4("512", {{ 0, 0, 2, 0 }}, 0, "0.0.2.0");
-
339 shouldParseEPV4("1.2.3:80", {{ 1, 2, 0, 3 }}, 80, "1.2.0.3:80");
+
337 shouldParseEPV4("255", {{0, 0, 0, 255}}, 0, "0.0.0.255");
+
338 shouldParseEPV4("512", {{0, 0, 2, 0}}, 0, "0.0.2.0");
+
339 shouldParseEPV4("1.2.3:80", {{1, 2, 0, 3}}, 80, "1.2.0.3:80");
340#else
341 failParseEP("255");
342 failParseEP("512");
diff --git a/InboundLedger_8cpp_source.html b/InboundLedger_8cpp_source.html index 7dd74044ee..8deef24869 100644 --- a/InboundLedger_8cpp_source.html +++ b/InboundLedger_8cpp_source.html @@ -1361,7 +1361,7 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
ValueType type() const
Stream fatal() const
Definition Journal.h:324
Stream debug() const
Definition Journal.h:300
@@ -1480,7 +1480,7 @@ $(document).ready(function() { init_codefold(0); });
@ missingNodesFind
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
@ hotLEDGER
Definition NodeObject.h:14
LedgerHeader deserializeHeader(Slice data, bool hasHash=false)
Deserialize a ledger header from a byte array.
@ jtLEDGER_DATA
Definition Job.h:45
diff --git a/Indexes_8h_source.html b/Indexes_8h_source.html index 9301e8879c..ffd6d7c7d1 100644 --- a/Indexes_8h_source.html +++ b/Indexes_8h_source.html @@ -476,7 +476,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition Indexes.cpp:133
std::uint64_t getQuality(uint256 const &uBase)
Definition Indexes.cpp:126
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
base_uint< 256 > uint256
Definition base_uint.h:526
base_uint< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:44
diff --git a/InvariantCheck_8cpp_source.html b/InvariantCheck_8cpp_source.html index 46d8f8ecb8..390c91c670 100644 --- a/InvariantCheck_8cpp_source.html +++ b/InvariantCheck_8cpp_source.html @@ -3595,7 +3595,7 @@ $(document).ready(function() { init_codefold(0); });
uint256 const & key() const
Returns the 'key' (or 'index') of this item.
LedgerEntryType getType() const
uint192 getFieldH192(SField const &field) const
Definition STObject.cpp:600
-
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1041
+
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1047
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:576
uint256 getHash(HashPrefix prefix) const
Definition STObject.cpp:350
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
@@ -3754,11 +3754,11 @@ $(document).ready(function() { init_codefold(0); });
bool hasPrivilege(STTx const &tx, Privilege priv)
@ ahIGNORE_AUTH
Definition View.h:61
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3436
-
Number root2(Number f)
Definition Number.cpp:1010
+
Number root2(Number f)
Definition Number.cpp:1011
std::size_t constexpr maxPermissionedDomainCredentialsArraySize
The maximum number of credentials can be passed in array for permissioned domain.
Definition Protocol.h:227
std::pair< STAmount, STAmount > ammPoolHolds(ReadView const &view, AccountID const &ammAccountID, Issue const &issue1, Issue const &issue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool balances.
Definition AMMUtils.cpp:12
@ transactionID
transaction plus signature to give transaction ID
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
constexpr XRPAmount INITIAL_XRP
Configure the native currency.
bool withinRelativeDistance(Quality const &calcQuality, Quality const &reqQuality, Number const &dist)
Check if the relative distance between the qualities is within the requested distance.
Definition AMMHelpers.h:98
diff --git a/Invariants__test_8cpp_source.html b/Invariants__test_8cpp_source.html index 6244864bca..bca47ce84d 100644 --- a/Invariants__test_8cpp_source.html +++ b/Invariants__test_8cpp_source.html @@ -3981,7 +3981,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value mint(jtx::Account const &account, std::uint32_t nfTokenTaxon)
Mint an NFToken.
Definition token.cpp:15
static MPTInit const mptInitNoFund
Definition mpt.h:103
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
static increment_t const increment
Definition tags.h:40
FeatureBitset testable_amendments()
Definition Env.h:76
@@ -4002,7 +4002,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:128
base_uint< 256 > uint256
Definition base_uint.h:526
constexpr std::uint32_t const tfMPTCanClawback
Definition TxFlags.h:133
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
base_uint< 160 > uint160
Definition base_uint.h:525
base_uint< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:44
diff --git a/Issue_8cpp_source.html b/Issue_8cpp_source.html index 672ad93f98..c8053b2f3a 100644 --- a/Issue_8cpp_source.html +++ b/Issue_8cpp_source.html @@ -240,11 +240,11 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
bool isString() const
-
bool isObject() const
+
bool isString() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
-
bool isNull() const
isNull() tests to see if this field is null.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isNull() const
isNull() tests to see if this field is null.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
AccountID account
Definition Issue.h:16
diff --git a/JSONRPCClient_8cpp_source.html b/JSONRPCClient_8cpp_source.html index c11ff52b54..ed61fb2473 100644 --- a/JSONRPCClient_8cpp_source.html +++ b/JSONRPCClient_8cpp_source.html @@ -252,7 +252,7 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Holds unparsed configuration information.
bool exists(std::string const &name) const
Returns true if a section with the given name exists.
Section & section(std::string const &name)
Returns the section with the given name.
diff --git a/JSONRPCUtil_8cpp_source.html b/JSONRPCUtil_8cpp_source.html index 7bb42f96f0..80c62b16e0 100644 --- a/JSONRPCUtil_8cpp_source.html +++ b/JSONRPCUtil_8cpp_source.html @@ -103,122 +103,120 @@ $(document).ready(function() { init_codefold(0); });
19 char buffer[96];
20 time_t now;
21 time(&now);
-
22 struct tm now_gmt
-
23 {
-
24 };
-
25#ifndef _MSC_VER
-
26 gmtime_r(&now, &now_gmt);
-
27#else
-
28 gmtime_s(&now_gmt, &now);
-
29#endif
-
30 strftime(buffer, sizeof(buffer), "Date: %a, %d %b %Y %H:%M:%S +0000\r\n", &now_gmt);
-
31 return std::string(buffer);
-
32}
+
22 struct tm now_gmt{};
+
23#ifndef _MSC_VER
+
24 gmtime_r(&now, &now_gmt);
+
25#else
+
26 gmtime_s(&now_gmt, &now);
+
27#endif
+
28 strftime(buffer, sizeof(buffer), "Date: %a, %d %b %Y %H:%M:%S +0000\r\n", &now_gmt);
+
29 return std::string(buffer);
+
30}
-
33
-
34void
-
-
35HTTPReply(int nStatus, std::string const& content, Json::Output const& output, beast::Journal j)
-
36{
-
37 JLOG(j.trace()) << "HTTP Reply " << nStatus << " " << content;
-
38
-
39 if (content.empty() && nStatus == 401)
-
40 {
-
41 output("HTTP/1.0 401 Authorization Required\r\n");
-
42 output(getHTTPHeaderTimestamp());
-
43
-
44 // CHECKME this returns a different version than the replies below. Is
-
45 // this by design or an accident or should it be using
-
46 // BuildInfo::getFullVersionString () as well?
-
47 output("Server: " + systemName() + "-json-rpc/v1");
-
48 output("\r\n");
-
49
-
50 // Be careful in modifying this! If you change the contents you MUST
-
51 // update the Content-Length header as well to indicate the correct
-
52 // size of the data.
-
53 output(
-
54 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
-
55 "Content-Type: text/html\r\n"
-
56 "Content-Length: 296\r\n"
-
57 "\r\n"
-
58 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 "
-
59 "Transitional//EN\"\r\n"
-
60 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"
-
61 "\">\r\n"
-
62 "<HTML>\r\n"
-
63 "<HEAD>\r\n"
-
64 "<TITLE>Error</TITLE>\r\n"
-
65 "<META HTTP-EQUIV='Content-Type' "
-
66 "CONTENT='text/html; charset=ISO-8859-1'>\r\n"
-
67 "</HEAD>\r\n"
-
68 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n");
-
69
-
70 return;
-
71 }
-
72
-
73 switch (nStatus)
-
74 {
-
75 case 200:
-
76 output("HTTP/1.1 200 OK\r\n");
-
77 break;
-
78 case 202:
-
79 output("HTTP/1.1 202 Accepted\r\n");
-
80 break;
-
81 case 400:
-
82 output("HTTP/1.1 400 Bad Request\r\n");
-
83 break;
-
84 case 401:
-
85 output("HTTP/1.1 401 Authorization Required\r\n");
-
86 break;
-
87 case 403:
-
88 output("HTTP/1.1 403 Forbidden\r\n");
-
89 break;
-
90 case 404:
-
91 output("HTTP/1.1 404 Not Found\r\n");
-
92 break;
-
93 case 405:
-
94 output("HTTP/1.1 405 Method Not Allowed\r\n");
-
95 break;
-
96 case 429:
-
97 output("HTTP/1.1 429 Too Many Requests\r\n");
-
98 break;
-
99 case 500:
-
100 output("HTTP/1.1 500 Internal Server Error\r\n");
-
101 break;
-
102 case 501:
-
103 output("HTTP/1.1 501 Not Implemented\r\n");
-
104 break;
-
105 case 503:
-
106 output("HTTP/1.1 503 Server is overloaded\r\n");
-
107 break;
-
108 }
+
31
+
32void
+
+
33HTTPReply(int nStatus, std::string const& content, Json::Output const& output, beast::Journal j)
+
34{
+
35 JLOG(j.trace()) << "HTTP Reply " << nStatus << " " << content;
+
36
+
37 if (content.empty() && nStatus == 401)
+
38 {
+
39 output("HTTP/1.0 401 Authorization Required\r\n");
+
40 output(getHTTPHeaderTimestamp());
+
41
+
42 // CHECKME this returns a different version than the replies below. Is
+
43 // this by design or an accident or should it be using
+
44 // BuildInfo::getFullVersionString () as well?
+
45 output("Server: " + systemName() + "-json-rpc/v1");
+
46 output("\r\n");
+
47
+
48 // Be careful in modifying this! If you change the contents you MUST
+
49 // update the Content-Length header as well to indicate the correct
+
50 // size of the data.
+
51 output(
+
52 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
+
53 "Content-Type: text/html\r\n"
+
54 "Content-Length: 296\r\n"
+
55 "\r\n"
+
56 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 "
+
57 "Transitional//EN\"\r\n"
+
58 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"
+
59 "\">\r\n"
+
60 "<HTML>\r\n"
+
61 "<HEAD>\r\n"
+
62 "<TITLE>Error</TITLE>\r\n"
+
63 "<META HTTP-EQUIV='Content-Type' "
+
64 "CONTENT='text/html; charset=ISO-8859-1'>\r\n"
+
65 "</HEAD>\r\n"
+
66 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n");
+
67
+
68 return;
+
69 }
+
70
+
71 switch (nStatus)
+
72 {
+
73 case 200:
+
74 output("HTTP/1.1 200 OK\r\n");
+
75 break;
+
76 case 202:
+
77 output("HTTP/1.1 202 Accepted\r\n");
+
78 break;
+
79 case 400:
+
80 output("HTTP/1.1 400 Bad Request\r\n");
+
81 break;
+
82 case 401:
+
83 output("HTTP/1.1 401 Authorization Required\r\n");
+
84 break;
+
85 case 403:
+
86 output("HTTP/1.1 403 Forbidden\r\n");
+
87 break;
+
88 case 404:
+
89 output("HTTP/1.1 404 Not Found\r\n");
+
90 break;
+
91 case 405:
+
92 output("HTTP/1.1 405 Method Not Allowed\r\n");
+
93 break;
+
94 case 429:
+
95 output("HTTP/1.1 429 Too Many Requests\r\n");
+
96 break;
+
97 case 500:
+
98 output("HTTP/1.1 500 Internal Server Error\r\n");
+
99 break;
+
100 case 501:
+
101 output("HTTP/1.1 501 Not Implemented\r\n");
+
102 break;
+
103 case 503:
+
104 output("HTTP/1.1 503 Server is overloaded\r\n");
+
105 break;
+
106 }
+
107
+
108 output(getHTTPHeaderTimestamp());
109
-
110 output(getHTTPHeaderTimestamp());
-
111
-
112 output(
-
113 "Connection: Keep-Alive\r\n"
-
114 "Content-Length: ");
-
115
-
116 // VFALCO TODO Determine if/when this header should be added
-
117 // if (context.app.config().RPC_ALLOW_REMOTE)
-
118 // output ("Access-Control-Allow-Origin: *\r\n");
-
119
-
120 output(std::to_string(content.size() + 2));
-
121 output(
-
122 "\r\n"
-
123 "Content-Type: application/json; charset=UTF-8\r\n");
-
124
-
125 output("Server: " + systemName() + "-json-rpc/");
- -
127 output(
-
128 "\r\n"
-
129 "\r\n");
-
130 output(content);
-
131 output("\r\n");
-
132}
+
110 output(
+
111 "Connection: Keep-Alive\r\n"
+
112 "Content-Length: ");
+
113
+
114 // VFALCO TODO Determine if/when this header should be added
+
115 // if (context.app.config().RPC_ALLOW_REMOTE)
+
116 // output ("Access-Control-Allow-Origin: *\r\n");
+
117
+
118 output(std::to_string(content.size() + 2));
+
119 output(
+
120 "\r\n"
+
121 "Content-Type: application/json; charset=UTF-8\r\n");
+
122
+
123 output("Server: " + systemName() + "-json-rpc/");
+ +
125 output(
+
126 "\r\n"
+
127 "\r\n");
+
128 output(content);
+
129 output("\r\n");
+
130}
-
133
-
134} // namespace xrpl
+
131
+
132} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream trace() const
Severity stream access functions.
Definition Journal.h:294
@@ -227,7 +225,7 @@ $(document).ready(function() { init_codefold(0); });
std::string const & getFullVersionString()
Full server version string.
Definition BuildInfo.cpp:64
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
-
void HTTPReply(int nStatus, std::string const &strMsg, Json::Output const &, beast::Journal j)
+
void HTTPReply(int nStatus, std::string const &strMsg, Json::Output const &, beast::Journal j)
std::string getHTTPHeaderTimestamp()
static std::string const & systemName()
T size(T... args)
diff --git a/JSONRPCUtil_8h_source.html b/JSONRPCUtil_8h_source.html index ad4603462e..5cc7221b06 100644 --- a/JSONRPCUtil_8h_source.html +++ b/JSONRPCUtil_8h_source.html @@ -97,7 +97,7 @@ $(document).ready(function() { init_codefold(0); });
A generic endpoint for log messages.
Definition Journal.h:40
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
-
void HTTPReply(int nStatus, std::string const &strMsg, Json::Output const &, beast::Journal j)
+
void HTTPReply(int nStatus, std::string const &strMsg, Json::Output const &, beast::Journal j)
-
248
-
249static Expected<uint256, Json::Value>
-
- -
251 Json::Value const& dp,
-
252 Json::StaticString const fieldName,
-
253 [[maybe_unused]] unsigned const apiVersion)
-
254{
-
255 if (!dp.isObject())
-
256 {
-
257 return parseObjectID(dp, fieldName);
-
258 }
-
259
-
260 if ((dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)))
-
261 {
- -
263 "malformedRequest",
-
264 "Must have exactly one of `authorized` and "
-
265 "`authorized_credentials`.");
-
266 }
-
267
-
268 auto const owner = LedgerEntryHelpers::requiredAccountID(dp, jss::owner, "malformedOwner");
-
269 if (!owner)
-
270 {
-
271 return Unexpected(owner.error());
-
272 }
-
273
-
274 if (dp.isMember(jss::authorized))
-
275 {
-
276 if (auto const authorized = LedgerEntryHelpers::parse<AccountID>(dp[jss::authorized]))
-
277 {
-
278 return keylet::depositPreauth(*owner, *authorized).key;
-
279 }
-
280 return LedgerEntryHelpers::invalidFieldError("malformedAuthorized", jss::authorized, "AccountID");
-
281 }
-
282
-
283 auto const& ac(dp[jss::authorized_credentials]);
-
284 auto const arr = parseAuthorizeCredentials(ac);
-
285 if (!arr.has_value())
-
286 return Unexpected(arr.error());
-
287
-
288 auto const& sorted = credentials::makeSorted(arr.value());
-
289 if (sorted.empty())
-
290 {
-
291 // TODO: this error message is bad/inaccurate
- -
293 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
-
294 }
-
295
-
296 return keylet::depositPreauth(*owner, std::move(sorted)).key;
-
297}
+
250
+
251static Expected<uint256, Json::Value>
+
+ +
253 Json::Value const& dp,
+
254 Json::StaticString const fieldName,
+
255 [[maybe_unused]] unsigned const apiVersion)
+
256{
+
257 if (!dp.isObject())
+
258 {
+
259 return parseObjectID(dp, fieldName);
+
260 }
+
261
+
262 if ((dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)))
+
263 {
+ +
265 "malformedRequest",
+
266 "Must have exactly one of `authorized` and "
+
267 "`authorized_credentials`.");
+
268 }
+
269
+
270 auto const owner = LedgerEntryHelpers::requiredAccountID(dp, jss::owner, "malformedOwner");
+
271 if (!owner)
+
272 {
+
273 return Unexpected(owner.error());
+
274 }
+
275
+
276 if (dp.isMember(jss::authorized))
+
277 {
+
278 if (auto const authorized = LedgerEntryHelpers::parse<AccountID>(dp[jss::authorized]))
+
279 {
+
280 return keylet::depositPreauth(*owner, *authorized).key;
+
281 }
+
282 return LedgerEntryHelpers::invalidFieldError("malformedAuthorized", jss::authorized, "AccountID");
+
283 }
+
284
+
285 auto const& ac(dp[jss::authorized_credentials]);
+
286 auto const arr = parseAuthorizeCredentials(ac);
+
287 if (!arr.has_value())
+
288 return Unexpected(arr.error());
+
289
+
290 auto const& sorted = credentials::makeSorted(arr.value());
+
291 if (sorted.empty())
+
292 {
+
293 // TODO: this error message is bad/inaccurate
+ +
295 "malformedAuthorizedCredentials", jss::authorized_credentials, "array");
+
296 }
+
297
+
298 return keylet::depositPreauth(*owner, std::move(sorted)).key;
+
299}
-
298
-
299static Expected<uint256, Json::Value>
-
-
300parseDID(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
-
301{
-
302 auto const account = LedgerEntryHelpers::parse<AccountID>(params);
-
303 if (!account)
-
304 {
-
305 return LedgerEntryHelpers::invalidFieldError("malformedAddress", fieldName, "AccountID");
-
306 }
-
307
-
308 return keylet::did(*account).key;
-
309}
+
300
+
301static Expected<uint256, Json::Value>
+
+
302parseDID(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
+
303{
+
304 auto const account = LedgerEntryHelpers::parse<AccountID>(params);
+
305 if (!account)
+
306 {
+
307 return LedgerEntryHelpers::invalidFieldError("malformedAddress", fieldName, "AccountID");
+
308 }
+
309
+
310 return keylet::did(*account).key;
+
311}
-
310
-
311static Expected<uint256, Json::Value>
-
- -
313 Json::Value const& params,
-
314 Json::StaticString const fieldName,
-
315 [[maybe_unused]] unsigned const apiVersion)
-
316{
-
317 if (!params.isObject())
-
318 {
-
319 return parseObjectID(params, fieldName);
-
320 }
-
321
-
322 if (params.isMember(jss::sub_index) &&
-
323 (!params[jss::sub_index].isConvertibleTo(Json::uintValue) || params[jss::sub_index].isBool()))
-
324 {
-
325 return LedgerEntryHelpers::invalidFieldError("malformedRequest", jss::sub_index, "number");
-
326 }
-
327
-
328 if (params.isMember(jss::owner) == params.isMember(jss::dir_root))
-
329 {
- -
331 "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
-
332 }
-
333
-
334 std::uint64_t uSubIndex = params.get(jss::sub_index, 0).asUInt();
+
312
+
313static Expected<uint256, Json::Value>
+
+ +
315 Json::Value const& params,
+
316 Json::StaticString const fieldName,
+
317 [[maybe_unused]] unsigned const apiVersion)
+
318{
+
319 if (!params.isObject())
+
320 {
+
321 return parseObjectID(params, fieldName);
+
322 }
+
323
+
324 if (params.isMember(jss::sub_index) &&
+
325 (!params[jss::sub_index].isConvertibleTo(Json::uintValue) || params[jss::sub_index].isBool()))
+
326 {
+
327 return LedgerEntryHelpers::invalidFieldError("malformedRequest", jss::sub_index, "number");
+
328 }
+
329
+
330 if (params.isMember(jss::owner) == params.isMember(jss::dir_root))
+
331 {
+ +
333 "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
+
334 }
335
-
336 if (params.isMember(jss::dir_root))
-
337 {
-
338 if (auto const uDirRoot = LedgerEntryHelpers::parse<uint256>(params[jss::dir_root]))
-
339 {
-
340 return keylet::page(*uDirRoot, uSubIndex).key;
-
341 }
-
342
-
343 return LedgerEntryHelpers::invalidFieldError("malformedDirRoot", jss::dir_root, "hash");
-
344 }
-
345
-
346 if (params.isMember(jss::owner))
-
347 {
-
348 auto const ownerID = LedgerEntryHelpers::parse<AccountID>(params[jss::owner]);
-
349 if (!ownerID)
-
350 {
-
351 return LedgerEntryHelpers::invalidFieldError("malformedAddress", jss::owner, "AccountID");
-
352 }
-
353
-
354 return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key;
-
355 }
-
356
-
357 return LedgerEntryHelpers::malformedError("malformedRequest", "");
-
358}
+
336 std::uint64_t uSubIndex = params.get(jss::sub_index, 0).asUInt();
+
337
+
338 if (params.isMember(jss::dir_root))
+
339 {
+
340 if (auto const uDirRoot = LedgerEntryHelpers::parse<uint256>(params[jss::dir_root]))
+
341 {
+
342 return keylet::page(*uDirRoot, uSubIndex).key;
+
343 }
+
344
+
345 return LedgerEntryHelpers::invalidFieldError("malformedDirRoot", jss::dir_root, "hash");
+
346 }
+
347
+
348 if (params.isMember(jss::owner))
+
349 {
+
350 auto const ownerID = LedgerEntryHelpers::parse<AccountID>(params[jss::owner]);
+
351 if (!ownerID)
+
352 {
+
353 return LedgerEntryHelpers::invalidFieldError("malformedAddress", jss::owner, "AccountID");
+
354 }
+
355
+
356 return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key;
+
357 }
+
358
+
359 return LedgerEntryHelpers::malformedError("malformedRequest", "");
+
360}
-
359
-
360static Expected<uint256, Json::Value>
-
-
361parseEscrow(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
-
362{
-
363 if (!params.isObject())
-
364 {
-
365 return parseObjectID(params, fieldName);
-
366 }
-
367
-
368 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
-
369 if (!id)
-
370 return Unexpected(id.error());
-
371 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq");
-
372 if (!seq)
-
373 return Unexpected(seq.error());
-
374
-
375 return keylet::escrow(*id, *seq).key;
-
376}
+
361
+
362static Expected<uint256, Json::Value>
+
+
363parseEscrow(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
+
364{
+
365 if (!params.isObject())
+
366 {
+
367 return parseObjectID(params, fieldName);
+
368 }
+
369
+
370 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
+
371 if (!id)
+
372 return Unexpected(id.error());
+
373 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq");
+
374 if (!seq)
+
375 return Unexpected(seq.error());
+
376
+
377 return keylet::escrow(*id, *seq).key;
+
378}
-
377
-
379
- -
- -
382 Keylet const& keylet,
-
383 Json::Value const& params,
-
384 Json::StaticString const& fieldName,
-
385 [[maybe_unused]] unsigned const apiVersion)
-
386{
-
387 if (!params.isBool())
-
388 {
-
389 return parseObjectID(params, fieldName, "hex string");
-
390 }
-
391 if (!params.asBool())
-
392 {
-
393 return LedgerEntryHelpers::invalidFieldError("invalidParams", fieldName, "true");
-
394 }
-
395
-
396 return keylet.key;
-
397}
+ +
381
+ +
+ +
384 Keylet const& keylet,
+
385 Json::Value const& params,
+
386 Json::StaticString const& fieldName,
+
387 [[maybe_unused]] unsigned const apiVersion)
+
388{
+
389 if (!params.isBool())
+
390 {
+
391 return parseObjectID(params, fieldName, "hex string");
+
392 }
+
393 if (!params.asBool())
+
394 {
+
395 return LedgerEntryHelpers::invalidFieldError("invalidParams", fieldName, "true");
+
396 }
+
397
+
398 return keylet.key;
+
399}
-
398
-
399static Expected<uint256, Json::Value>
-
-
400parseLedgerHashes(Json::Value const& params, Json::StaticString const fieldName, unsigned const apiVersion)
-
401{
-
402 if (params.isUInt() || params.isInt())
-
403 {
-
404 // If the index doesn't parse as a UInt, throw
-
405 auto const index = params.asUInt();
-
406
-
407 // Return the "long" skip list for the given ledger index.
-
408 auto const keylet = keylet::skip(index);
-
409 return keylet.key;
-
410 }
-
411 // Return the key in `params` or the "short" skip list, which contains
-
412 // hashes since the last flag ledger.
-
413 return parseFixed(keylet::skip(), params, fieldName, apiVersion);
-
414}
+
400
+
401static Expected<uint256, Json::Value>
+
+
402parseLedgerHashes(Json::Value const& params, Json::StaticString const fieldName, unsigned const apiVersion)
+
403{
+
404 if (params.isUInt() || params.isInt())
+
405 {
+
406 // If the index doesn't parse as a UInt, throw
+
407 auto const index = params.asUInt();
+
408
+
409 // Return the "long" skip list for the given ledger index.
+
410 auto const keylet = keylet::skip(index);
+
411 return keylet.key;
+
412 }
+
413 // Return the key in `params` or the "short" skip list, which contains
+
414 // hashes since the last flag ledger.
+
415 return parseFixed(keylet::skip(), params, fieldName, apiVersion);
+
416}
-
415
-
416static Expected<uint256, Json::Value>
-
- -
418 Json::Value const& params,
-
419 Json::StaticString const fieldName,
-
420 [[maybe_unused]] unsigned const apiVersion)
-
421{
-
422 if (!params.isObject())
-
423 {
-
424 return parseObjectID(params, fieldName, "hex string");
-
425 }
-
426
-
427 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
-
428 if (!id)
-
429 return Unexpected(id.error());
-
430 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq");
-
431 if (!seq)
-
432 return Unexpected(seq.error());
-
433
-
434 return keylet::loanbroker(*id, *seq).key;
-
435}
+
417
+
418static Expected<uint256, Json::Value>
+
+ +
420 Json::Value const& params,
+
421 Json::StaticString const fieldName,
+
422 [[maybe_unused]] unsigned const apiVersion)
+
423{
+
424 if (!params.isObject())
+
425 {
+
426 return parseObjectID(params, fieldName, "hex string");
+
427 }
+
428
+
429 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
+
430 if (!id)
+
431 return Unexpected(id.error());
+
432 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq");
+
433 if (!seq)
+
434 return Unexpected(seq.error());
+
435
+
436 return keylet::loanbroker(*id, *seq).key;
+
437}
-
436
-
437static Expected<uint256, Json::Value>
-
-
438parseLoan(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
-
439{
-
440 if (!params.isObject())
-
441 {
-
442 return parseObjectID(params, fieldName, "hex string");
-
443 }
-
444
-
445 auto const id = LedgerEntryHelpers::requiredUInt256(params, jss::loan_broker_id, "malformedBroker");
-
446 if (!id)
-
447 return Unexpected(id.error());
-
448 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::loan_seq, "malformedSeq");
-
449 if (!seq)
-
450 return Unexpected(seq.error());
-
451
-
452 return keylet::loan(*id, *seq).key;
-
453}
+
438
+
439static Expected<uint256, Json::Value>
+
+
440parseLoan(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
+
441{
+
442 if (!params.isObject())
+
443 {
+
444 return parseObjectID(params, fieldName, "hex string");
+
445 }
+
446
+
447 auto const id = LedgerEntryHelpers::requiredUInt256(params, jss::loan_broker_id, "malformedBroker");
+
448 if (!id)
+
449 return Unexpected(id.error());
+
450 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::loan_seq, "malformedSeq");
+
451 if (!seq)
+
452 return Unexpected(seq.error());
+
453
+
454 return keylet::loan(*id, *seq).key;
+
455}
-
454
-
455static Expected<uint256, Json::Value>
-
-
456parseMPToken(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
-
457{
-
458 if (!params.isObject())
-
459 {
-
460 return parseObjectID(params, fieldName);
-
461 }
-
462
-
463 auto const mptIssuanceID =
-
464 LedgerEntryHelpers::requiredUInt192(params, jss::mpt_issuance_id, "malformedMPTIssuanceID");
-
465 if (!mptIssuanceID)
-
466 return Unexpected(mptIssuanceID.error());
-
467
-
468 auto const account = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount");
-
469 if (!account)
-
470 return Unexpected(account.error());
-
471
-
472 return keylet::mptoken(*mptIssuanceID, *account).key;
-
473}
+
456
+
457static Expected<uint256, Json::Value>
+
+
458parseMPToken(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
+
459{
+
460 if (!params.isObject())
+
461 {
+
462 return parseObjectID(params, fieldName);
+
463 }
+
464
+
465 auto const mptIssuanceID =
+
466 LedgerEntryHelpers::requiredUInt192(params, jss::mpt_issuance_id, "malformedMPTIssuanceID");
+
467 if (!mptIssuanceID)
+
468 return Unexpected(mptIssuanceID.error());
+
469
+
470 auto const account = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount");
+
471 if (!account)
+
472 return Unexpected(account.error());
+
473
+
474 return keylet::mptoken(*mptIssuanceID, *account).key;
+
475}
-
474
-
475static Expected<uint256, Json::Value>
-
- -
477 Json::Value const& params,
-
478 Json::StaticString const fieldName,
-
479 [[maybe_unused]] unsigned const apiVersion)
-
480{
-
481 auto const mptIssuanceID = LedgerEntryHelpers::parse<uint192>(params);
-
482 if (!mptIssuanceID)
-
483 return LedgerEntryHelpers::invalidFieldError("malformedMPTokenIssuance", fieldName, "Hash192");
-
484
-
485 return keylet::mptIssuance(*mptIssuanceID).key;
-
486}
+
476
+
477static Expected<uint256, Json::Value>
+
+ +
479 Json::Value const& params,
+
480 Json::StaticString const fieldName,
+
481 [[maybe_unused]] unsigned const apiVersion)
+
482{
+
483 auto const mptIssuanceID = LedgerEntryHelpers::parse<uint192>(params);
+
484 if (!mptIssuanceID)
+
485 return LedgerEntryHelpers::invalidFieldError("malformedMPTokenIssuance", fieldName, "Hash192");
+
486
+
487 return keylet::mptIssuance(*mptIssuanceID).key;
+
488}
-
487
-
488static Expected<uint256, Json::Value>
-
- -
490 Json::Value const& params,
-
491 Json::StaticString const fieldName,
-
492 [[maybe_unused]] unsigned const apiVersion)
-
493{
-
494 return parseObjectID(params, fieldName, "hex string");
-
495}
+
489
+
490static Expected<uint256, Json::Value>
+
+ +
492 Json::Value const& params,
+
493 Json::StaticString const fieldName,
+
494 [[maybe_unused]] unsigned const apiVersion)
+
495{
+
496 return parseObjectID(params, fieldName, "hex string");
+
497}
-
496
-
497static Expected<uint256, Json::Value>
-
- -
499 Json::Value const& params,
-
500 Json::StaticString const fieldName,
-
501 [[maybe_unused]] unsigned const apiVersion)
-
502{
-
503 return parseObjectID(params, fieldName, "hex string");
-
504}
+
498
+
499static Expected<uint256, Json::Value>
+
+ +
501 Json::Value const& params,
+
502 Json::StaticString const fieldName,
+
503 [[maybe_unused]] unsigned const apiVersion)
+
504{
+
505 return parseObjectID(params, fieldName, "hex string");
+
506}
-
505
-
507
- -
-
509parseOffer(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
-
510{
-
511 if (!params.isObject())
-
512 {
-
513 return parseObjectID(params, fieldName);
-
514 }
-
515
-
516 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
-
517 if (!id)
-
518 return Unexpected(id.error());
-
519
-
520 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest");
-
521 if (!seq)
-
522 return Unexpected(seq.error());
-
523
-
524 return keylet::offer(*id, *seq).key;
-
525}
+ +
509
+ +
+
511parseOffer(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
+
512{
+
513 if (!params.isObject())
+
514 {
+
515 return parseObjectID(params, fieldName);
+
516 }
+
517
+
518 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
+
519 if (!id)
+
520 return Unexpected(id.error());
+
521
+
522 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest");
+
523 if (!seq)
+
524 return Unexpected(seq.error());
+
525
+
526 return keylet::offer(*id, *seq).key;
+
527}
-
526
-
527static Expected<uint256, Json::Value>
-
-
528parseOracle(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
-
529{
-
530 if (!params.isObject())
-
531 {
-
532 return parseObjectID(params, fieldName);
-
533 }
-
534
-
535 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount");
-
536 if (!id)
-
537 return Unexpected(id.error());
-
538
-
539 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::oracle_document_id, "malformedDocumentID");
-
540 if (!seq)
-
541 return Unexpected(seq.error());
-
542
-
543 return keylet::oracle(*id, *seq).key;
-
544}
+
528
+
529static Expected<uint256, Json::Value>
+
+
530parseOracle(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
+
531{
+
532 if (!params.isObject())
+
533 {
+
534 return parseObjectID(params, fieldName);
+
535 }
+
536
+
537 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount");
+
538 if (!id)
+
539 return Unexpected(id.error());
+
540
+
541 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::oracle_document_id, "malformedDocumentID");
+
542 if (!seq)
+
543 return Unexpected(seq.error());
+
544
+
545 return keylet::oracle(*id, *seq).key;
+
546}
-
545
-
546static Expected<uint256, Json::Value>
-
- -
548 Json::Value const& params,
-
549 Json::StaticString const fieldName,
-
550 [[maybe_unused]] unsigned const apiVersion)
-
551{
-
552 return parseObjectID(params, fieldName, "hex string");
-
553}
+
547
+
548static Expected<uint256, Json::Value>
+
+ +
550 Json::Value const& params,
+
551 Json::StaticString const fieldName,
+
552 [[maybe_unused]] unsigned const apiVersion)
+
553{
+
554 return parseObjectID(params, fieldName, "hex string");
+
555}
-
554
-
555static Expected<uint256, Json::Value>
-
- -
557 Json::Value const& pd,
-
558 Json::StaticString const fieldName,
-
559 [[maybe_unused]] unsigned const apiVersion)
-
560{
-
561 if (pd.isString())
-
562 {
-
563 return parseObjectID(pd, fieldName);
-
564 }
-
565
-
566 if (!pd.isObject())
-
567 {
-
568 return LedgerEntryHelpers::invalidFieldError("malformedRequest", fieldName, "hex string or object");
-
569 }
-
570
-
571 auto const account = LedgerEntryHelpers::requiredAccountID(pd, jss::account, "malformedAddress");
-
572 if (!account)
-
573 return Unexpected(account.error());
-
574
-
575 auto const seq = LedgerEntryHelpers::requiredUInt32(pd, jss::seq, "malformedRequest");
-
576 if (!seq)
-
577 return Unexpected(seq.error());
-
578
-
579 return keylet::permissionedDomain(*account, pd[jss::seq].asUInt()).key;
-
580}
+
556
+
557static Expected<uint256, Json::Value>
+
+ +
559 Json::Value const& pd,
+
560 Json::StaticString const fieldName,
+
561 [[maybe_unused]] unsigned const apiVersion)
+
562{
+
563 if (pd.isString())
+
564 {
+
565 return parseObjectID(pd, fieldName);
+
566 }
+
567
+
568 if (!pd.isObject())
+
569 {
+
570 return LedgerEntryHelpers::invalidFieldError("malformedRequest", fieldName, "hex string or object");
+
571 }
+
572
+
573 auto const account = LedgerEntryHelpers::requiredAccountID(pd, jss::account, "malformedAddress");
+
574 if (!account)
+
575 return Unexpected(account.error());
+
576
+
577 auto const seq = LedgerEntryHelpers::requiredUInt32(pd, jss::seq, "malformedRequest");
+
578 if (!seq)
+
579 return Unexpected(seq.error());
+
580
+
581 return keylet::permissionedDomain(*account, pd[jss::seq].asUInt()).key;
+
582}
-
581
-
582static Expected<uint256, Json::Value>
-
- -
584 Json::Value const& jvRippleState,
-
585 Json::StaticString const fieldName,
-
586 [[maybe_unused]] unsigned const apiVersion)
-
587{
-
588 Currency uCurrency;
-
589
-
590 if (!jvRippleState.isObject())
-
591 {
-
592 return parseObjectID(jvRippleState, fieldName);
-
593 }
-
594
-
595 if (auto const value = LedgerEntryHelpers::hasRequired(jvRippleState, {jss::currency, jss::accounts}); !value)
-
596 {
-
597 return Unexpected(value.error());
-
598 }
-
599
-
600 if (!jvRippleState[jss::accounts].isArray() || jvRippleState[jss::accounts].size() != 2)
-
601 {
-
602 return LedgerEntryHelpers::invalidFieldError("malformedRequest", jss::accounts, "length-2 array of Accounts");
-
603 }
-
604
-
605 auto const id1 = LedgerEntryHelpers::parse<AccountID>(jvRippleState[jss::accounts][0u]);
-
606 auto const id2 = LedgerEntryHelpers::parse<AccountID>(jvRippleState[jss::accounts][1u]);
-
607 if (!id1 || !id2)
-
608 {
-
609 return LedgerEntryHelpers::invalidFieldError("malformedAddress", jss::accounts, "array of Accounts");
-
610 }
-
611 if (id1 == id2)
-
612 {
-
613 return LedgerEntryHelpers::malformedError("malformedRequest", "Cannot have a trustline to self.");
-
614 }
-
615
-
616 if (!jvRippleState[jss::currency].isString() || jvRippleState[jss::currency] == "" ||
-
617 !to_currency(uCurrency, jvRippleState[jss::currency].asString()))
-
618 {
-
619 return LedgerEntryHelpers::invalidFieldError("malformedCurrency", jss::currency, "Currency");
-
620 }
-
621
-
622 return keylet::line(*id1, *id2, uCurrency).key;
-
623}
+
583
+
584static Expected<uint256, Json::Value>
+
+ +
586 Json::Value const& jvRippleState,
+
587 Json::StaticString const fieldName,
+
588 [[maybe_unused]] unsigned const apiVersion)
+
589{
+
590 Currency uCurrency;
+
591
+
592 if (!jvRippleState.isObject())
+
593 {
+
594 return parseObjectID(jvRippleState, fieldName);
+
595 }
+
596
+
597 if (auto const value = LedgerEntryHelpers::hasRequired(jvRippleState, {jss::currency, jss::accounts}); !value)
+
598 {
+
599 return Unexpected(value.error());
+
600 }
+
601
+
602 if (!jvRippleState[jss::accounts].isArray() || jvRippleState[jss::accounts].size() != 2)
+
603 {
+
604 return LedgerEntryHelpers::invalidFieldError("malformedRequest", jss::accounts, "length-2 array of Accounts");
+
605 }
+
606
+
607 auto const id1 = LedgerEntryHelpers::parse<AccountID>(jvRippleState[jss::accounts][0u]);
+
608 auto const id2 = LedgerEntryHelpers::parse<AccountID>(jvRippleState[jss::accounts][1u]);
+
609 if (!id1 || !id2)
+
610 {
+
611 return LedgerEntryHelpers::invalidFieldError("malformedAddress", jss::accounts, "array of Accounts");
+
612 }
+
613 if (id1 == id2)
+
614 {
+
615 return LedgerEntryHelpers::malformedError("malformedRequest", "Cannot have a trustline to self.");
+
616 }
+
617
+
618 if (!jvRippleState[jss::currency].isString() || jvRippleState[jss::currency] == "" ||
+
619 !to_currency(uCurrency, jvRippleState[jss::currency].asString()))
+
620 {
+
621 return LedgerEntryHelpers::invalidFieldError("malformedCurrency", jss::currency, "Currency");
+
622 }
+
623
+
624 return keylet::line(*id1, *id2, uCurrency).key;
+
625}
-
624
-
625static Expected<uint256, Json::Value>
-
- -
627 Json::Value const& params,
-
628 Json::StaticString const fieldName,
-
629 [[maybe_unused]] unsigned const apiVersion)
-
630{
-
631 return parseObjectID(params, fieldName, "hex string");
-
632}
+
626
+
627static Expected<uint256, Json::Value>
+
+ +
629 Json::Value const& params,
+
630 Json::StaticString const fieldName,
+
631 [[maybe_unused]] unsigned const apiVersion)
+
632{
+
633 return parseObjectID(params, fieldName, "hex string");
+
634}
-
633
-
634static Expected<uint256, Json::Value>
-
-
635parseTicket(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
-
636{
-
637 if (!params.isObject())
-
638 {
-
639 return parseObjectID(params, fieldName);
-
640 }
-
641
-
642 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
-
643 if (!id)
-
644 return Unexpected(id.error());
-
645
-
646 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::ticket_seq, "malformedRequest");
-
647 if (!seq)
-
648 return Unexpected(seq.error());
-
649
-
650 return getTicketIndex(*id, *seq);
-
651}
+
635
+
636static Expected<uint256, Json::Value>
+
+
637parseTicket(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
+
638{
+
639 if (!params.isObject())
+
640 {
+
641 return parseObjectID(params, fieldName);
+
642 }
+
643
+
644 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress");
+
645 if (!id)
+
646 return Unexpected(id.error());
+
647
+
648 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::ticket_seq, "malformedRequest");
+
649 if (!seq)
+
650 return Unexpected(seq.error());
+
651
+
652 return getTicketIndex(*id, *seq);
+
653}
-
652
-
653static Expected<uint256, Json::Value>
-
-
654parseVault(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
-
655{
-
656 if (!params.isObject())
-
657 {
-
658 return parseObjectID(params, fieldName);
-
659 }
-
660
-
661 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
-
662 if (!id)
-
663 return Unexpected(id.error());
-
664
-
665 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest");
-
666 if (!seq)
-
667 return Unexpected(seq.error());
-
668
-
669 return keylet::vault(*id, *seq).key;
-
670}
+
654
+
655static Expected<uint256, Json::Value>
+
+
656parseVault(Json::Value const& params, Json::StaticString const fieldName, [[maybe_unused]] unsigned const apiVersion)
+
657{
+
658 if (!params.isObject())
+
659 {
+
660 return parseObjectID(params, fieldName);
+
661 }
+
662
+
663 auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner");
+
664 if (!id)
+
665 return Unexpected(id.error());
+
666
+
667 auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest");
+
668 if (!seq)
+
669 return Unexpected(seq.error());
+
670
+
671 return keylet::vault(*id, *seq).key;
+
672}
-
671
-
672static Expected<uint256, Json::Value>
-
- -
674 Json::Value const& claim_id,
-
675 Json::StaticString const fieldName,
-
676 [[maybe_unused]] unsigned const apiVersion)
-
677{
-
678 if (!claim_id.isObject())
-
679 {
-
680 return parseObjectID(claim_id, fieldName);
-
681 }
-
682
-
683 auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id);
-
684 if (!bridge_spec)
-
685 return Unexpected(bridge_spec.error());
-
686
-
687 auto const seq =
-
688 LedgerEntryHelpers::requiredUInt32(claim_id, jss::xchain_owned_claim_id, "malformedXChainOwnedClaimID");
-
689 if (!seq)
-
690 {
-
691 return Unexpected(seq.error());
-
692 }
-
693
-
694 Keylet keylet = keylet::xChainClaimID(*bridge_spec, *seq);
-
695 return keylet.key;
-
696}
+
673
+
674static Expected<uint256, Json::Value>
+
+ +
676 Json::Value const& claim_id,
+
677 Json::StaticString const fieldName,
+
678 [[maybe_unused]] unsigned const apiVersion)
+
679{
+
680 if (!claim_id.isObject())
+
681 {
+
682 return parseObjectID(claim_id, fieldName);
+
683 }
+
684
+
685 auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id);
+
686 if (!bridge_spec)
+
687 return Unexpected(bridge_spec.error());
+
688
+
689 auto const seq =
+
690 LedgerEntryHelpers::requiredUInt32(claim_id, jss::xchain_owned_claim_id, "malformedXChainOwnedClaimID");
+
691 if (!seq)
+
692 {
+
693 return Unexpected(seq.error());
+
694 }
+
695
+
696 Keylet keylet = keylet::xChainClaimID(*bridge_spec, *seq);
+
697 return keylet.key;
+
698}
-
697
-
698static Expected<uint256, Json::Value>
-
- -
700 Json::Value const& claim_id,
-
701 Json::StaticString const fieldName,
-
702 [[maybe_unused]] unsigned const apiVersion)
-
703{
-
704 if (!claim_id.isObject())
-
705 {
-
706 return parseObjectID(claim_id, fieldName);
-
707 }
-
708
-
709 auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id);
-
710 if (!bridge_spec)
-
711 return Unexpected(bridge_spec.error());
-
712
-
713 auto const seq = LedgerEntryHelpers::requiredUInt32(
-
714 claim_id, jss::xchain_owned_create_account_claim_id, "malformedXChainOwnedCreateAccountClaimID");
-
715 if (!seq)
-
716 {
-
717 return Unexpected(seq.error());
-
718 }
-
719
-
720 Keylet keylet = keylet::xChainCreateAccountClaimID(*bridge_spec, *seq);
-
721 return keylet.key;
-
722}
+
699
+
700static Expected<uint256, Json::Value>
+
+ +
702 Json::Value const& claim_id,
+
703 Json::StaticString const fieldName,
+
704 [[maybe_unused]] unsigned const apiVersion)
+
705{
+
706 if (!claim_id.isObject())
+
707 {
+
708 return parseObjectID(claim_id, fieldName);
+
709 }
+
710
+
711 auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id);
+
712 if (!bridge_spec)
+
713 return Unexpected(bridge_spec.error());
+
714
+
715 auto const seq = LedgerEntryHelpers::requiredUInt32(
+
716 claim_id, jss::xchain_owned_create_account_claim_id, "malformedXChainOwnedCreateAccountClaimID");
+
717 if (!seq)
+
718 {
+
719 return Unexpected(seq.error());
+
720 }
+
721
+
722 Keylet keylet = keylet::xChainCreateAccountClaimID(*bridge_spec, *seq);
+
723 return keylet.key;
+
724}
-
723
-
- -
725{
- - - -
729};
+
725
+ -
730
-
731// {
-
732// ledger_hash : <ledger>
-
733// ledger_index : <ledger_index>
-
734// ...
-
735// }
- -
- -
738{
-
739 static auto ledgerEntryParsers = std::to_array<LedgerEntry>({
-
740#pragma push_macro("LEDGER_ENTRY")
-
741#undef LEDGER_ENTRY
-
742
-
743#define LEDGER_ENTRY(tag, value, name, rpcName, fields) {jss::rpcName, parse##name, tag},
+
732
+
733// {
+
734// ledger_hash : <ledger>
+
735// ledger_index : <ledger_index>
+
736// ...
+
737// }
+ +
+ +
740{
+
741 static auto ledgerEntryParsers = std::to_array<LedgerEntry>({
+
742#pragma push_macro("LEDGER_ENTRY")
+
743#undef LEDGER_ENTRY
744
-
745#include <xrpl/protocol/detail/ledger_entries.macro>
+
745#define LEDGER_ENTRY(tag, value, name, rpcName, fields) {jss::rpcName, parse##name, tag},
746
-
747#undef LEDGER_ENTRY
-
748#pragma pop_macro("LEDGER_ENTRY")
-
749 {jss::index, parseIndex, ltANY},
-
750 // aliases
-
751 {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT},
-
752 {jss::ripple_state, parseRippleState, ltRIPPLE_STATE},
-
753 });
-
754
-
755 auto const hasMoreThanOneMember = [&]() {
-
756 int count = 0;
-
757
-
758 for (auto const& ledgerEntry : ledgerEntryParsers)
-
759 {
-
760 if (context.params.isMember(ledgerEntry.fieldName))
-
761 {
-
762 count++;
-
763 if (count > 1) // Early exit if more than one is found
-
764 return true;
-
765 }
-
766 }
-
767 return false; // Return false if <= 1 is found
-
768 }();
-
769
-
770 if (hasMoreThanOneMember)
-
771 {
-
772 return RPC::make_param_error("Too many fields provided.");
-
773 }
-
774
- -
776 auto jvResult = RPC::lookupLedger(lpLedger, context);
-
777
-
778 if (!lpLedger)
-
779 return jvResult;
-
780
-
781 uint256 uNodeIndex;
-
782 LedgerEntryType expectedType = ltANY;
-
783
-
784 try
-
785 {
-
786 bool found = false;
-
787 for (auto const& ledgerEntry : ledgerEntryParsers)
-
788 {
-
789 if (context.params.isMember(ledgerEntry.fieldName))
-
790 {
-
791 expectedType = ledgerEntry.expectedType;
-
792 // `Bridge` is the only type that involves two fields at the
-
793 // `ledger_entry` param level.
-
794 // So that parser needs to have the whole `params` field.
-
795 // All other parsers only need the one field name's info.
-
796 Json::Value const& params =
-
797 ledgerEntry.fieldName == jss::bridge ? context.params : context.params[ledgerEntry.fieldName];
-
798 auto const result = ledgerEntry.parseFunction(params, ledgerEntry.fieldName, context.apiVersion);
-
799 if (!result)
-
800 return result.error();
-
801
-
802 uNodeIndex = result.value();
-
803 found = true;
-
804 break;
-
805 }
-
806 }
-
807 if (!found)
-
808 {
-
809 if (context.apiVersion < 2u)
-
810 {
-
811 jvResult[jss::error] = "unknownOption";
-
812 return jvResult;
-
813 }
-
814 return RPC::make_param_error("No ledger_entry params provided.");
-
815 }
-
816 }
-
817 catch (Json::error& e)
-
818 {
-
819 if (context.apiVersion > 1u)
-
820 {
-
821 // For apiVersion 2 onwards, any parsing failures that throw
-
822 // this exception return an invalidParam error.
- -
824 }
-
825 else
-
826 throw;
-
827 }
-
828
-
829 // Return the computed index regardless of whether the node exists.
-
830 jvResult[jss::index] = to_string(uNodeIndex);
-
831
-
832 if (uNodeIndex.isZero())
-
833 {
- -
835 return jvResult;
-
836 }
-
837
-
838 auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
+
747#include <xrpl/protocol/detail/ledger_entries.macro>
+
748
+
749#undef LEDGER_ENTRY
+
750#pragma pop_macro("LEDGER_ENTRY")
+
751 {jss::index, parseIndex, ltANY},
+
752 // aliases
+
753 {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT},
+
754 {jss::ripple_state, parseRippleState, ltRIPPLE_STATE},
+
755 });
+
756
+
757 auto const hasMoreThanOneMember = [&]() {
+
758 int count = 0;
+
759
+
760 for (auto const& ledgerEntry : ledgerEntryParsers)
+
761 {
+
762 if (context.params.isMember(ledgerEntry.fieldName))
+
763 {
+
764 count++;
+
765 if (count > 1) // Early exit if more than one is found
+
766 return true;
+
767 }
+
768 }
+
769 return false; // Return false if <= 1 is found
+
770 }();
+
771
+
772 if (hasMoreThanOneMember)
+
773 {
+
774 return RPC::make_param_error("Too many fields provided.");
+
775 }
+
776
+ +
778 auto jvResult = RPC::lookupLedger(lpLedger, context);
+
779
+
780 if (!lpLedger)
+
781 return jvResult;
+
782
+
783 uint256 uNodeIndex;
+
784 LedgerEntryType expectedType = ltANY;
+
785
+
786 try
+
787 {
+
788 bool found = false;
+
789 for (auto const& ledgerEntry : ledgerEntryParsers)
+
790 {
+
791 if (context.params.isMember(ledgerEntry.fieldName))
+
792 {
+
793 expectedType = ledgerEntry.expectedType;
+
794 // `Bridge` is the only type that involves two fields at the
+
795 // `ledger_entry` param level.
+
796 // So that parser needs to have the whole `params` field.
+
797 // All other parsers only need the one field name's info.
+
798 Json::Value const& params =
+
799 ledgerEntry.fieldName == jss::bridge ? context.params : context.params[ledgerEntry.fieldName];
+
800 auto const result = ledgerEntry.parseFunction(params, ledgerEntry.fieldName, context.apiVersion);
+
801 if (!result)
+
802 return result.error();
+
803
+
804 uNodeIndex = result.value();
+
805 found = true;
+
806 break;
+
807 }
+
808 }
+
809 if (!found)
+
810 {
+
811 if (context.apiVersion < 2u)
+
812 {
+
813 jvResult[jss::error] = "unknownOption";
+
814 return jvResult;
+
815 }
+
816 return RPC::make_param_error("No ledger_entry params provided.");
+
817 }
+
818 }
+
819 catch (Json::error& e)
+
820 {
+
821 if (context.apiVersion > 1u)
+
822 {
+
823 // For apiVersion 2 onwards, any parsing failures that throw
+
824 // this exception return an invalidParam error.
+ +
826 }
+
827 else
+
828 throw;
+
829 }
+
830
+
831 // Return the computed index regardless of whether the node exists.
+
832 jvResult[jss::index] = to_string(uNodeIndex);
+
833
+
834 if (uNodeIndex.isZero())
+
835 {
+ +
837 return jvResult;
+
838 }
839
-
840 bool bNodeBinary = false;
-
841 if (context.params.isMember(jss::binary))
-
842 bNodeBinary = context.params[jss::binary].asBool();
-
843
-
844 if (!sleNode)
-
845 {
-
846 // Not found.
- -
848 return jvResult;
-
849 }
-
850
-
851 if ((expectedType != ltANY) && (expectedType != sleNode->getType()))
-
852 {
- -
854 return jvResult;
-
855 }
-
856
-
857 if (bNodeBinary)
-
858 {
-
859 Serializer s;
-
860
-
861 sleNode->add(s);
+
840 auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
+
841
+
842 bool bNodeBinary = false;
+
843 if (context.params.isMember(jss::binary))
+
844 bNodeBinary = context.params[jss::binary].asBool();
+
845
+
846 if (!sleNode)
+
847 {
+
848 // Not found.
+ +
850 return jvResult;
+
851 }
+
852
+
853 if ((expectedType != ltANY) && (expectedType != sleNode->getType()))
+
854 {
+ +
856 return jvResult;
+
857 }
+
858
+
859 if (bNodeBinary)
+
860 {
+
861 Serializer s;
862
-
863 jvResult[jss::node_binary] = strHex(s.peekData());
-
864 }
-
865 else
-
866 {
-
867 jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
-
868 }
-
869
-
870 return jvResult;
-
871}
+
863 sleNode->add(s);
+
864
+
865 jvResult[jss::node_binary] = strHex(s.peekData());
+
866 }
+
867 else
+
868 {
+
869 jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
+
870 }
+
871
+
872 return jvResult;
+
873}
-
872
- -
- -
875{
-
876 org::xrpl::rpc::v1::GetLedgerEntryRequest& request = context.params;
-
877 org::xrpl::rpc::v1::GetLedgerEntryResponse response;
-
878 grpc::Status status = grpc::Status::OK;
-
879
- -
881 if (auto status = RPC::ledgerFromRequest(ledger, context))
-
882 {
-
883 grpc::Status errorStatus;
-
884 if (status.toErrorCode() == rpcINVALID_PARAMS)
-
885 {
-
886 errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
-
887 }
-
888 else
-
889 {
-
890 errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
-
891 }
-
892 return {response, errorStatus};
-
893 }
-
894
-
895 auto const key = uint256::fromVoidChecked(request.key());
-
896 if (!key)
-
897 {
-
898 grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
-
899 return {response, errorStatus};
-
900 }
-
901
-
902 auto const sleNode = ledger->read(keylet::unchecked(*key));
-
903 if (!sleNode)
-
904 {
-
905 grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "object not found"};
-
906 return {response, errorStatus};
-
907 }
-
908
-
909 Serializer s;
-
910 sleNode->add(s);
-
911
-
912 auto& stateObject = *response.mutable_ledger_object();
-
913 stateObject.set_data(s.peekData().data(), s.getLength());
-
914 stateObject.set_key(request.key());
-
915 *(response.mutable_ledger()) = request.ledger();
-
916 return {response, status};
-
917}
+
874
+ +
+ +
877{
+
878 org::xrpl::rpc::v1::GetLedgerEntryRequest& request = context.params;
+
879 org::xrpl::rpc::v1::GetLedgerEntryResponse response;
+
880 grpc::Status status = grpc::Status::OK;
+
881
+ +
883 if (auto status = RPC::ledgerFromRequest(ledger, context))
+
884 {
+
885 grpc::Status errorStatus;
+
886 if (status.toErrorCode() == rpcINVALID_PARAMS)
+
887 {
+
888 errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
+
889 }
+
890 else
+
891 {
+
892 errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
+
893 }
+
894 return {response, errorStatus};
+
895 }
+
896
+
897 auto const key = uint256::fromVoidChecked(request.key());
+
898 if (!key)
+
899 {
+
900 grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
+
901 return {response, errorStatus};
+
902 }
+
903
+
904 auto const sleNode = ledger->read(keylet::unchecked(*key));
+
905 if (!sleNode)
+
906 {
+
907 grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "object not found"};
+
908 return {response, errorStatus};
+
909 }
+
910
+
911 Serializer s;
+
912 sleNode->add(s);
+
913
+
914 auto& stateObject = *response.mutable_ledger_object();
+
915 stateObject.set_data(s.peekData().data(), s.getLength());
+
916 stateObject.set_key(request.key());
+
917 *(response.mutable_ledger()) = request.ledger();
+
918 return {response, status};
+
919}
-
918} // namespace xrpl
+
920} // namespace xrpl
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
+
bool isArray() const
UInt size() const
Number of values in array or object.
-
bool isString() const
+
bool isString() const
UInt asUInt() const
-
bool isObject() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
-
bool isBool() const
+
bool isBool() const
bool asBool() const
-
bool isUInt() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
bool isUInt() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
bool isConvertibleTo(ValueType other) const
-
bool isInt() const
+
bool isInt() const
void push_back(STObject const &object)
Definition STArray.h:187
@@ -1148,65 +1150,65 @@ $(document).ready(function() { init_codefold(0); });
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition Indexes.cpp:486
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:422
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
-
static Expected< uint256, Json::Value > parseDirectoryNode(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseMPToken(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parsePermissionedDomain(Json::Value const &pd, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseDirectoryNode(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseMPToken(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parsePermissionedDomain(Json::Value const &pd, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseAMM(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
auto const parseFeeSettings
-
static Expected< uint256, Json::Value > parseSignerList(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
auto const parseFeeSettings
+
static Expected< uint256, Json::Value > parseSignerList(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
-
static Expected< uint256, Json::Value > parseOracle(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseOracle(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseCheck(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition Indexes.cpp:133
static Expected< uint256, Json::Value > parseIndex(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseDID(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseLoan(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseXChainOwnedClaimID(Json::Value const &claim_id, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseDID(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseLoan(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseXChainOwnedClaimID(Json::Value const &claim_id, Json::StaticString const fieldName, unsigned const apiVersion)
static bool authorized(Port const &port, std::map< std::string, std::string > const &h)
static FunctionType fixed(Keylet const &keylet)
static Expected< uint256, Json::Value > parseDelegate(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
auto const parseAmendments
-
static Expected< uint256, Json::Value > parseFixed(Keylet const &keylet, Json::Value const &params, Json::StaticString const &fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseLedgerHashes(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseEscrow(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseTicket(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseFixed(Keylet const &keylet, Json::Value const &params, Json::StaticString const &fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseLedgerHashes(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseEscrow(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseTicket(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseCredential(Json::Value const &cred, Json::StaticString const fieldName, unsigned const apiVersion)
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition Protocol.h:223
-
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
-
auto const parseNegativeUNL
-
static Expected< uint256, Json::Value > parsePayChannel(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
+
auto const parseNegativeUNL
+
static Expected< uint256, Json::Value > parsePayChannel(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseObjectID(Json::Value const &params, Json::StaticString const fieldName, std::string const &expectedType="hex string or object")
-
static Expected< uint256, Json::Value > parseRippleState(Json::Value const &jvRippleState, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseNFTokenPage(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseRippleState(Json::Value const &jvRippleState, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseNFTokenPage(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< STArray, Json::Value > parseAuthorizeCredentials(Json::Value const &jv)
-
static Expected< uint256, Json::Value > parseOffer(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseMPTokenIssuance(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseOffer(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseMPTokenIssuance(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseBridge(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseDepositPreauth(Json::Value const &dp, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseVault(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseDepositPreauth(Json::Value const &dp, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseVault(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
LedgerEntryType
Identifiers for on-ledger objects.
@ ltANY
A special type, matching any ledger entry type.
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:62
-
static Expected< uint256, Json::Value > parseNFTokenOffer(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
-
static Expected< uint256, Json::Value > parseLoanBroker(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseNFTokenOffer(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseLoanBroker(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
static Expected< uint256, Json::Value > parseAccountRoot(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
@ rpcUNEXPECTED_LEDGER_TYPE
Definition ErrorCodes.h:142
@ rpcENTRY_NOT_FOUND
Definition ErrorCodes.h:141
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:64
-
static Expected< uint256, Json::Value > parseXChainOwnedCreateAccountClaimID(Json::Value const &claim_id, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseXChainOwnedCreateAccountClaimID(Json::Value const &claim_id, Json::StaticString const fieldName, unsigned const apiVersion)
std::size_t constexpr maxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
Definition Protocol.h:220
-
Json::Value doLedgerEntry(RPC::JsonContext &)
+
Json::Value doLedgerEntry(RPC::JsonContext &)
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
uint256 key
Definition Keylet.h:20
- -
Json::StaticString fieldName
-
FunctionType parseFunction
-
LedgerEntryType expectedType
+ +
Json::StaticString fieldName
+
FunctionType parseFunction
+
LedgerEntryType expectedType
unsigned int apiVersion
Definition Context.h:29
RequestType params
Definition Context.h:51
diff --git a/LedgerEntry__test_8cpp_source.html b/LedgerEntry__test_8cpp_source.html index fa88b7f1bf..dac4fc6652 100644 --- a/LedgerEntry__test_8cpp_source.html +++ b/LedgerEntry__test_8cpp_source.html @@ -694,2215 +694,2222 @@ $(document).ready(function() { init_codefold(0); });
581 // Create Amendments vector (enabled amendments)
582 std::vector<uint256> enabledAmendments;
583 enabledAmendments.push_back(
-
584 uint256::fromVoid("42426C4D4F1009EE67080A9B7965B44656D7"
-
585 "714D104A72F9B4369F97ABF044EE"));
-
586 enabledAmendments.push_back(
-
587 uint256::fromVoid("4C97EBA926031A7CF7D7B36FDE3ED66DDA54"
-
588 "21192D63DE53FFB46E43B9DC8373"));
-
589 enabledAmendments.push_back(
-
590 uint256::fromVoid("03BDC0099C4E14163ADA272C1B6F6FABB448"
-
591 "CC3E51F522F978041E4B57D9158C"));
-
592 enabledAmendments.push_back(
-
593 uint256::fromVoid("35291ADD2D79EB6991343BDA0912269C817D"
-
594 "0F094B02226C1C14AD2858962ED4"));
-
595 sle->setFieldV256(sfAmendments, STVector256(enabledAmendments));
-
596
-
597 // Create Majorities array
-
598 STArray majorities;
-
599
-
600 auto majority1 = STObject::makeInnerObject(sfMajority);
-
601 majority1.setFieldH256(
-
602 sfAmendment,
-
603 uint256::fromVoid("7BB62DC13EC72B775091E9C71BF8CF97E122"
-
604 "647693B50C5E87A80DFD6FCFAC50"));
-
605 majority1.setFieldU32(sfCloseTime, 779561310);
-
606 majorities.push_back(std::move(majority1));
-
607
-
608 auto majority2 = STObject::makeInnerObject(sfMajority);
-
609 majority2.setFieldH256(
-
610 sfAmendment,
-
611 uint256::fromVoid("755C971C29971C9F20C6F080F2ED96F87884"
-
612 "E40AD19554A5EBECDCEC8A1F77FE"));
-
613 majority2.setFieldU32(sfCloseTime, 779561310);
-
614 majorities.push_back(std::move(majority2));
-
615
-
616 sle->setFieldArray(sfMajorities, majorities);
-
617
-
618 view.rawInsert(sle);
-
619 return true;
-
620 };
-
621 env.app().openLedger().modify(amendments);
-
622 }
+ +
585 "42426C4D4F1009EE67080A9B7965B44656D7"
+
586 "714D104A72F9B4369F97ABF044EE"));
+
587 enabledAmendments.push_back(
+ +
589 "4C97EBA926031A7CF7D7B36FDE3ED66DDA54"
+
590 "21192D63DE53FFB46E43B9DC8373"));
+
591 enabledAmendments.push_back(
+ +
593 "03BDC0099C4E14163ADA272C1B6F6FABB448"
+
594 "CC3E51F522F978041E4B57D9158C"));
+
595 enabledAmendments.push_back(
+ +
597 "35291ADD2D79EB6991343BDA0912269C817D"
+
598 "0F094B02226C1C14AD2858962ED4"));
+
599 sle->setFieldV256(sfAmendments, STVector256(enabledAmendments));
+
600
+
601 // Create Majorities array
+
602 STArray majorities;
+
603
+
604 auto majority1 = STObject::makeInnerObject(sfMajority);
+
605 majority1.setFieldH256(
+
606 sfAmendment,
+ +
608 "7BB62DC13EC72B775091E9C71BF8CF97E122"
+
609 "647693B50C5E87A80DFD6FCFAC50"));
+
610 majority1.setFieldU32(sfCloseTime, 779561310);
+
611 majorities.push_back(std::move(majority1));
+
612
+
613 auto majority2 = STObject::makeInnerObject(sfMajority);
+
614 majority2.setFieldH256(
+
615 sfAmendment,
+ +
617 "755C971C29971C9F20C6F080F2ED96F87884"
+
618 "E40AD19554A5EBECDCEC8A1F77FE"));
+
619 majority2.setFieldU32(sfCloseTime, 779561310);
+
620 majorities.push_back(std::move(majority2));
+
621
+
622 sle->setFieldArray(sfMajorities, majorities);
623
-
624 Json::Value jvParams;
-
625 jvParams[jss::amendments] = to_string(keylet.key);
-
626 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
627 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Amendments);
-
628 }
+
624 view.rawInsert(sle);
+
625 return true;
+
626 };
+
627 env.app().openLedger().modify(amendments);
+
628 }
629
-
630 // negative tests
-
631 testMalformedField(env, Json::Value{}, jss::amendments, FieldType::FixedHashField, "malformedRequest");
-
632 }
+
630 Json::Value jvParams;
+
631 jvParams[jss::amendments] = to_string(keylet.key);
+
632 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
633 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Amendments);
+
634 }
+
635
+
636 // negative tests
+
637 testMalformedField(env, Json::Value{}, jss::amendments, FieldType::FixedHashField, "malformedRequest");
+
638 }
-
633
-
634 void
-
- -
636 {
-
637 testcase("AMM");
-
638 using namespace test::jtx;
-
639 Env env{*this};
-
640
-
641 // positive test
-
642 Account const alice{"alice"};
-
643 env.fund(XRP(10000), alice);
-
644 env.close();
-
645 AMM amm(env, alice, XRP(10), alice["USD"](1000));
-
646 env.close();
-
647
-
648 {
-
649 Json::Value jvParams;
-
650 jvParams[jss::amm] = to_string(amm.ammID());
-
651 auto const result = env.rpc("json", "ledger_entry", to_string(jvParams));
-
652 BEAST_EXPECT(
-
653 result.isObject() && result.isMember(jss::result) && !result[jss::result].isMember(jss::error) &&
-
654 result[jss::result].isMember(jss::node) &&
-
655 result[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
-
656 result[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::AMM);
-
657 }
-
658
-
659 {
-
660 Json::Value jvParams;
- -
662 {
- -
664 obj[jss::currency] = "XRP";
-
665 ammParams[jss::asset] = obj;
-
666 }
-
667 {
- -
669 obj[jss::currency] = "USD";
-
670 obj[jss::issuer] = alice.human();
-
671 ammParams[jss::asset2] = obj;
+
639
+
640 void
+
+ +
642 {
+
643 testcase("AMM");
+
644 using namespace test::jtx;
+
645 Env env{*this};
+
646
+
647 // positive test
+
648 Account const alice{"alice"};
+
649 env.fund(XRP(10000), alice);
+
650 env.close();
+
651 AMM amm(env, alice, XRP(10), alice["USD"](1000));
+
652 env.close();
+
653
+
654 {
+
655 Json::Value jvParams;
+
656 jvParams[jss::amm] = to_string(amm.ammID());
+
657 auto const result = env.rpc("json", "ledger_entry", to_string(jvParams));
+
658 BEAST_EXPECT(
+
659 result.isObject() && result.isMember(jss::result) && !result[jss::result].isMember(jss::error) &&
+
660 result[jss::result].isMember(jss::node) &&
+
661 result[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
+
662 result[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::AMM);
+
663 }
+
664
+
665 {
+
666 Json::Value jvParams;
+ +
668 {
+ +
670 obj[jss::currency] = "XRP";
+
671 ammParams[jss::asset] = obj;
672 }
-
673 jvParams[jss::amm] = ammParams;
-
674 auto const result = env.rpc("json", "ledger_entry", to_string(jvParams));
-
675 BEAST_EXPECT(
-
676 result.isObject() && result.isMember(jss::result) && !result[jss::result].isMember(jss::error) &&
-
677 result[jss::result].isMember(jss::node) &&
-
678 result[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
-
679 result[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::AMM);
-
680 }
-
681
-
682 // negative tests
- -
684 env,
-
685 jss::amm,
-
686 {
-
687 {jss::asset, "malformedRequest"},
-
688 {jss::asset2, "malformedRequest"},
-
689 });
-
690 }
+
673 {
+ +
675 obj[jss::currency] = "USD";
+
676 obj[jss::issuer] = alice.human();
+
677 ammParams[jss::asset2] = obj;
+
678 }
+
679 jvParams[jss::amm] = ammParams;
+
680 auto const result = env.rpc("json", "ledger_entry", to_string(jvParams));
+
681 BEAST_EXPECT(
+
682 result.isObject() && result.isMember(jss::result) && !result[jss::result].isMember(jss::error) &&
+
683 result[jss::result].isMember(jss::node) &&
+
684 result[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
+
685 result[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::AMM);
+
686 }
+
687
+
688 // negative tests
+ +
690 env,
+
691 jss::amm,
+
692 {
+
693 {jss::asset, "malformedRequest"},
+
694 {jss::asset2, "malformedRequest"},
+
695 });
+
696 }
-
691
-
692 void
-
- -
694 {
-
695 testcase("Check");
-
696 using namespace test::jtx;
-
697 Env env{*this};
-
698 Account const alice{"alice"};
-
699 env.fund(XRP(10000), alice);
-
700 env.close();
-
701
-
702 auto const checkId = keylet::check(env.master, env.seq(env.master));
-
703
-
704 env(check::create(env.master, alice, XRP(100)));
-
705 env.close();
-
706
-
707 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
708 {
-
709 // Request a check.
-
710 Json::Value jvParams;
-
711 jvParams[jss::check] = to_string(checkId.key);
-
712 jvParams[jss::ledger_hash] = ledgerHash;
-
713 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
714 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
-
715 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
-
716 }
-
717 {
-
718 // Request an index that is not a check. We'll use alice's
-
719 // account root index.
-
720 std::string accountRootIndex;
-
721 {
-
722 Json::Value jvParams;
-
723 jvParams[jss::account_root] = alice.human();
-
724 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
725 accountRootIndex = jrr[jss::index].asString();
-
726 }
-
727 Json::Value jvParams;
-
728 jvParams[jss::check] = accountRootIndex;
-
729 jvParams[jss::ledger_hash] = ledgerHash;
-
730 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
731 checkErrorValue(jrr, "unexpectedLedgerType", "Unexpected ledger type.");
-
732 }
-
733 {
-
734 // Check malformed cases
-
735 runLedgerEntryTest(env, jss::check);
-
736 }
-
737 }
+
697
+
698 void
+
+ +
700 {
+
701 testcase("Check");
+
702 using namespace test::jtx;
+
703 Env env{*this};
+
704 Account const alice{"alice"};
+
705 env.fund(XRP(10000), alice);
+
706 env.close();
+
707
+
708 auto const checkId = keylet::check(env.master, env.seq(env.master));
+
709
+
710 env(check::create(env.master, alice, XRP(100)));
+
711 env.close();
+
712
+
713 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
714 {
+
715 // Request a check.
+
716 Json::Value jvParams;
+
717 jvParams[jss::check] = to_string(checkId.key);
+
718 jvParams[jss::ledger_hash] = ledgerHash;
+
719 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
720 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
+
721 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
+
722 }
+
723 {
+
724 // Request an index that is not a check. We'll use alice's
+
725 // account root index.
+
726 std::string accountRootIndex;
+
727 {
+
728 Json::Value jvParams;
+
729 jvParams[jss::account_root] = alice.human();
+
730 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
731 accountRootIndex = jrr[jss::index].asString();
+
732 }
+
733 Json::Value jvParams;
+
734 jvParams[jss::check] = accountRootIndex;
+
735 jvParams[jss::ledger_hash] = ledgerHash;
+
736 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
737 checkErrorValue(jrr, "unexpectedLedgerType", "Unexpected ledger type.");
+
738 }
+
739 {
+
740 // Check malformed cases
+
741 runLedgerEntryTest(env, jss::check);
+
742 }
+
743 }
-
738
-
739 void
-
- -
741 {
-
742 testcase("Credentials");
-
743
-
744 using namespace test::jtx;
-
745
-
746 Env env(*this);
-
747 Account const issuer{"issuer"};
-
748 Account const alice{"alice"};
-
749 Account const bob{"bob"};
-
750 char const credType[] = "abcde";
+
744
+
745 void
+
+ +
747 {
+
748 testcase("Credentials");
+
749
+
750 using namespace test::jtx;
751
-
752 env.fund(XRP(5000), issuer, alice, bob);
-
753 env.close();
-
754
-
755 // Setup credentials with DepositAuth object for Alice and Bob
-
756 env(credentials::create(alice, issuer, credType));
-
757 env.close();
-
758
-
759 {
-
760 // Succeed
-
761 auto jv = credentials::ledgerEntry(env, alice, issuer, credType);
-
762 BEAST_EXPECT(
-
763 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
-
764 jv[jss::result].isMember(jss::node) &&
-
765 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
-
766 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::Credential);
-
767
-
768 std::string const credIdx = jv[jss::result][jss::index].asString();
-
769
-
770 jv = credentials::ledgerEntry(env, credIdx);
-
771 BEAST_EXPECT(
-
772 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
-
773 jv[jss::result].isMember(jss::node) &&
-
774 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
-
775 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::Credential);
-
776 }
-
777
-
778 {
-
779 // Fail, credential doesn't exist
-
780 auto const jv = credentials::ledgerEntry(
-
781 env,
-
782 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
-
783 "E4");
-
784 checkErrorValue(jv[jss::result], "entryNotFound", "Entry not found.");
-
785 }
-
786
-
787 {
-
788 // Check all malformed cases
- -
790 env,
-
791 jss::credential,
-
792 {
-
793 {jss::subject, "malformedRequest"},
-
794 {jss::issuer, "malformedRequest"},
-
795 {jss::credential_type, "malformedRequest"},
-
796 });
-
797 }
-
798 }
+
752 Env env(*this);
+
753 Account const issuer{"issuer"};
+
754 Account const alice{"alice"};
+
755 Account const bob{"bob"};
+
756 char const credType[] = "abcde";
+
757
+
758 env.fund(XRP(5000), issuer, alice, bob);
+
759 env.close();
+
760
+
761 // Setup credentials with DepositAuth object for Alice and Bob
+
762 env(credentials::create(alice, issuer, credType));
+
763 env.close();
+
764
+
765 {
+
766 // Succeed
+
767 auto jv = credentials::ledgerEntry(env, alice, issuer, credType);
+
768 BEAST_EXPECT(
+
769 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
+
770 jv[jss::result].isMember(jss::node) &&
+
771 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
+
772 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::Credential);
+
773
+
774 std::string const credIdx = jv[jss::result][jss::index].asString();
+
775
+
776 jv = credentials::ledgerEntry(env, credIdx);
+
777 BEAST_EXPECT(
+
778 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
+
779 jv[jss::result].isMember(jss::node) &&
+
780 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
+
781 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::Credential);
+
782 }
+
783
+
784 {
+
785 // Fail, credential doesn't exist
+
786 auto const jv = credentials::ledgerEntry(
+
787 env,
+
788 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
+
789 "E4");
+
790 checkErrorValue(jv[jss::result], "entryNotFound", "Entry not found.");
+
791 }
+
792
+
793 {
+
794 // Check all malformed cases
+ +
796 env,
+
797 jss::credential,
+
798 {
+
799 {jss::subject, "malformedRequest"},
+
800 {jss::issuer, "malformedRequest"},
+
801 {jss::credential_type, "malformedRequest"},
+
802 });
+
803 }
+
804 }
-
799
-
800 void
-
- -
802 {
-
803 testcase("Delegate");
-
804
-
805 using namespace test::jtx;
-
806
-
807 Env env{*this};
-
808 Account const alice{"alice"};
-
809 Account const bob{"bob"};
-
810 env.fund(XRP(10000), alice, bob);
-
811 env.close();
-
812 env(delegate::set(alice, bob, {"Payment", "CheckCreate"}));
-
813 env.close();
-
814 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
815 std::string delegateIndex;
-
816 {
-
817 // Request by account and authorize
-
818 Json::Value jvParams;
-
819 jvParams[jss::delegate][jss::account] = alice.human();
-
820 jvParams[jss::delegate][jss::authorize] = bob.human();
-
821 jvParams[jss::ledger_hash] = ledgerHash;
-
822 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
823 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate);
-
824 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
-
825 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human());
-
826 delegateIndex = jrr[jss::node][jss::index].asString();
-
827 }
-
828 {
-
829 // Request by index.
-
830 Json::Value jvParams;
-
831 jvParams[jss::delegate] = delegateIndex;
-
832 jvParams[jss::ledger_hash] = ledgerHash;
-
833 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
834 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate);
-
835 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
-
836 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human());
-
837 }
-
838
-
839 {
-
840 // Check all malformed cases
- -
842 env,
-
843 jss::delegate,
-
844 {
-
845 {jss::account, "malformedAddress"},
-
846 {jss::authorize, "malformedAddress"},
-
847 });
-
848 }
-
849 }
+
805
+
806 void
+
+ +
808 {
+
809 testcase("Delegate");
+
810
+
811 using namespace test::jtx;
+
812
+
813 Env env{*this};
+
814 Account const alice{"alice"};
+
815 Account const bob{"bob"};
+
816 env.fund(XRP(10000), alice, bob);
+
817 env.close();
+
818 env(delegate::set(alice, bob, {"Payment", "CheckCreate"}));
+
819 env.close();
+
820 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
821 std::string delegateIndex;
+
822 {
+
823 // Request by account and authorize
+
824 Json::Value jvParams;
+
825 jvParams[jss::delegate][jss::account] = alice.human();
+
826 jvParams[jss::delegate][jss::authorize] = bob.human();
+
827 jvParams[jss::ledger_hash] = ledgerHash;
+
828 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
829 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate);
+
830 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
+
831 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human());
+
832 delegateIndex = jrr[jss::node][jss::index].asString();
+
833 }
+
834 {
+
835 // Request by index.
+
836 Json::Value jvParams;
+
837 jvParams[jss::delegate] = delegateIndex;
+
838 jvParams[jss::ledger_hash] = ledgerHash;
+
839 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
840 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate);
+
841 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
+
842 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human());
+
843 }
+
844
+
845 {
+
846 // Check all malformed cases
+ +
848 env,
+
849 jss::delegate,
+
850 {
+
851 {jss::account, "malformedAddress"},
+
852 {jss::authorize, "malformedAddress"},
+
853 });
+
854 }
+
855 }
-
850
-
851 void
-
- -
853 {
-
854 testcase("Deposit Preauth");
-
855
-
856 using namespace test::jtx;
-
857
-
858 Env env{*this};
-
859 Account const alice{"alice"};
-
860 Account const becky{"becky"};
+
856
+
857 void
+
+ +
859 {
+
860 testcase("Deposit Preauth");
861
-
862 env.fund(XRP(10000), alice, becky);
-
863 env.close();
-
864
-
865 env(deposit::auth(alice, becky));
-
866 env.close();
+
862 using namespace test::jtx;
+
863
+
864 Env env{*this};
+
865 Account const alice{"alice"};
+
866 Account const becky{"becky"};
867
-
868 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
869 std::string depositPreauthIndex;
-
870 {
-
871 // Request a depositPreauth by owner and authorized.
-
872 Json::Value jvParams;
-
873 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
-
874 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
-
875 jvParams[jss::ledger_hash] = ledgerHash;
-
876 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
877
-
878 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::DepositPreauth);
-
879 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
-
880 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
-
881 depositPreauthIndex = jrr[jss::node][jss::index].asString();
-
882 }
-
883 {
-
884 // Request a depositPreauth by index.
-
885 Json::Value jvParams;
-
886 jvParams[jss::deposit_preauth] = depositPreauthIndex;
-
887 jvParams[jss::ledger_hash] = ledgerHash;
-
888 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
889
-
890 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::DepositPreauth);
-
891 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
-
892 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
-
893 }
-
894 {
-
895 // test all missing/malformed field cases
- -
897 env,
-
898 jss::deposit_preauth,
-
899 {
-
900 {jss::owner, "malformedOwner"},
-
901 {jss::authorized, "malformedAuthorized", false},
-
902 });
-
903 }
-
904 }
+
868 env.fund(XRP(10000), alice, becky);
+
869 env.close();
+
870
+
871 env(deposit::auth(alice, becky));
+
872 env.close();
+
873
+
874 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
875 std::string depositPreauthIndex;
+
876 {
+
877 // Request a depositPreauth by owner and authorized.
+
878 Json::Value jvParams;
+
879 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
+
880 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
+
881 jvParams[jss::ledger_hash] = ledgerHash;
+
882 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
883
+
884 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::DepositPreauth);
+
885 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
+
886 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
+
887 depositPreauthIndex = jrr[jss::node][jss::index].asString();
+
888 }
+
889 {
+
890 // Request a depositPreauth by index.
+
891 Json::Value jvParams;
+
892 jvParams[jss::deposit_preauth] = depositPreauthIndex;
+
893 jvParams[jss::ledger_hash] = ledgerHash;
+
894 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
895
+
896 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::DepositPreauth);
+
897 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
+
898 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
+
899 }
+
900 {
+
901 // test all missing/malformed field cases
+ +
903 env,
+
904 jss::deposit_preauth,
+
905 {
+
906 {jss::owner, "malformedOwner"},
+
907 {jss::authorized, "malformedAuthorized", false},
+
908 });
+
909 }
+
910 }
-
905
-
906 void
-
- -
908 {
-
909 testcase("Deposit Preauth with credentials");
-
910
-
911 using namespace test::jtx;
-
912
-
913 Env env(*this);
-
914 Account const issuer{"issuer"};
-
915 Account const alice{"alice"};
-
916 Account const bob{"bob"};
-
917 char const credType[] = "abcde";
+
911
+
912 void
+
+ +
914 {
+
915 testcase("Deposit Preauth with credentials");
+
916
+
917 using namespace test::jtx;
918
-
919 env.fund(XRP(5000), issuer, alice, bob);
-
920 env.close();
-
921
-
922 {
-
923 // Setup Bob with DepositAuth
-
924 env(fset(bob, asfDepositAuth));
-
925 env.close();
-
926 env(deposit::authCredentials(bob, {{issuer, credType}}));
-
927 env.close();
-
928 }
-
929
-
930 {
-
931 // Succeed
-
932 Json::Value jvParams;
-
933 jvParams[jss::ledger_index] = jss::validated;
-
934 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
919 Env env(*this);
+
920 Account const issuer{"issuer"};
+
921 Account const alice{"alice"};
+
922 Account const bob{"bob"};
+
923 char const credType[] = "abcde";
+
924
+
925 env.fund(XRP(5000), issuer, alice, bob);
+
926 env.close();
+
927
+
928 {
+
929 // Setup Bob with DepositAuth
+
930 env(fset(bob, asfDepositAuth));
+
931 env.close();
+
932 env(deposit::authCredentials(bob, {{issuer, credType}}));
+
933 env.close();
+
934 }
935
-
936 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
-
937 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
-
938
-
939 Json::Value jo;
-
940 jo[jss::issuer] = issuer.human();
-
941 jo[jss::credential_type] = strHex(std::string_view(credType));
-
942 arr.append(std::move(jo));
-
943 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
+
936 {
+
937 // Succeed
+
938 Json::Value jvParams;
+
939 jvParams[jss::ledger_index] = jss::validated;
+
940 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
941
+
942 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
+
943 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
944
-
945 BEAST_EXPECT(
-
946 jrr.isObject() && jrr.isMember(jss::result) && !jrr[jss::result].isMember(jss::error) &&
-
947 jrr[jss::result].isMember(jss::node) &&
-
948 jrr[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
-
949 jrr[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::DepositPreauth);
-
950 }
-
951
-
952 {
-
953 // Failed, invalid account
-
954 Json::Value jvParams;
-
955 jvParams[jss::ledger_index] = jss::validated;
-
956 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
945 Json::Value jo;
+
946 jo[jss::issuer] = issuer.human();
+
947 jo[jss::credential_type] = strHex(std::string_view(credType));
+
948 arr.append(std::move(jo));
+
949 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
+
950
+
951 BEAST_EXPECT(
+
952 jrr.isObject() && jrr.isMember(jss::result) && !jrr[jss::result].isMember(jss::error) &&
+
953 jrr[jss::result].isMember(jss::node) &&
+
954 jrr[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
+
955 jrr[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::DepositPreauth);
+
956 }
957
-
958 auto tryField = [&](Json::Value fieldValue) -> void {
- -
960 Json::Value jo;
-
961 jo[jss::issuer] = fieldValue;
-
962 jo[jss::credential_type] = strHex(std::string_view(credType));
-
963 arr.append(jo);
-
964 jvParams[jss::deposit_preauth][jss::authorized_credentials] = arr;
-
965
-
966 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
967 auto const expectedErrMsg = fieldValue.isNull() ? RPC::missing_field_message(jss::issuer.c_str())
-
968 : RPC::expected_field_message(jss::issuer, "AccountID");
-
969 checkErrorValue(jrr, "malformedAuthorizedCredentials", expectedErrMsg);
-
970 };
+
958 {
+
959 // Failed, invalid account
+
960 Json::Value jvParams;
+
961 jvParams[jss::ledger_index] = jss::validated;
+
962 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
963
+
964 auto tryField = [&](Json::Value fieldValue) -> void {
+ +
966 Json::Value jo;
+
967 jo[jss::issuer] = fieldValue;
+
968 jo[jss::credential_type] = strHex(std::string_view(credType));
+
969 arr.append(jo);
+
970 jvParams[jss::deposit_preauth][jss::authorized_credentials] = arr;
971
-
972 auto const& badValues = getBadValues(FieldType::AccountField);
-
973 for (auto const& value : badValues)
-
974 {
-
975 tryField(value);
-
976 }
-
977 tryField(Json::nullValue);
-
978 }
-
979
-
980 {
-
981 // Failed, duplicates in credentials
-
982 Json::Value jvParams;
-
983 jvParams[jss::ledger_index] = jss::validated;
-
984 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
972 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
973 auto const expectedErrMsg = fieldValue.isNull() ? RPC::missing_field_message(jss::issuer.c_str())
+
974 : RPC::expected_field_message(jss::issuer, "AccountID");
+
975 checkErrorValue(jrr, "malformedAuthorizedCredentials", expectedErrMsg);
+
976 };
+
977
+
978 auto const& badValues = getBadValues(FieldType::AccountField);
+
979 for (auto const& value : badValues)
+
980 {
+
981 tryField(value);
+
982 }
+
983 tryField(Json::nullValue);
+
984 }
985
-
986 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
-
987 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
-
988
-
989 Json::Value jo;
-
990 jo[jss::issuer] = issuer.human();
-
991 jo[jss::credential_type] = strHex(std::string_view(credType));
-
992 arr.append(jo);
-
993 arr.append(std::move(jo));
-
994 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
- -
996 jrr[jss::result],
-
997 "malformedAuthorizedCredentials",
-
998 RPC::expected_field_message(jss::authorized_credentials, "array"));
-
999 }
-
1000
-
1001 {
-
1002 // Failed, invalid credential_type
-
1003 Json::Value jvParams;
-
1004 jvParams[jss::ledger_index] = jss::validated;
-
1005 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
986 {
+
987 // Failed, duplicates in credentials
+
988 Json::Value jvParams;
+
989 jvParams[jss::ledger_index] = jss::validated;
+
990 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
991
+
992 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
+
993 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
+
994
+
995 Json::Value jo;
+
996 jo[jss::issuer] = issuer.human();
+
997 jo[jss::credential_type] = strHex(std::string_view(credType));
+
998 arr.append(jo);
+
999 arr.append(std::move(jo));
+
1000 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
+ +
1002 jrr[jss::result],
+
1003 "malformedAuthorizedCredentials",
+
1004 RPC::expected_field_message(jss::authorized_credentials, "array"));
+
1005 }
1006
-
1007 auto tryField = [&](Json::Value fieldValue) -> void {
- -
1009 Json::Value jo;
-
1010 jo[jss::issuer] = issuer.human();
-
1011 jo[jss::credential_type] = fieldValue;
-
1012 arr.append(jo);
-
1013 jvParams[jss::deposit_preauth][jss::authorized_credentials] = arr;
-
1014
-
1015 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1016 auto const expectedErrMsg = fieldValue.isNull()
-
1017 ? RPC::missing_field_message(jss::credential_type.c_str())
-
1018 : RPC::expected_field_message(jss::credential_type, "hex string");
-
1019 checkErrorValue(jrr, "malformedAuthorizedCredentials", expectedErrMsg);
-
1020 };
-
1021
-
1022 auto const& badValues = getBadValues(FieldType::BlobField);
-
1023 for (auto const& value : badValues)
-
1024 {
-
1025 tryField(value);
-
1026 }
-
1027 tryField(Json::nullValue);
-
1028 }
-
1029
-
1030 {
-
1031 // Failed, authorized and authorized_credentials both present
-
1032 Json::Value jvParams;
-
1033 jvParams[jss::ledger_index] = jss::validated;
-
1034 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
-
1035 jvParams[jss::deposit_preauth][jss::authorized] = alice.human();
-
1036
-
1037 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
-
1038 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
-
1039
-
1040 Json::Value jo;
-
1041 jo[jss::issuer] = issuer.human();
-
1042 jo[jss::credential_type] = strHex(std::string_view(credType));
-
1043 arr.append(std::move(jo));
-
1044
-
1045 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
- -
1047 jrr[jss::result],
-
1048 "malformedRequest",
-
1049 "Must have exactly one of `authorized` and "
-
1050 "`authorized_credentials`.");
-
1051 }
-
1052
-
1053 {
-
1054 // Failed, authorized_credentials is not an array
-
1055 Json::Value jvParams;
-
1056 jvParams[jss::ledger_index] = jss::validated;
-
1057 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
- -
1059 env,
-
1060 jvParams,
-
1061 jss::deposit_preauth,
-
1062 jss::authorized_credentials,
- -
1064 "malformedAuthorizedCredentials",
-
1065 false);
-
1066 }
-
1067
-
1068 {
-
1069 // Failed, authorized_credentials contains string data
-
1070 Json::Value jvParams;
-
1071 jvParams[jss::ledger_index] = jss::validated;
-
1072 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
-
1073 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
-
1074 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
-
1075 arr.append("foobar");
-
1076
-
1077 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
- -
1079 jrr[jss::result],
-
1080 "malformedAuthorizedCredentials",
-
1081 "Invalid field 'authorized_credentials', not array.");
-
1082 }
-
1083
-
1084 {
-
1085 // Failed, authorized_credentials contains arrays
-
1086 Json::Value jvParams;
-
1087 jvParams[jss::ledger_index] = jss::validated;
-
1088 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
-
1089 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
-
1090 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
-
1091 Json::Value payload = Json::arrayValue;
-
1092 payload.append(42);
-
1093 arr.append(std::move(payload));
-
1094
-
1095 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
- -
1097 jrr[jss::result],
-
1098 "malformedAuthorizedCredentials",
-
1099 "Invalid field 'authorized_credentials', not array.");
-
1100 }
-
1101
-
1102 {
-
1103 // Failed, authorized_credentials is empty array
-
1104 Json::Value jvParams;
-
1105 jvParams[jss::ledger_index] = jss::validated;
-
1106 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
-
1107 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
-
1108
-
1109 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
- -
1111 jrr[jss::result],
-
1112 "malformedAuthorizedCredentials",
-
1113 "Invalid field 'authorized_credentials', array empty.");
-
1114 }
-
1115
-
1116 {
-
1117 // Failed, authorized_credentials is too long
-
1118 static std::array<std::string_view, 9> const credTypes = {
-
1119 "cred1", "cred2", "cred3", "cred4", "cred5", "cred6", "cred7", "cred8", "cred9"};
-
1120 static_assert(sizeof(credTypes) / sizeof(credTypes[0]) > maxCredentialsArraySize);
+
1007 {
+
1008 // Failed, invalid credential_type
+
1009 Json::Value jvParams;
+
1010 jvParams[jss::ledger_index] = jss::validated;
+
1011 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
1012
+
1013 auto tryField = [&](Json::Value fieldValue) -> void {
+ +
1015 Json::Value jo;
+
1016 jo[jss::issuer] = issuer.human();
+
1017 jo[jss::credential_type] = fieldValue;
+
1018 arr.append(jo);
+
1019 jvParams[jss::deposit_preauth][jss::authorized_credentials] = arr;
+
1020
+
1021 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1022 auto const expectedErrMsg = fieldValue.isNull()
+
1023 ? RPC::missing_field_message(jss::credential_type.c_str())
+
1024 : RPC::expected_field_message(jss::credential_type, "hex string");
+
1025 checkErrorValue(jrr, "malformedAuthorizedCredentials", expectedErrMsg);
+
1026 };
+
1027
+
1028 auto const& badValues = getBadValues(FieldType::BlobField);
+
1029 for (auto const& value : badValues)
+
1030 {
+
1031 tryField(value);
+
1032 }
+
1033 tryField(Json::nullValue);
+
1034 }
+
1035
+
1036 {
+
1037 // Failed, authorized and authorized_credentials both present
+
1038 Json::Value jvParams;
+
1039 jvParams[jss::ledger_index] = jss::validated;
+
1040 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
1041 jvParams[jss::deposit_preauth][jss::authorized] = alice.human();
+
1042
+
1043 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
+
1044 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
+
1045
+
1046 Json::Value jo;
+
1047 jo[jss::issuer] = issuer.human();
+
1048 jo[jss::credential_type] = strHex(std::string_view(credType));
+
1049 arr.append(std::move(jo));
+
1050
+
1051 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
+ +
1053 jrr[jss::result],
+
1054 "malformedRequest",
+
1055 "Must have exactly one of `authorized` and "
+
1056 "`authorized_credentials`.");
+
1057 }
+
1058
+
1059 {
+
1060 // Failed, authorized_credentials is not an array
+
1061 Json::Value jvParams;
+
1062 jvParams[jss::ledger_index] = jss::validated;
+
1063 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+ +
1065 env,
+
1066 jvParams,
+
1067 jss::deposit_preauth,
+
1068 jss::authorized_credentials,
+ +
1070 "malformedAuthorizedCredentials",
+
1071 false);
+
1072 }
+
1073
+
1074 {
+
1075 // Failed, authorized_credentials contains string data
+
1076 Json::Value jvParams;
+
1077 jvParams[jss::ledger_index] = jss::validated;
+
1078 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
1079 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
+
1080 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
+
1081 arr.append("foobar");
+
1082
+
1083 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
+ +
1085 jrr[jss::result],
+
1086 "malformedAuthorizedCredentials",
+
1087 "Invalid field 'authorized_credentials', not array.");
+
1088 }
+
1089
+
1090 {
+
1091 // Failed, authorized_credentials contains arrays
+
1092 Json::Value jvParams;
+
1093 jvParams[jss::ledger_index] = jss::validated;
+
1094 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
1095 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
+
1096 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
+
1097 Json::Value payload = Json::arrayValue;
+
1098 payload.append(42);
+
1099 arr.append(std::move(payload));
+
1100
+
1101 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
+ +
1103 jrr[jss::result],
+
1104 "malformedAuthorizedCredentials",
+
1105 "Invalid field 'authorized_credentials', not array.");
+
1106 }
+
1107
+
1108 {
+
1109 // Failed, authorized_credentials is empty array
+
1110 Json::Value jvParams;
+
1111 jvParams[jss::ledger_index] = jss::validated;
+
1112 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
1113 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
+
1114
+
1115 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
+ +
1117 jrr[jss::result],
+
1118 "malformedAuthorizedCredentials",
+
1119 "Invalid field 'authorized_credentials', array empty.");
+
1120 }
1121
-
1122 Json::Value jvParams;
-
1123 jvParams[jss::ledger_index] = jss::validated;
-
1124 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
-
1125 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
-
1126
-
1127 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
-
1128
-
1129 for (auto cred : credTypes)
-
1130 {
-
1131 Json::Value jo;
-
1132 jo[jss::issuer] = issuer.human();
-
1133 jo[jss::credential_type] = strHex(std::string_view(cred));
-
1134 arr.append(std::move(jo));
-
1135 }
-
1136
-
1137 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
- -
1139 jrr[jss::result],
-
1140 "malformedAuthorizedCredentials",
-
1141 "Invalid field 'authorized_credentials', array too long.");
-
1142 }
-
1143 }
+
1122 {
+
1123 // Failed, authorized_credentials is too long
+
1124 static std::array<std::string_view, 9> const credTypes = {
+
1125 "cred1", "cred2", "cred3", "cred4", "cred5", "cred6", "cred7", "cred8", "cred9"};
+
1126 static_assert(sizeof(credTypes) / sizeof(credTypes[0]) > maxCredentialsArraySize);
+
1127
+
1128 Json::Value jvParams;
+
1129 jvParams[jss::ledger_index] = jss::validated;
+
1130 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
+
1131 jvParams[jss::deposit_preauth][jss::authorized_credentials] = Json::arrayValue;
+
1132
+
1133 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
+
1134
+
1135 for (auto cred : credTypes)
+
1136 {
+
1137 Json::Value jo;
+
1138 jo[jss::issuer] = issuer.human();
+
1139 jo[jss::credential_type] = strHex(std::string_view(cred));
+
1140 arr.append(std::move(jo));
+
1141 }
+
1142
+
1143 auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams));
+ +
1145 jrr[jss::result],
+
1146 "malformedAuthorizedCredentials",
+
1147 "Invalid field 'authorized_credentials', array too long.");
+
1148 }
+
1149 }
-
1144
-
1145 void
-
- -
1147 {
-
1148 testcase("Directory");
-
1149 using namespace test::jtx;
-
1150 Env env{*this};
-
1151 Account const alice{"alice"};
-
1152 Account const gw{"gateway"};
-
1153 auto const USD = gw["USD"];
-
1154 env.fund(XRP(10000), alice, gw);
-
1155 env.close();
-
1156
-
1157 env.trust(USD(1000), alice);
-
1158 env.close();
-
1159
-
1160 // Run up the number of directory entries so alice has two
-
1161 // directory nodes.
-
1162 for (int d = 1'000'032; d >= 1'000'000; --d)
-
1163 {
-
1164 env(offer(alice, USD(1), drops(d)));
-
1165 }
-
1166 env.close();
-
1167
-
1168 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
1150
+
1151 void
+
+ +
1153 {
+
1154 testcase("Directory");
+
1155 using namespace test::jtx;
+
1156 Env env{*this};
+
1157 Account const alice{"alice"};
+
1158 Account const gw{"gateway"};
+
1159 auto const USD = gw["USD"];
+
1160 env.fund(XRP(10000), alice, gw);
+
1161 env.close();
+
1162
+
1163 env.trust(USD(1000), alice);
+
1164 env.close();
+
1165
+
1166 // Run up the number of directory entries so alice has two
+
1167 // directory nodes.
+
1168 for (int d = 1'000'032; d >= 1'000'000; --d)
1169 {
-
1170 // Exercise ledger_closed along the way.
-
1171 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
-
1172 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
-
1173 BEAST_EXPECT(jrr[jss::ledger_index] == 5);
-
1174 }
-
1175
-
1176 std::string const dirRootIndex = "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D";
-
1177 {
-
1178 // Locate directory by index.
-
1179 Json::Value jvParams;
-
1180 jvParams[jss::directory] = dirRootIndex;
-
1181 jvParams[jss::ledger_hash] = ledgerHash;
-
1182 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1183 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32);
-
1184 }
-
1185 {
-
1186 // Locate directory by directory root.
-
1187 Json::Value jvParams;
-
1188 jvParams[jss::directory] = Json::objectValue;
-
1189 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
-
1190 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1191 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
-
1192 }
-
1193 {
-
1194 // Locate directory by owner.
-
1195 Json::Value jvParams;
-
1196 jvParams[jss::directory] = Json::objectValue;
-
1197 jvParams[jss::directory][jss::owner] = alice.human();
-
1198 jvParams[jss::ledger_hash] = ledgerHash;
-
1199 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1200 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
-
1201 }
-
1202 {
-
1203 // Locate directory by directory root and sub_index.
-
1204 Json::Value jvParams;
-
1205 jvParams[jss::directory] = Json::objectValue;
-
1206 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
-
1207 jvParams[jss::directory][jss::sub_index] = 1;
-
1208 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1209 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
-
1210 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
-
1211 }
-
1212 {
-
1213 // Locate directory by owner and sub_index.
-
1214 Json::Value jvParams;
-
1215 jvParams[jss::directory] = Json::objectValue;
-
1216 jvParams[jss::directory][jss::owner] = alice.human();
-
1217 jvParams[jss::directory][jss::sub_index] = 1;
-
1218 jvParams[jss::ledger_hash] = ledgerHash;
-
1219 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1220 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
-
1221 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
-
1222 }
-
1223 {
-
1224 // Bad directory argument.
-
1225 Json::Value jvParams;
-
1226 jvParams[jss::ledger_hash] = ledgerHash;
-
1227 testMalformedField(env, jvParams, jss::directory, FieldType::HashOrObjectField, "malformedRequest");
+
1170 env(offer(alice, USD(1), drops(d)));
+
1171 }
+
1172 env.close();
+
1173
+
1174 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
1175 {
+
1176 // Exercise ledger_closed along the way.
+
1177 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
+
1178 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
+
1179 BEAST_EXPECT(jrr[jss::ledger_index] == 5);
+
1180 }
+
1181
+
1182 std::string const dirRootIndex = "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D";
+
1183 {
+
1184 // Locate directory by index.
+
1185 Json::Value jvParams;
+
1186 jvParams[jss::directory] = dirRootIndex;
+
1187 jvParams[jss::ledger_hash] = ledgerHash;
+
1188 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1189 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32);
+
1190 }
+
1191 {
+
1192 // Locate directory by directory root.
+
1193 Json::Value jvParams;
+
1194 jvParams[jss::directory] = Json::objectValue;
+
1195 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
+
1196 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1197 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
+
1198 }
+
1199 {
+
1200 // Locate directory by owner.
+
1201 Json::Value jvParams;
+
1202 jvParams[jss::directory] = Json::objectValue;
+
1203 jvParams[jss::directory][jss::owner] = alice.human();
+
1204 jvParams[jss::ledger_hash] = ledgerHash;
+
1205 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1206 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
+
1207 }
+
1208 {
+
1209 // Locate directory by directory root and sub_index.
+
1210 Json::Value jvParams;
+
1211 jvParams[jss::directory] = Json::objectValue;
+
1212 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
+
1213 jvParams[jss::directory][jss::sub_index] = 1;
+
1214 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1215 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
+
1216 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
+
1217 }
+
1218 {
+
1219 // Locate directory by owner and sub_index.
+
1220 Json::Value jvParams;
+
1221 jvParams[jss::directory] = Json::objectValue;
+
1222 jvParams[jss::directory][jss::owner] = alice.human();
+
1223 jvParams[jss::directory][jss::sub_index] = 1;
+
1224 jvParams[jss::ledger_hash] = ledgerHash;
+
1225 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1226 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
+
1227 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
1228 }
1229 {
-
1230 // Non-integer sub_index.
+
1230 // Bad directory argument.
1231 Json::Value jvParams;
-
1232 jvParams[jss::directory] = Json::objectValue;
-
1233 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
-
1234 jvParams[jss::ledger_hash] = ledgerHash;
- -
1236 env, jvParams, jss::directory, jss::sub_index, FieldType::UInt64Field, "malformedRequest", false);
-
1237 }
-
1238 {
-
1239 // Malformed owner entry.
-
1240 Json::Value jvParams;
-
1241 jvParams[jss::directory] = Json::objectValue;
-
1242
-
1243 jvParams[jss::ledger_hash] = ledgerHash;
- -
1245 env, jvParams, jss::directory, jss::owner, FieldType::AccountField, "malformedAddress", false);
-
1246 }
-
1247 {
-
1248 // Malformed directory object. Specifies both dir_root and owner.
-
1249 Json::Value jvParams;
-
1250 jvParams[jss::directory] = Json::objectValue;
-
1251 jvParams[jss::directory][jss::owner] = alice.human();
-
1252 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
-
1253 jvParams[jss::ledger_hash] = ledgerHash;
-
1254 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1255 checkErrorValue(jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
-
1256 }
-
1257 {
-
1258 // Incomplete directory object. Missing both dir_root and owner.
-
1259 Json::Value jvParams;
-
1260 jvParams[jss::directory] = Json::objectValue;
-
1261 jvParams[jss::directory][jss::sub_index] = 1;
-
1262 jvParams[jss::ledger_hash] = ledgerHash;
-
1263 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1264 checkErrorValue(jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
-
1265 }
-
1266 }
+
1232 jvParams[jss::ledger_hash] = ledgerHash;
+
1233 testMalformedField(env, jvParams, jss::directory, FieldType::HashOrObjectField, "malformedRequest");
+
1234 }
+
1235 {
+
1236 // Non-integer sub_index.
+
1237 Json::Value jvParams;
+
1238 jvParams[jss::directory] = Json::objectValue;
+
1239 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
+
1240 jvParams[jss::ledger_hash] = ledgerHash;
+ +
1242 env, jvParams, jss::directory, jss::sub_index, FieldType::UInt64Field, "malformedRequest", false);
+
1243 }
+
1244 {
+
1245 // Malformed owner entry.
+
1246 Json::Value jvParams;
+
1247 jvParams[jss::directory] = Json::objectValue;
+
1248
+
1249 jvParams[jss::ledger_hash] = ledgerHash;
+ +
1251 env, jvParams, jss::directory, jss::owner, FieldType::AccountField, "malformedAddress", false);
+
1252 }
+
1253 {
+
1254 // Malformed directory object. Specifies both dir_root and owner.
+
1255 Json::Value jvParams;
+
1256 jvParams[jss::directory] = Json::objectValue;
+
1257 jvParams[jss::directory][jss::owner] = alice.human();
+
1258 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
+
1259 jvParams[jss::ledger_hash] = ledgerHash;
+
1260 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1261 checkErrorValue(jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
+
1262 }
+
1263 {
+
1264 // Incomplete directory object. Missing both dir_root and owner.
+
1265 Json::Value jvParams;
+
1266 jvParams[jss::directory] = Json::objectValue;
+
1267 jvParams[jss::directory][jss::sub_index] = 1;
+
1268 jvParams[jss::ledger_hash] = ledgerHash;
+
1269 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1270 checkErrorValue(jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
+
1271 }
+
1272 }
-
1267
-
1268 void
-
- -
1270 {
-
1271 testcase("Escrow");
-
1272 using namespace test::jtx;
-
1273 Env env{*this};
-
1274 Account const alice{"alice"};
-
1275 env.fund(XRP(10000), alice);
-
1276 env.close();
-
1277
-
1278 // Lambda to create an escrow.
-
1279 auto escrowCreate = [](test::jtx::Account const& account,
-
1280 test::jtx::Account const& to,
-
1281 STAmount const& amount,
-
1282 NetClock::time_point const& cancelAfter) {
-
1283 Json::Value jv;
-
1284 jv[jss::TransactionType] = jss::EscrowCreate;
-
1285 jv[jss::Account] = account.human();
-
1286 jv[jss::Destination] = to.human();
-
1287 jv[jss::Amount] = amount.getJson(JsonOptions::none);
-
1288 jv[sfFinishAfter.jsonName] = cancelAfter.time_since_epoch().count() + 2;
-
1289 return jv;
-
1290 };
-
1291
-
1292 using namespace std::chrono_literals;
-
1293 env(escrowCreate(alice, alice, XRP(333), env.now() + 2s));
-
1294 env.close();
-
1295
-
1296 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
1297 std::string escrowIndex;
-
1298 {
-
1299 // Request the escrow using owner and sequence.
-
1300 Json::Value jvParams;
-
1301 jvParams[jss::escrow] = Json::objectValue;
-
1302 jvParams[jss::escrow][jss::owner] = alice.human();
-
1303 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
-
1304 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1305 BEAST_EXPECT(jrr[jss::node][jss::Amount] == XRP(333).value().getText());
-
1306 escrowIndex = jrr[jss::index].asString();
-
1307 }
-
1308 {
-
1309 // Request the escrow by index.
-
1310 Json::Value jvParams;
-
1311 jvParams[jss::escrow] = escrowIndex;
-
1312 jvParams[jss::ledger_hash] = ledgerHash;
-
1313 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1314 BEAST_EXPECT(jrr[jss::node][jss::Amount] == XRP(333).value().getText());
-
1315 }
-
1316 {
-
1317 // Malformed escrow fields
-
1318 runLedgerEntryTest(env, jss::escrow, {{jss::owner, "malformedOwner"}, {jss::seq, "malformedSeq"}});
-
1319 }
-
1320 }
+
1273
+
1274 void
+
+ +
1276 {
+
1277 testcase("Escrow");
+
1278 using namespace test::jtx;
+
1279 Env env{*this};
+
1280 Account const alice{"alice"};
+
1281 env.fund(XRP(10000), alice);
+
1282 env.close();
+
1283
+
1284 // Lambda to create an escrow.
+
1285 auto escrowCreate = [](test::jtx::Account const& account,
+
1286 test::jtx::Account const& to,
+
1287 STAmount const& amount,
+
1288 NetClock::time_point const& cancelAfter) {
+
1289 Json::Value jv;
+
1290 jv[jss::TransactionType] = jss::EscrowCreate;
+
1291 jv[jss::Account] = account.human();
+
1292 jv[jss::Destination] = to.human();
+
1293 jv[jss::Amount] = amount.getJson(JsonOptions::none);
+
1294 jv[sfFinishAfter.jsonName] = cancelAfter.time_since_epoch().count() + 2;
+
1295 return jv;
+
1296 };
+
1297
+
1298 using namespace std::chrono_literals;
+
1299 env(escrowCreate(alice, alice, XRP(333), env.now() + 2s));
+
1300 env.close();
+
1301
+
1302 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
1303 std::string escrowIndex;
+
1304 {
+
1305 // Request the escrow using owner and sequence.
+
1306 Json::Value jvParams;
+
1307 jvParams[jss::escrow] = Json::objectValue;
+
1308 jvParams[jss::escrow][jss::owner] = alice.human();
+
1309 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
+
1310 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1311 BEAST_EXPECT(jrr[jss::node][jss::Amount] == XRP(333).value().getText());
+
1312 escrowIndex = jrr[jss::index].asString();
+
1313 }
+
1314 {
+
1315 // Request the escrow by index.
+
1316 Json::Value jvParams;
+
1317 jvParams[jss::escrow] = escrowIndex;
+
1318 jvParams[jss::ledger_hash] = ledgerHash;
+
1319 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1320 BEAST_EXPECT(jrr[jss::node][jss::Amount] == XRP(333).value().getText());
+
1321 }
+
1322 {
+
1323 // Malformed escrow fields
+
1324 runLedgerEntryTest(env, jss::escrow, {{jss::owner, "malformedOwner"}, {jss::seq, "malformedSeq"}});
+
1325 }
+
1326 }
-
1321
-
1322 void
-
- -
1324 {
-
1325 testcase("Fee Settings");
-
1326 using namespace test::jtx;
-
1327 Env env{*this};
-
1328
-
1329 // positive test
-
1330 {
-
1331 Keylet const keylet = keylet::fees();
-
1332 Json::Value jvParams;
-
1333 jvParams[jss::fee] = to_string(keylet.key);
-
1334 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1335 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::FeeSettings);
-
1336 }
-
1337
-
1338 // negative tests
-
1339 testMalformedField(env, Json::Value{}, jss::fee, FieldType::FixedHashField, "malformedRequest");
-
1340 }
+
1327
+
1328 void
+
+ +
1330 {
+
1331 testcase("Fee Settings");
+
1332 using namespace test::jtx;
+
1333 Env env{*this};
+
1334
+
1335 // positive test
+
1336 {
+
1337 Keylet const keylet = keylet::fees();
+
1338 Json::Value jvParams;
+
1339 jvParams[jss::fee] = to_string(keylet.key);
+
1340 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1341 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::FeeSettings);
+
1342 }
+
1343
+
1344 // negative tests
+
1345 testMalformedField(env, Json::Value{}, jss::fee, FieldType::FixedHashField, "malformedRequest");
+
1346 }
-
1341
-
1342 void
-
- -
1344 {
-
1345 testcase("Ledger Hashes");
-
1346 using namespace test::jtx;
-
1347 Env env{*this};
-
1348
-
1349 // positive test
-
1350 {
-
1351 Keylet const keylet = keylet::skip();
-
1352 Json::Value jvParams;
-
1353 jvParams[jss::hashes] = to_string(keylet.key);
-
1354 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1355 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::LedgerHashes);
-
1356 }
-
1357
-
1358 // negative tests
-
1359 testMalformedField(env, Json::Value{}, jss::hashes, FieldType::FixedHashField, "malformedRequest");
-
1360 }
+
1347
+
1348 void
+
+ +
1350 {
+
1351 testcase("Ledger Hashes");
+
1352 using namespace test::jtx;
+
1353 Env env{*this};
+
1354
+
1355 // positive test
+
1356 {
+
1357 Keylet const keylet = keylet::skip();
+
1358 Json::Value jvParams;
+
1359 jvParams[jss::hashes] = to_string(keylet.key);
+
1360 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1361 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::LedgerHashes);
+
1362 }
+
1363
+
1364 // negative tests
+
1365 testMalformedField(env, Json::Value{}, jss::hashes, FieldType::FixedHashField, "malformedRequest");
+
1366 }
-
1361
-
1362 void
-
- -
1364 {
-
1365 testcase("NFT Offer");
-
1366 using namespace test::jtx;
-
1367 Env env{*this};
-
1368
-
1369 // positive test
-
1370 Account const issuer{"issuer"};
-
1371 Account const buyer{"buyer"};
-
1372 env.fund(XRP(1000), issuer, buyer);
-
1373
-
1374 uint256 const nftokenID0 = token::getNextID(env, issuer, 0, tfTransferable);
-
1375 env(token::mint(issuer, 0), txflags(tfTransferable));
-
1376 env.close();
-
1377 uint256 const offerID = keylet::nftoffer(issuer, env.seq(issuer)).key;
-
1378 env(token::createOffer(issuer, nftokenID0, drops(1)), token::destination(buyer), txflags(tfSellNFToken));
+
1367
+
1368 void
+
+ +
1370 {
+
1371 testcase("NFT Offer");
+
1372 using namespace test::jtx;
+
1373 Env env{*this};
+
1374
+
1375 // positive test
+
1376 Account const issuer{"issuer"};
+
1377 Account const buyer{"buyer"};
+
1378 env.fund(XRP(1000), issuer, buyer);
1379
-
1380 {
-
1381 Json::Value jvParams;
-
1382 jvParams[jss::nft_offer] = to_string(offerID);
-
1383 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1384 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NFTokenOffer);
-
1385 BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == issuer.human());
-
1386 BEAST_EXPECT(jrr[jss::node][sfNFTokenID.jsonName] == to_string(nftokenID0));
-
1387 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "1");
-
1388 }
-
1389
-
1390 // negative tests
-
1391 runLedgerEntryTest(env, jss::nft_offer);
-
1392 }
+
1380 uint256 const nftokenID0 = token::getNextID(env, issuer, 0, tfTransferable);
+
1381 env(token::mint(issuer, 0), txflags(tfTransferable));
+
1382 env.close();
+
1383 uint256 const offerID = keylet::nftoffer(issuer, env.seq(issuer)).key;
+
1384 env(token::createOffer(issuer, nftokenID0, drops(1)), token::destination(buyer), txflags(tfSellNFToken));
+
1385
+
1386 {
+
1387 Json::Value jvParams;
+
1388 jvParams[jss::nft_offer] = to_string(offerID);
+
1389 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1390 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NFTokenOffer);
+
1391 BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == issuer.human());
+
1392 BEAST_EXPECT(jrr[jss::node][sfNFTokenID.jsonName] == to_string(nftokenID0));
+
1393 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "1");
+
1394 }
+
1395
+
1396 // negative tests
+
1397 runLedgerEntryTest(env, jss::nft_offer);
+
1398 }
-
1393
-
1394 void
-
- -
1396 {
-
1397 testcase("NFT Page");
-
1398 using namespace test::jtx;
-
1399 Env env{*this};
-
1400
-
1401 // positive test
-
1402 Account const issuer{"issuer"};
-
1403 env.fund(XRP(1000), issuer);
-
1404
-
1405 env(token::mint(issuer, 0), txflags(tfTransferable));
-
1406 env.close();
-
1407
-
1408 auto const nftpage = keylet::nftpage_max(issuer);
-
1409 BEAST_EXPECT(env.le(nftpage) != nullptr);
+
1399
+
1400 void
+
+ +
1402 {
+
1403 testcase("NFT Page");
+
1404 using namespace test::jtx;
+
1405 Env env{*this};
+
1406
+
1407 // positive test
+
1408 Account const issuer{"issuer"};
+
1409 env.fund(XRP(1000), issuer);
1410
-
1411 {
-
1412 Json::Value jvParams;
-
1413 jvParams[jss::nft_page] = to_string(nftpage.key);
-
1414 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1415 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NFTokenPage);
-
1416 }
-
1417
-
1418 // negative tests
-
1419 runLedgerEntryTest(env, jss::nft_page);
-
1420 }
+
1411 env(token::mint(issuer, 0), txflags(tfTransferable));
+
1412 env.close();
+
1413
+
1414 auto const nftpage = keylet::nftpage_max(issuer);
+
1415 BEAST_EXPECT(env.le(nftpage) != nullptr);
+
1416
+
1417 {
+
1418 Json::Value jvParams;
+
1419 jvParams[jss::nft_page] = to_string(nftpage.key);
+
1420 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1421 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NFTokenPage);
+
1422 }
+
1423
+
1424 // negative tests
+
1425 runLedgerEntryTest(env, jss::nft_page);
+
1426 }
-
1421
-
1422 void
-
- -
1424 {
-
1425 testcase("Negative UNL");
-
1426 using namespace test::jtx;
-
1427 Env env{*this};
-
1428
-
1429 // positive test
-
1430 {
-
1431 Keylet const keylet = keylet::negativeUNL();
-
1432
-
1433 // easier to hack an object into the ledger than generate it
-
1434 // legitimately
-
1435 {
-
1436 auto const nUNL = [&](OpenView& view, beast::Journal) -> bool {
-
1437 auto const sle = std::make_shared<SLE>(keylet);
+
1427
+
1428 void
+
+ +
1430 {
+
1431 testcase("Negative UNL");
+
1432 using namespace test::jtx;
+
1433 Env env{*this};
+
1434
+
1435 // positive test
+
1436 {
+
1437 Keylet const keylet = keylet::negativeUNL();
1438
-
1439 // Create DisabledValidators array
-
1440 STArray disabledValidators;
-
1441 auto disabledValidator = STObject::makeInnerObject(sfDisabledValidator);
-
1442 auto pubKeyBlob = strUnHex(
-
1443 "ED58F6770DB5DD77E59D28CB650EC3816E2FC95021BB56E720C9A1"
-
1444 "2DA79C58A3AB");
-
1445 disabledValidator.setFieldVL(sfPublicKey, *pubKeyBlob);
-
1446 disabledValidator.setFieldU32(sfFirstLedgerSequence, 91371264);
-
1447 disabledValidators.push_back(std::move(disabledValidator));
-
1448
-
1449 sle->setFieldArray(sfDisabledValidators, disabledValidators);
-
1450 sle->setFieldH256(
-
1451 sfPreviousTxnID,
-
1452 uint256::fromVoid("8D47FFE664BE6C335108DF689537625855A6"
-
1453 "A95160CC6D351341B9"
-
1454 "2624D9C5E3"));
-
1455 sle->setFieldU32(sfPreviousTxnLgrSeq, 91442944);
-
1456
-
1457 view.rawInsert(sle);
-
1458 return true;
-
1459 };
-
1460 env.app().openLedger().modify(nUNL);
-
1461 }
-
1462
-
1463 Json::Value jvParams;
-
1464 jvParams[jss::nunl] = to_string(keylet.key);
-
1465 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1466 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NegativeUNL);
-
1467 }
-
1468
-
1469 // negative tests
-
1470 testMalformedField(env, Json::Value{}, jss::nunl, FieldType::FixedHashField, "malformedRequest");
-
1471 }
+
1439 // easier to hack an object into the ledger than generate it
+
1440 // legitimately
+
1441 {
+
1442 auto const nUNL = [&](OpenView& view, beast::Journal) -> bool {
+
1443 auto const sle = std::make_shared<SLE>(keylet);
+
1444
+
1445 // Create DisabledValidators array
+
1446 STArray disabledValidators;
+
1447 auto disabledValidator = STObject::makeInnerObject(sfDisabledValidator);
+
1448 auto pubKeyBlob = strUnHex(
+
1449 "ED58F6770DB5DD77E59D28CB650EC3816E2FC95021BB56E720C9A1"
+
1450 "2DA79C58A3AB");
+
1451 disabledValidator.setFieldVL(sfPublicKey, *pubKeyBlob);
+
1452 disabledValidator.setFieldU32(sfFirstLedgerSequence, 91371264);
+
1453 disabledValidators.push_back(std::move(disabledValidator));
+
1454
+
1455 sle->setFieldArray(sfDisabledValidators, disabledValidators);
+
1456 sle->setFieldH256(
+
1457 sfPreviousTxnID,
+ +
1459 "8D47FFE664BE6C335108DF689537625855A6"
+
1460 "A95160CC6D351341B9"
+
1461 "2624D9C5E3"));
+
1462 sle->setFieldU32(sfPreviousTxnLgrSeq, 91442944);
+
1463
+
1464 view.rawInsert(sle);
+
1465 return true;
+
1466 };
+
1467 env.app().openLedger().modify(nUNL);
+
1468 }
+
1469
+
1470 Json::Value jvParams;
+
1471 jvParams[jss::nunl] = to_string(keylet.key);
+
1472 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1473 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NegativeUNL);
+
1474 }
+
1475
+
1476 // negative tests
+
1477 testMalformedField(env, Json::Value{}, jss::nunl, FieldType::FixedHashField, "malformedRequest");
+
1478 }
-
1472
-
1473 void
-
- -
1475 {
-
1476 testcase("Offer");
-
1477 using namespace test::jtx;
-
1478 Env env{*this};
-
1479 Account const alice{"alice"};
-
1480 Account const gw{"gateway"};
-
1481 auto const USD = gw["USD"];
-
1482 env.fund(XRP(10000), alice, gw);
-
1483 env.close();
-
1484
-
1485 env(offer(alice, USD(321), XRP(322)));
-
1486 env.close();
-
1487
-
1488 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
1489 std::string offerIndex;
-
1490 {
-
1491 // Request the offer using owner and sequence.
-
1492 Json::Value jvParams;
-
1493 jvParams[jss::offer] = Json::objectValue;
-
1494 jvParams[jss::offer][jss::account] = alice.human();
-
1495 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
-
1496 jvParams[jss::ledger_hash] = ledgerHash;
-
1497 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1498 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
-
1499 offerIndex = jrr[jss::index].asString();
-
1500 }
-
1501 {
-
1502 // Request the offer using its index.
-
1503 Json::Value jvParams;
-
1504 jvParams[jss::offer] = offerIndex;
-
1505 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1506 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
+
1479
+
1480 void
+
+ +
1482 {
+
1483 testcase("Offer");
+
1484 using namespace test::jtx;
+
1485 Env env{*this};
+
1486 Account const alice{"alice"};
+
1487 Account const gw{"gateway"};
+
1488 auto const USD = gw["USD"];
+
1489 env.fund(XRP(10000), alice, gw);
+
1490 env.close();
+
1491
+
1492 env(offer(alice, USD(321), XRP(322)));
+
1493 env.close();
+
1494
+
1495 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
1496 std::string offerIndex;
+
1497 {
+
1498 // Request the offer using owner and sequence.
+
1499 Json::Value jvParams;
+
1500 jvParams[jss::offer] = Json::objectValue;
+
1501 jvParams[jss::offer][jss::account] = alice.human();
+
1502 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
+
1503 jvParams[jss::ledger_hash] = ledgerHash;
+
1504 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1505 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
+
1506 offerIndex = jrr[jss::index].asString();
1507 }
-
1508
-
1509 {
-
1510 // Malformed offer fields
-
1511 runLedgerEntryTest(env, jss::offer, {{jss::account, "malformedAddress"}, {jss::seq, "malformedRequest"}});
-
1512 }
-
1513 }
+
1508 {
+
1509 // Request the offer using its index.
+
1510 Json::Value jvParams;
+
1511 jvParams[jss::offer] = offerIndex;
+
1512 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1513 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
+
1514 }
+
1515
+
1516 {
+
1517 // Malformed offer fields
+
1518 runLedgerEntryTest(env, jss::offer, {{jss::account, "malformedAddress"}, {jss::seq, "malformedRequest"}});
+
1519 }
+
1520 }
-
1514
-
1515 void
-
- -
1517 {
-
1518 testcase("Pay Chan");
-
1519 using namespace test::jtx;
-
1520 using namespace std::literals::chrono_literals;
-
1521 Env env{*this};
-
1522 Account const alice{"alice"};
-
1523
-
1524 env.fund(XRP(10000), alice);
-
1525 env.close();
-
1526
-
1527 // Lambda to create a PayChan.
-
1528 auto payChanCreate = [](test::jtx::Account const& account,
-
1529 test::jtx::Account const& to,
-
1530 STAmount const& amount,
-
1531 NetClock::duration const& settleDelay,
-
1532 PublicKey const& pk) {
-
1533 Json::Value jv;
-
1534 jv[jss::TransactionType] = jss::PaymentChannelCreate;
-
1535 jv[jss::Account] = account.human();
-
1536 jv[jss::Destination] = to.human();
-
1537 jv[jss::Amount] = amount.getJson(JsonOptions::none);
-
1538 jv[sfSettleDelay.jsonName] = settleDelay.count();
-
1539 jv[sfPublicKey.jsonName] = strHex(pk.slice());
-
1540 return jv;
-
1541 };
-
1542
-
1543 env(payChanCreate(alice, env.master, XRP(57), 18s, alice.pk()));
-
1544 env.close();
-
1545
-
1546 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
1547
-
1548 uint256 const payChanIndex{keylet::payChan(alice, env.master, env.seq(alice) - 1).key};
-
1549 {
-
1550 // Request the payment channel using its index.
-
1551 Json::Value jvParams;
-
1552 jvParams[jss::payment_channel] = to_string(payChanIndex);
-
1553 jvParams[jss::ledger_hash] = ledgerHash;
-
1554 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1555 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
-
1556 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
-
1557 BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
-
1558 }
-
1559 {
-
1560 // Request an index that is not a payment channel.
-
1561 Json::Value jvParams;
-
1562 jvParams[jss::payment_channel] = ledgerHash;
-
1563 jvParams[jss::ledger_hash] = ledgerHash;
-
1564 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1565 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
-
1566 }
-
1567
-
1568 {
-
1569 // Malformed paychan field
-
1570 runLedgerEntryTest(env, jss::payment_channel);
-
1571 }
-
1572 }
+
1521
+
1522 void
+
+ +
1524 {
+
1525 testcase("Pay Chan");
+
1526 using namespace test::jtx;
+
1527 using namespace std::literals::chrono_literals;
+
1528 Env env{*this};
+
1529 Account const alice{"alice"};
+
1530
+
1531 env.fund(XRP(10000), alice);
+
1532 env.close();
+
1533
+
1534 // Lambda to create a PayChan.
+
1535 auto payChanCreate = [](test::jtx::Account const& account,
+
1536 test::jtx::Account const& to,
+
1537 STAmount const& amount,
+
1538 NetClock::duration const& settleDelay,
+
1539 PublicKey const& pk) {
+
1540 Json::Value jv;
+
1541 jv[jss::TransactionType] = jss::PaymentChannelCreate;
+
1542 jv[jss::Account] = account.human();
+
1543 jv[jss::Destination] = to.human();
+
1544 jv[jss::Amount] = amount.getJson(JsonOptions::none);
+
1545 jv[sfSettleDelay.jsonName] = settleDelay.count();
+
1546 jv[sfPublicKey.jsonName] = strHex(pk.slice());
+
1547 return jv;
+
1548 };
+
1549
+
1550 env(payChanCreate(alice, env.master, XRP(57), 18s, alice.pk()));
+
1551 env.close();
+
1552
+
1553 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
1554
+
1555 uint256 const payChanIndex{keylet::payChan(alice, env.master, env.seq(alice) - 1).key};
+
1556 {
+
1557 // Request the payment channel using its index.
+
1558 Json::Value jvParams;
+
1559 jvParams[jss::payment_channel] = to_string(payChanIndex);
+
1560 jvParams[jss::ledger_hash] = ledgerHash;
+
1561 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1562 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
+
1563 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
+
1564 BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
+
1565 }
+
1566 {
+
1567 // Request an index that is not a payment channel.
+
1568 Json::Value jvParams;
+
1569 jvParams[jss::payment_channel] = ledgerHash;
+
1570 jvParams[jss::ledger_hash] = ledgerHash;
+
1571 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1572 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
+
1573 }
+
1574
+
1575 {
+
1576 // Malformed paychan field
+
1577 runLedgerEntryTest(env, jss::payment_channel);
+
1578 }
+
1579 }
-
1573
-
1574 void
-
- -
1576 {
-
1577 testcase("RippleState");
-
1578 using namespace test::jtx;
-
1579 Env env{*this};
-
1580 Account const alice{"alice"};
-
1581 Account const gw{"gateway"};
-
1582 auto const USD = gw["USD"];
-
1583 env.fund(XRP(10000), alice, gw);
-
1584 env.close();
-
1585
-
1586 env.trust(USD(999), alice);
-
1587 env.close();
-
1588
-
1589 env(pay(gw, alice, USD(97)));
-
1590 env.close();
-
1591
-
1592 // check both aliases
-
1593 for (auto const& fieldName : {jss::ripple_state, jss::state})
-
1594 {
-
1595 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
1596 {
-
1597 // Request the trust line using the accounts and currency.
-
1598 Json::Value jvParams;
-
1599 jvParams[fieldName] = Json::objectValue;
-
1600 jvParams[fieldName][jss::accounts] = Json::arrayValue;
-
1601 jvParams[fieldName][jss::accounts][0u] = alice.human();
-
1602 jvParams[fieldName][jss::accounts][1u] = gw.human();
-
1603 jvParams[fieldName][jss::currency] = "USD";
-
1604 jvParams[jss::ledger_hash] = ledgerHash;
-
1605 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1606 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
-
1607 BEAST_EXPECT(jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
-
1608 }
-
1609 {
-
1610 // test basic malformed scenarios
- -
1612 env,
-
1613 fieldName,
-
1614 {
-
1615 {jss::accounts, "malformedRequest"},
-
1616 {jss::currency, "malformedCurrency"},
-
1617 });
-
1618 }
-
1619 {
-
1620 // ripple_state one of the accounts is missing.
-
1621 Json::Value jvParams;
-
1622 jvParams[fieldName] = Json::objectValue;
-
1623 jvParams[fieldName][jss::accounts] = Json::arrayValue;
-
1624 jvParams[fieldName][jss::accounts][0u] = alice.human();
-
1625 jvParams[fieldName][jss::currency] = "USD";
-
1626 jvParams[jss::ledger_hash] = ledgerHash;
-
1627 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
- -
1629 jrr,
-
1630 "malformedRequest",
-
1631 "Invalid field 'accounts', not length-2 array of "
-
1632 "Accounts.");
-
1633 }
-
1634 {
-
1635 // ripple_state more than 2 accounts.
-
1636 Json::Value jvParams;
-
1637 jvParams[fieldName] = Json::objectValue;
-
1638 jvParams[fieldName][jss::accounts] = Json::arrayValue;
-
1639 jvParams[fieldName][jss::accounts][0u] = alice.human();
-
1640 jvParams[fieldName][jss::accounts][1u] = gw.human();
-
1641 jvParams[fieldName][jss::accounts][2u] = alice.human();
-
1642 jvParams[fieldName][jss::currency] = "USD";
-
1643 jvParams[jss::ledger_hash] = ledgerHash;
-
1644 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
- -
1646 jrr,
-
1647 "malformedRequest",
-
1648 "Invalid field 'accounts', not length-2 array of "
-
1649 "Accounts.");
-
1650 }
-
1651 {
-
1652 // ripple_state account[0] / account[1] is not an account.
-
1653 Json::Value jvParams;
-
1654 jvParams[fieldName] = Json::objectValue;
-
1655 auto tryField = [&](Json::Value badAccount) -> void {
-
1656 {
-
1657 // account[0]
-
1658 jvParams[fieldName][jss::accounts] = Json::arrayValue;
-
1659 jvParams[fieldName][jss::accounts][0u] = badAccount;
-
1660 jvParams[fieldName][jss::accounts][1u] = gw.human();
-
1661 jvParams[fieldName][jss::currency] = "USD";
-
1662
-
1663 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
- -
1665 jrr, "malformedAddress", RPC::expected_field_message(jss::accounts, "array of Accounts"));
-
1666 }
-
1667
-
1668 {
-
1669 // account[1]
-
1670 jvParams[fieldName][jss::accounts] = Json::arrayValue;
-
1671 jvParams[fieldName][jss::accounts][0u] = alice.human();
-
1672 jvParams[fieldName][jss::accounts][1u] = badAccount;
-
1673 jvParams[fieldName][jss::currency] = "USD";
+
1580
+
1581 void
+
+ +
1583 {
+
1584 testcase("RippleState");
+
1585 using namespace test::jtx;
+
1586 Env env{*this};
+
1587 Account const alice{"alice"};
+
1588 Account const gw{"gateway"};
+
1589 auto const USD = gw["USD"];
+
1590 env.fund(XRP(10000), alice, gw);
+
1591 env.close();
+
1592
+
1593 env.trust(USD(999), alice);
+
1594 env.close();
+
1595
+
1596 env(pay(gw, alice, USD(97)));
+
1597 env.close();
+
1598
+
1599 // check both aliases
+
1600 for (auto const& fieldName : {jss::ripple_state, jss::state})
+
1601 {
+
1602 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
1603 {
+
1604 // Request the trust line using the accounts and currency.
+
1605 Json::Value jvParams;
+
1606 jvParams[fieldName] = Json::objectValue;
+
1607 jvParams[fieldName][jss::accounts] = Json::arrayValue;
+
1608 jvParams[fieldName][jss::accounts][0u] = alice.human();
+
1609 jvParams[fieldName][jss::accounts][1u] = gw.human();
+
1610 jvParams[fieldName][jss::currency] = "USD";
+
1611 jvParams[jss::ledger_hash] = ledgerHash;
+
1612 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1613 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
+
1614 BEAST_EXPECT(jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
+
1615 }
+
1616 {
+
1617 // test basic malformed scenarios
+ +
1619 env,
+
1620 fieldName,
+
1621 {
+
1622 {jss::accounts, "malformedRequest"},
+
1623 {jss::currency, "malformedCurrency"},
+
1624 });
+
1625 }
+
1626 {
+
1627 // ripple_state one of the accounts is missing.
+
1628 Json::Value jvParams;
+
1629 jvParams[fieldName] = Json::objectValue;
+
1630 jvParams[fieldName][jss::accounts] = Json::arrayValue;
+
1631 jvParams[fieldName][jss::accounts][0u] = alice.human();
+
1632 jvParams[fieldName][jss::currency] = "USD";
+
1633 jvParams[jss::ledger_hash] = ledgerHash;
+
1634 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+ +
1636 jrr,
+
1637 "malformedRequest",
+
1638 "Invalid field 'accounts', not length-2 array of "
+
1639 "Accounts.");
+
1640 }
+
1641 {
+
1642 // ripple_state more than 2 accounts.
+
1643 Json::Value jvParams;
+
1644 jvParams[fieldName] = Json::objectValue;
+
1645 jvParams[fieldName][jss::accounts] = Json::arrayValue;
+
1646 jvParams[fieldName][jss::accounts][0u] = alice.human();
+
1647 jvParams[fieldName][jss::accounts][1u] = gw.human();
+
1648 jvParams[fieldName][jss::accounts][2u] = alice.human();
+
1649 jvParams[fieldName][jss::currency] = "USD";
+
1650 jvParams[jss::ledger_hash] = ledgerHash;
+
1651 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+ +
1653 jrr,
+
1654 "malformedRequest",
+
1655 "Invalid field 'accounts', not length-2 array of "
+
1656 "Accounts.");
+
1657 }
+
1658 {
+
1659 // ripple_state account[0] / account[1] is not an account.
+
1660 Json::Value jvParams;
+
1661 jvParams[fieldName] = Json::objectValue;
+
1662 auto tryField = [&](Json::Value badAccount) -> void {
+
1663 {
+
1664 // account[0]
+
1665 jvParams[fieldName][jss::accounts] = Json::arrayValue;
+
1666 jvParams[fieldName][jss::accounts][0u] = badAccount;
+
1667 jvParams[fieldName][jss::accounts][1u] = gw.human();
+
1668 jvParams[fieldName][jss::currency] = "USD";
+
1669
+
1670 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+ +
1672 jrr, "malformedAddress", RPC::expected_field_message(jss::accounts, "array of Accounts"));
+
1673 }
1674
-
1675 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
- -
1677 jrr, "malformedAddress", RPC::expected_field_message(jss::accounts, "array of Accounts"));
-
1678 }
-
1679 };
-
1680
-
1681 auto const& badValues = getBadValues(FieldType::AccountField);
-
1682 for (auto const& value : badValues)
-
1683 {
-
1684 tryField(value);
-
1685 }
-
1686 tryField(Json::nullValue);
-
1687 }
-
1688 {
-
1689 // ripple_state account[0] == account[1].
-
1690 Json::Value jvParams;
-
1691 jvParams[fieldName] = Json::objectValue;
-
1692 jvParams[fieldName][jss::accounts] = Json::arrayValue;
-
1693 jvParams[fieldName][jss::accounts][0u] = alice.human();
-
1694 jvParams[fieldName][jss::accounts][1u] = alice.human();
-
1695 jvParams[fieldName][jss::currency] = "USD";
-
1696 jvParams[jss::ledger_hash] = ledgerHash;
-
1697 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1698 checkErrorValue(jrr, "malformedRequest", "Cannot have a trustline to self.");
-
1699 }
-
1700 }
-
1701 }
+
1675 {
+
1676 // account[1]
+
1677 jvParams[fieldName][jss::accounts] = Json::arrayValue;
+
1678 jvParams[fieldName][jss::accounts][0u] = alice.human();
+
1679 jvParams[fieldName][jss::accounts][1u] = badAccount;
+
1680 jvParams[fieldName][jss::currency] = "USD";
+
1681
+
1682 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+ +
1684 jrr, "malformedAddress", RPC::expected_field_message(jss::accounts, "array of Accounts"));
+
1685 }
+
1686 };
+
1687
+
1688 auto const& badValues = getBadValues(FieldType::AccountField);
+
1689 for (auto const& value : badValues)
+
1690 {
+
1691 tryField(value);
+
1692 }
+
1693 tryField(Json::nullValue);
+
1694 }
+
1695 {
+
1696 // ripple_state account[0] == account[1].
+
1697 Json::Value jvParams;
+
1698 jvParams[fieldName] = Json::objectValue;
+
1699 jvParams[fieldName][jss::accounts] = Json::arrayValue;
+
1700 jvParams[fieldName][jss::accounts][0u] = alice.human();
+
1701 jvParams[fieldName][jss::accounts][1u] = alice.human();
+
1702 jvParams[fieldName][jss::currency] = "USD";
+
1703 jvParams[jss::ledger_hash] = ledgerHash;
+
1704 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1705 checkErrorValue(jrr, "malformedRequest", "Cannot have a trustline to self.");
+
1706 }
+
1707 }
+
1708 }
-
1702
-
1703 void
-
- -
1705 {
-
1706 testcase("Signer List");
-
1707 using namespace test::jtx;
-
1708 Env env{*this};
-
1709 runLedgerEntryTest(env, jss::signer_list);
-
1710 }
+
1709
+
1710 void
+
+ +
1712 {
+
1713 testcase("Signer List");
+
1714 using namespace test::jtx;
+
1715 Env env{*this};
+
1716 runLedgerEntryTest(env, jss::signer_list);
+
1717 }
-
1711
-
1712 void
-
- -
1714 {
-
1715 testcase("Ticket");
-
1716 using namespace test::jtx;
-
1717 Env env{*this};
-
1718 env.close();
-
1719
-
1720 // Create two tickets.
-
1721 std::uint32_t const tkt1{env.seq(env.master) + 1};
-
1722 env(ticket::create(env.master, 2));
-
1723 env.close();
-
1724
-
1725 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
1726 // Request four tickets: one before the first one we created, the
-
1727 // two created tickets, and the ticket that would come after the
-
1728 // last created ticket.
-
1729 {
-
1730 // Not a valid ticket requested by index.
-
1731 Json::Value jvParams;
-
1732 jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1 - 1));
-
1733 jvParams[jss::ledger_hash] = ledgerHash;
-
1734 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1735 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
-
1736 }
-
1737 {
-
1738 // First real ticket requested by index.
-
1739 Json::Value jvParams;
-
1740 jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1));
-
1741 jvParams[jss::ledger_hash] = ledgerHash;
-
1742 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1743 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Ticket);
-
1744 BEAST_EXPECT(jrr[jss::node][sfTicketSequence.jsonName] == tkt1);
-
1745 }
-
1746 {
-
1747 // Second real ticket requested by account and sequence.
-
1748 Json::Value jvParams;
-
1749 jvParams[jss::ticket] = Json::objectValue;
-
1750 jvParams[jss::ticket][jss::account] = env.master.human();
-
1751 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 1;
-
1752 jvParams[jss::ledger_hash] = ledgerHash;
-
1753 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1754 BEAST_EXPECT(jrr[jss::node][jss::index] == to_string(getTicketIndex(env.master, tkt1 + 1)));
-
1755 }
-
1756 {
-
1757 // Not a valid ticket requested by account and sequence.
-
1758 Json::Value jvParams;
-
1759 jvParams[jss::ticket] = Json::objectValue;
-
1760 jvParams[jss::ticket][jss::account] = env.master.human();
-
1761 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 2;
-
1762 jvParams[jss::ledger_hash] = ledgerHash;
-
1763 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1764 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
-
1765 }
-
1766 {
-
1767 // Request a ticket using an account root entry.
-
1768 Json::Value jvParams;
-
1769 jvParams[jss::ticket] = to_string(keylet::account(env.master).key);
-
1770 jvParams[jss::ledger_hash] = ledgerHash;
-
1771 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1772 checkErrorValue(jrr, "unexpectedLedgerType", "Unexpected ledger type.");
-
1773 }
-
1774
-
1775 {
-
1776 // test basic malformed scenarios
- -
1778 env,
-
1779 jss::ticket,
-
1780 {
-
1781 {jss::account, "malformedAddress"},
-
1782 {jss::ticket_seq, "malformedRequest"},
-
1783 });
-
1784 }
-
1785 }
+
1718
+
1719 void
+
+ +
1721 {
+
1722 testcase("Ticket");
+
1723 using namespace test::jtx;
+
1724 Env env{*this};
+
1725 env.close();
+
1726
+
1727 // Create two tickets.
+
1728 std::uint32_t const tkt1{env.seq(env.master) + 1};
+
1729 env(ticket::create(env.master, 2));
+
1730 env.close();
+
1731
+
1732 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
1733 // Request four tickets: one before the first one we created, the
+
1734 // two created tickets, and the ticket that would come after the
+
1735 // last created ticket.
+
1736 {
+
1737 // Not a valid ticket requested by index.
+
1738 Json::Value jvParams;
+
1739 jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1 - 1));
+
1740 jvParams[jss::ledger_hash] = ledgerHash;
+
1741 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1742 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
+
1743 }
+
1744 {
+
1745 // First real ticket requested by index.
+
1746 Json::Value jvParams;
+
1747 jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1));
+
1748 jvParams[jss::ledger_hash] = ledgerHash;
+
1749 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1750 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Ticket);
+
1751 BEAST_EXPECT(jrr[jss::node][sfTicketSequence.jsonName] == tkt1);
+
1752 }
+
1753 {
+
1754 // Second real ticket requested by account and sequence.
+
1755 Json::Value jvParams;
+
1756 jvParams[jss::ticket] = Json::objectValue;
+
1757 jvParams[jss::ticket][jss::account] = env.master.human();
+
1758 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 1;
+
1759 jvParams[jss::ledger_hash] = ledgerHash;
+
1760 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1761 BEAST_EXPECT(jrr[jss::node][jss::index] == to_string(getTicketIndex(env.master, tkt1 + 1)));
+
1762 }
+
1763 {
+
1764 // Not a valid ticket requested by account and sequence.
+
1765 Json::Value jvParams;
+
1766 jvParams[jss::ticket] = Json::objectValue;
+
1767 jvParams[jss::ticket][jss::account] = env.master.human();
+
1768 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 2;
+
1769 jvParams[jss::ledger_hash] = ledgerHash;
+
1770 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1771 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
+
1772 }
+
1773 {
+
1774 // Request a ticket using an account root entry.
+
1775 Json::Value jvParams;
+
1776 jvParams[jss::ticket] = to_string(keylet::account(env.master).key);
+
1777 jvParams[jss::ledger_hash] = ledgerHash;
+
1778 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1779 checkErrorValue(jrr, "unexpectedLedgerType", "Unexpected ledger type.");
+
1780 }
+
1781
+
1782 {
+
1783 // test basic malformed scenarios
+ +
1785 env,
+
1786 jss::ticket,
+
1787 {
+
1788 {jss::account, "malformedAddress"},
+
1789 {jss::ticket_seq, "malformedRequest"},
+
1790 });
+
1791 }
+
1792 }
-
1786
-
1787 void
-
- -
1789 {
-
1790 testcase("DID");
-
1791 using namespace test::jtx;
-
1792 using namespace std::literals::chrono_literals;
-
1793 Env env{*this};
-
1794 Account const alice{"alice"};
-
1795
-
1796 env.fund(XRP(10000), alice);
-
1797 env.close();
-
1798
-
1799 // Lambda to create a DID.
-
1800 auto didCreate = [](test::jtx::Account const& account) {
-
1801 Json::Value jv;
-
1802 jv[jss::TransactionType] = jss::DIDSet;
-
1803 jv[jss::Account] = account.human();
-
1804 jv[sfDIDDocument.jsonName] = strHex(std::string{"data"});
-
1805 jv[sfURI.jsonName] = strHex(std::string{"uri"});
-
1806 return jv;
-
1807 };
-
1808
-
1809 env(didCreate(alice));
-
1810 env.close();
-
1811
-
1812 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
1813
-
1814 {
-
1815 // Request the DID using its index.
-
1816 Json::Value jvParams;
-
1817 jvParams[jss::did] = alice.human();
-
1818 jvParams[jss::ledger_hash] = ledgerHash;
-
1819 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1820 BEAST_EXPECT(jrr[jss::node][sfDIDDocument.jsonName] == strHex(std::string{"data"}));
-
1821 BEAST_EXPECT(jrr[jss::node][sfURI.jsonName] == strHex(std::string{"uri"}));
-
1822 }
-
1823 {
-
1824 // Request an index that is not a DID.
-
1825 Json::Value jvParams;
-
1826 jvParams[jss::did] = env.master.human();
-
1827 jvParams[jss::ledger_hash] = ledgerHash;
-
1828 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1829 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
-
1830 }
-
1831 {
-
1832 // Malformed DID index
-
1833 Json::Value jvParams;
-
1834 testMalformedField(env, jvParams, jss::did, FieldType::AccountField, "malformedAddress");
-
1835 }
-
1836 }
+
1793
+
1794 void
+
+ +
1796 {
+
1797 testcase("DID");
+
1798 using namespace test::jtx;
+
1799 using namespace std::literals::chrono_literals;
+
1800 Env env{*this};
+
1801 Account const alice{"alice"};
+
1802
+
1803 env.fund(XRP(10000), alice);
+
1804 env.close();
+
1805
+
1806 // Lambda to create a DID.
+
1807 auto didCreate = [](test::jtx::Account const& account) {
+
1808 Json::Value jv;
+
1809 jv[jss::TransactionType] = jss::DIDSet;
+
1810 jv[jss::Account] = account.human();
+
1811 jv[sfDIDDocument.jsonName] = strHex(std::string{"data"});
+
1812 jv[sfURI.jsonName] = strHex(std::string{"uri"});
+
1813 return jv;
+
1814 };
+
1815
+
1816 env(didCreate(alice));
+
1817 env.close();
+
1818
+
1819 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
1820
+
1821 {
+
1822 // Request the DID using its index.
+
1823 Json::Value jvParams;
+
1824 jvParams[jss::did] = alice.human();
+
1825 jvParams[jss::ledger_hash] = ledgerHash;
+
1826 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1827 BEAST_EXPECT(jrr[jss::node][sfDIDDocument.jsonName] == strHex(std::string{"data"}));
+
1828 BEAST_EXPECT(jrr[jss::node][sfURI.jsonName] == strHex(std::string{"uri"}));
+
1829 }
+
1830 {
+
1831 // Request an index that is not a DID.
+
1832 Json::Value jvParams;
+
1833 jvParams[jss::did] = env.master.human();
+
1834 jvParams[jss::ledger_hash] = ledgerHash;
+
1835 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1836 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
+
1837 }
+
1838 {
+
1839 // Malformed DID index
+
1840 Json::Value jvParams;
+
1841 testMalformedField(env, jvParams, jss::did, FieldType::AccountField, "malformedAddress");
+
1842 }
+
1843 }
-
1837
-
1838 void
-
- -
1840 {
-
1841 testcase("Invalid Oracle Ledger Entry");
-
1842 using namespace xrpl::test::jtx;
-
1843 using namespace xrpl::test::jtx::oracle;
1844
-
1845 Env env(*this);
-
1846 Account const owner("owner");
-
1847 env.fund(XRP(1'000), owner);
-
1848 Oracle oracle(env, {.owner = owner, .fee = static_cast<int>(env.current()->fees().base.drops())});
-
1849
-
1850 {
-
1851 // test basic malformed scenarios
- -
1853 env,
-
1854 jss::oracle,
-
1855 {
-
1856 {jss::account, "malformedAccount"},
-
1857 {jss::oracle_document_id, "malformedDocumentID"},
-
1858 });
-
1859 }
-
1860 }
+
1845 void
+
+ +
1847 {
+
1848 testcase("Invalid Oracle Ledger Entry");
+
1849 using namespace xrpl::test::jtx;
+
1850 using namespace xrpl::test::jtx::oracle;
+
1851
+
1852 Env env(*this);
+
1853 Account const owner("owner");
+
1854 env.fund(XRP(1'000), owner);
+
1855 Oracle oracle(env, {.owner = owner, .fee = static_cast<int>(env.current()->fees().base.drops())});
+
1856
+
1857 {
+
1858 // test basic malformed scenarios
+ +
1860 env,
+
1861 jss::oracle,
+
1862 {
+
1863 {jss::account, "malformedAccount"},
+
1864 {jss::oracle_document_id, "malformedDocumentID"},
+
1865 });
+
1866 }
+
1867 }
-
1861
-
1862 void
-
- -
1864 {
-
1865 testcase("Oracle Ledger Entry");
-
1866 using namespace xrpl::test::jtx;
-
1867 using namespace xrpl::test::jtx::oracle;
1868
-
1869 Env env(*this);
-
1870 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
1871 std::vector<AccountID> accounts;
- -
1873 for (int i = 0; i < 10; ++i)
-
1874 {
-
1875 Account const owner(std::string("owner") + std::to_string(i));
-
1876 env.fund(XRP(1'000), owner);
-
1877 // different accounts can have the same asset pair
-
1878 Oracle oracle(env, {.owner = owner, .documentID = i, .fee = baseFee});
-
1879 accounts.push_back(owner.id());
-
1880 oracles.push_back(oracle.documentID());
-
1881 // same account can have different asset pair
-
1882 Oracle oracle1(env, {.owner = owner, .documentID = i + 10, .fee = baseFee});
-
1883 accounts.push_back(owner.id());
-
1884 oracles.push_back(oracle1.documentID());
-
1885 }
-
1886 for (int i = 0; i < accounts.size(); ++i)
-
1887 {
-
1888 auto const jv = [&]() {
-
1889 // document id is uint32
-
1890 if (i % 2)
-
1891 return Oracle::ledgerEntry(env, accounts[i], oracles[i]);
-
1892 // document id is string
-
1893 return Oracle::ledgerEntry(env, accounts[i], std::to_string(oracles[i]));
-
1894 }();
-
1895 try
-
1896 {
-
1897 BEAST_EXPECT(jv[jss::node][jss::Owner] == to_string(accounts[i]));
-
1898 }
-
1899 catch (...)
-
1900 {
-
1901 fail();
-
1902 }
-
1903 }
-
1904 }
+
1869 void
+
+ +
1871 {
+
1872 testcase("Oracle Ledger Entry");
+
1873 using namespace xrpl::test::jtx;
+
1874 using namespace xrpl::test::jtx::oracle;
+
1875
+
1876 Env env(*this);
+
1877 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
1878 std::vector<AccountID> accounts;
+ +
1880 for (int i = 0; i < 10; ++i)
+
1881 {
+
1882 Account const owner(std::string("owner") + std::to_string(i));
+
1883 env.fund(XRP(1'000), owner);
+
1884 // different accounts can have the same asset pair
+
1885 Oracle oracle(env, {.owner = owner, .documentID = i, .fee = baseFee});
+
1886 accounts.push_back(owner.id());
+
1887 oracles.push_back(oracle.documentID());
+
1888 // same account can have different asset pair
+
1889 Oracle oracle1(env, {.owner = owner, .documentID = i + 10, .fee = baseFee});
+
1890 accounts.push_back(owner.id());
+
1891 oracles.push_back(oracle1.documentID());
+
1892 }
+
1893 for (int i = 0; i < accounts.size(); ++i)
+
1894 {
+
1895 auto const jv = [&]() {
+
1896 // document id is uint32
+
1897 if (i % 2)
+
1898 return Oracle::ledgerEntry(env, accounts[i], oracles[i]);
+
1899 // document id is string
+
1900 return Oracle::ledgerEntry(env, accounts[i], std::to_string(oracles[i]));
+
1901 }();
+
1902 try
+
1903 {
+
1904 BEAST_EXPECT(jv[jss::node][jss::Owner] == to_string(accounts[i]));
+
1905 }
+
1906 catch (...)
+
1907 {
+
1908 fail();
+
1909 }
+
1910 }
+
1911 }
-
1905
-
1906 void
-
- -
1908 {
-
1909 testcase("MPT");
-
1910 using namespace test::jtx;
-
1911 using namespace std::literals::chrono_literals;
-
1912 Env env{*this};
-
1913 Account const alice{"alice"};
-
1914 Account const bob("bob");
-
1915
-
1916 MPTTester mptAlice(env, alice, {.holders = {bob}});
-
1917 mptAlice.create(
-
1918 {.transferFee = 10,
-
1919 .metadata = "123",
-
1920 .ownerCount = 1,
- - -
1923 mptAlice.authorize({.account = bob, .holderCount = 1});
-
1924
-
1925 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
1926
-
1927 std::string const badMptID = "00000193B9DDCAF401B5B3B26875986043F82CD0D13B4315";
-
1928 {
-
1929 // Request the MPTIssuance using its MPTIssuanceID.
-
1930 Json::Value jvParams;
-
1931 jvParams[jss::mpt_issuance] = strHex(mptAlice.issuanceID());
-
1932 jvParams[jss::ledger_hash] = ledgerHash;
-
1933 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1934 BEAST_EXPECT(jrr[jss::node][sfMPTokenMetadata.jsonName] == strHex(std::string{"123"}));
-
1935 BEAST_EXPECT(jrr[jss::node][jss::mpt_issuance_id] == strHex(mptAlice.issuanceID()));
-
1936 }
-
1937 {
-
1938 // Request an index that is not a MPTIssuance.
-
1939 Json::Value jvParams;
-
1940 jvParams[jss::mpt_issuance] = badMptID;
-
1941 jvParams[jss::ledger_hash] = ledgerHash;
-
1942 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1943 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
-
1944 }
-
1945 {
-
1946 // Request the MPToken using its owner + mptIssuanceID.
-
1947 Json::Value jvParams;
-
1948 jvParams[jss::mptoken] = Json::objectValue;
-
1949 jvParams[jss::mptoken][jss::account] = bob.human();
-
1950 jvParams[jss::mptoken][jss::mpt_issuance_id] = strHex(mptAlice.issuanceID());
-
1951 jvParams[jss::ledger_hash] = ledgerHash;
-
1952 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1953 BEAST_EXPECT(jrr[jss::node][sfMPTokenIssuanceID.jsonName] == strHex(mptAlice.issuanceID()));
-
1954 }
-
1955 {
-
1956 // Request the MPToken using a bad mptIssuanceID.
-
1957 Json::Value jvParams;
-
1958 jvParams[jss::mptoken] = Json::objectValue;
-
1959 jvParams[jss::mptoken][jss::account] = bob.human();
-
1960 jvParams[jss::mptoken][jss::mpt_issuance_id] = badMptID;
-
1961 jvParams[jss::ledger_hash] = ledgerHash;
-
1962 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
1963 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
-
1964 }
-
1965 {
-
1966 // Malformed MPTIssuance index
-
1967 Json::Value jvParams;
-
1968 testMalformedField(env, jvParams, jss::mptoken, FieldType::HashOrObjectField, "malformedRequest");
-
1969 }
-
1970 }
+
1912
+
1913 void
+
+ +
1915 {
+
1916 testcase("MPT");
+
1917 using namespace test::jtx;
+
1918 using namespace std::literals::chrono_literals;
+
1919 Env env{*this};
+
1920 Account const alice{"alice"};
+
1921 Account const bob("bob");
+
1922
+
1923 MPTTester mptAlice(env, alice, {.holders = {bob}});
+
1924 mptAlice.create(
+
1925 {.transferFee = 10,
+
1926 .metadata = "123",
+
1927 .ownerCount = 1,
+ + +
1930 mptAlice.authorize({.account = bob, .holderCount = 1});
+
1931
+
1932 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
1933
+
1934 std::string const badMptID = "00000193B9DDCAF401B5B3B26875986043F82CD0D13B4315";
+
1935 {
+
1936 // Request the MPTIssuance using its MPTIssuanceID.
+
1937 Json::Value jvParams;
+
1938 jvParams[jss::mpt_issuance] = strHex(mptAlice.issuanceID());
+
1939 jvParams[jss::ledger_hash] = ledgerHash;
+
1940 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1941 BEAST_EXPECT(jrr[jss::node][sfMPTokenMetadata.jsonName] == strHex(std::string{"123"}));
+
1942 BEAST_EXPECT(jrr[jss::node][jss::mpt_issuance_id] == strHex(mptAlice.issuanceID()));
+
1943 }
+
1944 {
+
1945 // Request an index that is not a MPTIssuance.
+
1946 Json::Value jvParams;
+
1947 jvParams[jss::mpt_issuance] = badMptID;
+
1948 jvParams[jss::ledger_hash] = ledgerHash;
+
1949 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1950 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
+
1951 }
+
1952 {
+
1953 // Request the MPToken using its owner + mptIssuanceID.
+
1954 Json::Value jvParams;
+
1955 jvParams[jss::mptoken] = Json::objectValue;
+
1956 jvParams[jss::mptoken][jss::account] = bob.human();
+
1957 jvParams[jss::mptoken][jss::mpt_issuance_id] = strHex(mptAlice.issuanceID());
+
1958 jvParams[jss::ledger_hash] = ledgerHash;
+
1959 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1960 BEAST_EXPECT(jrr[jss::node][sfMPTokenIssuanceID.jsonName] == strHex(mptAlice.issuanceID()));
+
1961 }
+
1962 {
+
1963 // Request the MPToken using a bad mptIssuanceID.
+
1964 Json::Value jvParams;
+
1965 jvParams[jss::mptoken] = Json::objectValue;
+
1966 jvParams[jss::mptoken][jss::account] = bob.human();
+
1967 jvParams[jss::mptoken][jss::mpt_issuance_id] = badMptID;
+
1968 jvParams[jss::ledger_hash] = ledgerHash;
+
1969 Json::Value const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
1970 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
+
1971 }
+
1972 {
+
1973 // Malformed MPTIssuance index
+
1974 Json::Value jvParams;
+
1975 testMalformedField(env, jvParams, jss::mptoken, FieldType::HashOrObjectField, "malformedRequest");
+
1976 }
+
1977 }
-
1971
-
1972 void
-
- -
1974 {
-
1975 testcase("PermissionedDomain");
-
1976
-
1977 using namespace test::jtx;
1978
-
1979 Env env(*this, testable_amendments() | featurePermissionedDomains);
-
1980 Account const issuer{"issuer"};
-
1981 Account const alice{"alice"};
-
1982 Account const bob{"bob"};
+
1979 void
+
+ +
1981 {
+
1982 testcase("PermissionedDomain");
1983
-
1984 env.fund(XRP(5000), issuer, alice, bob);
-
1985 env.close();
-
1986
-
1987 auto const seq = env.seq(alice);
-
1988 env(pdomain::setTx(alice, {{alice, "first credential"}}));
-
1989 env.close();
-
1990 auto const objects = pdomain::getObjects(alice, env);
-
1991 if (!BEAST_EXPECT(objects.size() == 1))
-
1992 return;
+
1984 using namespace test::jtx;
+
1985
+
1986 Env env(*this, testable_amendments() | featurePermissionedDomains);
+
1987 Account const issuer{"issuer"};
+
1988 Account const alice{"alice"};
+
1989 Account const bob{"bob"};
+
1990
+
1991 env.fund(XRP(5000), issuer, alice, bob);
+
1992 env.close();
1993
-
1994 {
-
1995 // Succeed
-
1996 Json::Value params;
-
1997 params[jss::ledger_index] = jss::validated;
-
1998 params[jss::permissioned_domain][jss::account] = alice.human();
-
1999 params[jss::permissioned_domain][jss::seq] = seq;
-
2000 auto jv = env.rpc("json", "ledger_entry", to_string(params));
-
2001 BEAST_EXPECT(
-
2002 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
-
2003 jv[jss::result].isMember(jss::node) &&
-
2004 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
-
2005 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::PermissionedDomain);
-
2006
-
2007 std::string const pdIdx = jv[jss::result][jss::index].asString();
-
2008 BEAST_EXPECT(strHex(keylet::permissionedDomain(alice, seq).key) == pdIdx);
-
2009
-
2010 params.clear();
-
2011 params[jss::ledger_index] = jss::validated;
-
2012 params[jss::permissioned_domain] = pdIdx;
-
2013 jv = env.rpc("json", "ledger_entry", to_string(params));
-
2014 BEAST_EXPECT(
-
2015 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
-
2016 jv[jss::result].isMember(jss::node) &&
-
2017 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
-
2018 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::PermissionedDomain);
-
2019 }
-
2020
-
2021 {
-
2022 // Fail, invalid permissioned domain index
-
2023 Json::Value params;
-
2024 params[jss::ledger_index] = jss::validated;
-
2025 params[jss::permissioned_domain] =
-
2026 "12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A"
-
2027 "DE";
-
2028 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
-
2029 checkErrorValue(jrr[jss::result], "entryNotFound", "Entry not found.");
-
2030 }
-
2031 {
-
2032 // test basic malformed scenarios
- -
2034 env,
-
2035 jss::permissioned_domain,
-
2036 {
-
2037 {jss::account, "malformedAddress"},
-
2038 {jss::seq, "malformedRequest"},
-
2039 });
-
2040 }
-
2041 }
+
1994 auto const seq = env.seq(alice);
+
1995 env(pdomain::setTx(alice, {{alice, "first credential"}}));
+
1996 env.close();
+
1997 auto const objects = pdomain::getObjects(alice, env);
+
1998 if (!BEAST_EXPECT(objects.size() == 1))
+
1999 return;
+
2000
+
2001 {
+
2002 // Succeed
+
2003 Json::Value params;
+
2004 params[jss::ledger_index] = jss::validated;
+
2005 params[jss::permissioned_domain][jss::account] = alice.human();
+
2006 params[jss::permissioned_domain][jss::seq] = seq;
+
2007 auto jv = env.rpc("json", "ledger_entry", to_string(params));
+
2008 BEAST_EXPECT(
+
2009 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
+
2010 jv[jss::result].isMember(jss::node) &&
+
2011 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
+
2012 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::PermissionedDomain);
+
2013
+
2014 std::string const pdIdx = jv[jss::result][jss::index].asString();
+
2015 BEAST_EXPECT(strHex(keylet::permissionedDomain(alice, seq).key) == pdIdx);
+
2016
+
2017 params.clear();
+
2018 params[jss::ledger_index] = jss::validated;
+
2019 params[jss::permissioned_domain] = pdIdx;
+
2020 jv = env.rpc("json", "ledger_entry", to_string(params));
+
2021 BEAST_EXPECT(
+
2022 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
+
2023 jv[jss::result].isMember(jss::node) &&
+
2024 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
+
2025 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::PermissionedDomain);
+
2026 }
+
2027
+
2028 {
+
2029 // Fail, invalid permissioned domain index
+
2030 Json::Value params;
+
2031 params[jss::ledger_index] = jss::validated;
+
2032 params[jss::permissioned_domain] =
+
2033 "12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A"
+
2034 "DE";
+
2035 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
+
2036 checkErrorValue(jrr[jss::result], "entryNotFound", "Entry not found.");
+
2037 }
+
2038 {
+
2039 // test basic malformed scenarios
+ +
2041 env,
+
2042 jss::permissioned_domain,
+
2043 {
+
2044 {jss::account, "malformedAddress"},
+
2045 {jss::seq, "malformedRequest"},
+
2046 });
+
2047 }
+
2048 }
-
2042
-
2044 void
-
- -
2046 {
-
2047 using namespace test::jtx;
-
2048
-
2049 Account const alice{"alice"};
-
2050 Account const bob{"bob"};
-
2051
-
2052 Env env{*this, envconfig([](auto cfg) {
-
2053 cfg->START_UP = StartUpType::FRESH;
-
2054 return cfg;
-
2055 })};
-
2056
-
2057 env.close();
+
2049
+
2051 void
+
+ +
2053 {
+
2054 using namespace test::jtx;
+
2055
+
2056 Account const alice{"alice"};
+
2057 Account const bob{"bob"};
2058
-
2069 auto checkResult = [&](bool good,
-
2070 Json::Value const& jv,
-
2071 Json::StaticString const& expectedType,
-
2072 std::optional<std::string> const& expectedError = {}) {
-
2073 if (good)
-
2074 {
-
2075 BEAST_EXPECTS(
-
2076 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
-
2077 jv[jss::result].isMember(jss::node) &&
-
2078 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
-
2079 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == expectedType,
-
2080 to_string(jv));
-
2081 }
-
2082 else
-
2083 {
-
2084 BEAST_EXPECTS(
-
2085 jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) &&
-
2086 !jv[jss::result].isMember(jss::node) &&
-
2087 jv[jss::result][jss::error] == expectedError.value_or("entryNotFound"),
-
2088 to_string(jv));
-
2089 }
-
2090 };
-
2091
-
2102 auto test = [&](Json::StaticString const& field,
-
2103 Json::StaticString const& expectedType,
-
2104 Keylet const& expectedKey,
-
2105 bool good) {
-
2106 testcase << expectedType.c_str() << (good ? "" : " not") << " found";
-
2107
-
2108 auto const hexKey = strHex(expectedKey.key);
-
2109
-
2110 {
-
2111 // Test bad values
-
2112 // "field":null
-
2113 Json::Value params;
-
2114 params[jss::ledger_index] = jss::validated;
-
2115 params[field] = Json::nullValue;
-
2116 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2117 checkResult(false, jv, expectedType, "malformedRequest");
-
2118 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2119 }
-
2120
-
2121 {
-
2122 Json::Value params;
-
2123 // "field":"string"
-
2124 params[jss::ledger_index] = jss::validated;
-
2125 params[field] = "arbitrary string";
-
2126 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2127 checkResult(false, jv, expectedType, "malformedRequest");
-
2128 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2129 }
-
2130
-
2131 {
-
2132 Json::Value params;
-
2133 // "field":false
-
2134 params[jss::ledger_index] = jss::validated;
-
2135 params[field] = false;
-
2136 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2137 checkResult(false, jv, expectedType, "invalidParams");
-
2138 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2139 }
-
2140
-
2141 {
-
2142 Json::Value params;
-
2143
-
2144 // "field":[incorrect index hash]
-
2145 auto const badKey = strHex(expectedKey.key + uint256{1});
-
2146 params[jss::ledger_index] = jss::validated;
-
2147 params[field] = badKey;
-
2148 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2149 checkResult(false, jv, expectedType, "entryNotFound");
-
2150 BEAST_EXPECTS(jv[jss::result][jss::index] == badKey, to_string(jv));
-
2151 }
-
2152
-
2153 {
-
2154 Json::Value params;
-
2155 // "index":"field" using API 2
-
2156 params[jss::ledger_index] = jss::validated;
-
2157 params[jss::index] = field;
-
2158 params[jss::api_version] = 2;
-
2159 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2160 checkResult(false, jv, expectedType, "malformedRequest");
-
2161 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2162 }
-
2163
-
2164 std::string const pdIdx = [&]() {
-
2165 {
-
2166 Json::Value params;
-
2167 // Test good values
-
2168 // Use the "field":true notation
-
2169 params[jss::ledger_index] = jss::validated;
-
2170 params[field] = true;
-
2171 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2172 // Index will always be returned for valid parameters.
-
2173 std::string const pdIdx = jv[jss::result][jss::index].asString();
-
2174 BEAST_EXPECTS(hexKey == pdIdx, to_string(jv));
-
2175 checkResult(good, jv, expectedType);
-
2176
-
2177 return pdIdx;
-
2178 }
-
2179 }();
-
2180
-
2181 {
-
2182 Json::Value params;
-
2183 // "field":"[index hash]"
-
2184 params[jss::ledger_index] = jss::validated;
-
2185 params[field] = hexKey;
-
2186 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2187 checkResult(good, jv, expectedType);
-
2188 BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
-
2189 }
-
2190
-
2191 {
-
2192 // Bad value
-
2193 // Use the "index":"field" notation with API 2
-
2194 Json::Value params;
-
2195 params[jss::ledger_index] = jss::validated;
-
2196 params[jss::index] = field;
-
2197 params[jss::api_version] = 2;
-
2198 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2199 checkResult(false, jv, expectedType, "malformedRequest");
-
2200 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2201 }
-
2202
-
2203 {
-
2204 Json::Value params;
-
2205 // Use the "index":"field" notation with API 3
-
2206 params[jss::ledger_index] = jss::validated;
-
2207 params[jss::index] = field;
-
2208 params[jss::api_version] = 3;
-
2209 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2210 // Index is correct either way
-
2211 BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
-
2212 checkResult(good, jv, expectedType);
-
2213 }
-
2214
-
2215 {
-
2216 Json::Value params;
-
2217 // Use the "index":"[index hash]" notation
-
2218 params[jss::ledger_index] = jss::validated;
-
2219 params[jss::index] = pdIdx;
-
2220 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2221 // Index is correct either way
-
2222 BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
-
2223 checkResult(good, jv, expectedType);
-
2224 }
-
2225 };
-
2226
-
2227 test(jss::amendments, jss::Amendments, keylet::amendments(), true);
-
2228 test(jss::fee, jss::FeeSettings, keylet::fees(), true);
-
2229 // There won't be an nunl
-
2230 test(jss::nunl, jss::NegativeUNL, keylet::negativeUNL(), false);
-
2231 // Can only get the short skip list this way
-
2232 test(jss::hashes, jss::LedgerHashes, keylet::skip(), true);
-
2233 }
+
2059 Env env{*this, envconfig([](auto cfg) {
+
2060 cfg->START_UP = StartUpType::FRESH;
+
2061 return cfg;
+
2062 })};
+
2063
+
2064 env.close();
+
2065
+
2076 auto checkResult = [&](bool good,
+
2077 Json::Value const& jv,
+
2078 Json::StaticString const& expectedType,
+
2079 std::optional<std::string> const& expectedError = {}) {
+
2080 if (good)
+
2081 {
+
2082 BEAST_EXPECTS(
+
2083 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
+
2084 jv[jss::result].isMember(jss::node) &&
+
2085 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
+
2086 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == expectedType,
+
2087 to_string(jv));
+
2088 }
+
2089 else
+
2090 {
+
2091 BEAST_EXPECTS(
+
2092 jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) &&
+
2093 !jv[jss::result].isMember(jss::node) &&
+
2094 jv[jss::result][jss::error] == expectedError.value_or("entryNotFound"),
+
2095 to_string(jv));
+
2096 }
+
2097 };
+
2098
+
2109 auto test = [&](Json::StaticString const& field,
+
2110 Json::StaticString const& expectedType,
+
2111 Keylet const& expectedKey,
+
2112 bool good) {
+
2113 testcase << expectedType.c_str() << (good ? "" : " not") << " found";
+
2114
+
2115 auto const hexKey = strHex(expectedKey.key);
+
2116
+
2117 {
+
2118 // Test bad values
+
2119 // "field":null
+
2120 Json::Value params;
+
2121 params[jss::ledger_index] = jss::validated;
+
2122 params[field] = Json::nullValue;
+
2123 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2124 checkResult(false, jv, expectedType, "malformedRequest");
+
2125 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2126 }
+
2127
+
2128 {
+
2129 Json::Value params;
+
2130 // "field":"string"
+
2131 params[jss::ledger_index] = jss::validated;
+
2132 params[field] = "arbitrary string";
+
2133 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2134 checkResult(false, jv, expectedType, "malformedRequest");
+
2135 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2136 }
+
2137
+
2138 {
+
2139 Json::Value params;
+
2140 // "field":false
+
2141 params[jss::ledger_index] = jss::validated;
+
2142 params[field] = false;
+
2143 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2144 checkResult(false, jv, expectedType, "invalidParams");
+
2145 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2146 }
+
2147
+
2148 {
+
2149 Json::Value params;
+
2150
+
2151 // "field":[incorrect index hash]
+
2152 auto const badKey = strHex(expectedKey.key + uint256{1});
+
2153 params[jss::ledger_index] = jss::validated;
+
2154 params[field] = badKey;
+
2155 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2156 checkResult(false, jv, expectedType, "entryNotFound");
+
2157 BEAST_EXPECTS(jv[jss::result][jss::index] == badKey, to_string(jv));
+
2158 }
+
2159
+
2160 {
+
2161 Json::Value params;
+
2162 // "index":"field" using API 2
+
2163 params[jss::ledger_index] = jss::validated;
+
2164 params[jss::index] = field;
+
2165 params[jss::api_version] = 2;
+
2166 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2167 checkResult(false, jv, expectedType, "malformedRequest");
+
2168 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2169 }
+
2170
+
2171 std::string const pdIdx = [&]() {
+
2172 {
+
2173 Json::Value params;
+
2174 // Test good values
+
2175 // Use the "field":true notation
+
2176 params[jss::ledger_index] = jss::validated;
+
2177 params[field] = true;
+
2178 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2179 // Index will always be returned for valid parameters.
+
2180 std::string const pdIdx = jv[jss::result][jss::index].asString();
+
2181 BEAST_EXPECTS(hexKey == pdIdx, to_string(jv));
+
2182 checkResult(good, jv, expectedType);
+
2183
+
2184 return pdIdx;
+
2185 }
+
2186 }();
+
2187
+
2188 {
+
2189 Json::Value params;
+
2190 // "field":"[index hash]"
+
2191 params[jss::ledger_index] = jss::validated;
+
2192 params[field] = hexKey;
+
2193 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2194 checkResult(good, jv, expectedType);
+
2195 BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
+
2196 }
+
2197
+
2198 {
+
2199 // Bad value
+
2200 // Use the "index":"field" notation with API 2
+
2201 Json::Value params;
+
2202 params[jss::ledger_index] = jss::validated;
+
2203 params[jss::index] = field;
+
2204 params[jss::api_version] = 2;
+
2205 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2206 checkResult(false, jv, expectedType, "malformedRequest");
+
2207 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2208 }
+
2209
+
2210 {
+
2211 Json::Value params;
+
2212 // Use the "index":"field" notation with API 3
+
2213 params[jss::ledger_index] = jss::validated;
+
2214 params[jss::index] = field;
+
2215 params[jss::api_version] = 3;
+
2216 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2217 // Index is correct either way
+
2218 BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
+
2219 checkResult(good, jv, expectedType);
+
2220 }
+
2221
+
2222 {
+
2223 Json::Value params;
+
2224 // Use the "index":"[index hash]" notation
+
2225 params[jss::ledger_index] = jss::validated;
+
2226 params[jss::index] = pdIdx;
+
2227 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2228 // Index is correct either way
+
2229 BEAST_EXPECT(jv[jss::result][jss::index].asString() == hexKey);
+
2230 checkResult(good, jv, expectedType);
+
2231 }
+
2232 };
+
2233
+
2234 test(jss::amendments, jss::Amendments, keylet::amendments(), true);
+
2235 test(jss::fee, jss::FeeSettings, keylet::fees(), true);
+
2236 // There won't be an nunl
+
2237 test(jss::nunl, jss::NegativeUNL, keylet::negativeUNL(), false);
+
2238 // Can only get the short skip list this way
+
2239 test(jss::hashes, jss::LedgerHashes, keylet::skip(), true);
+
2240 }
-
2234
-
2235 void
-
- -
2237 {
-
2238 using namespace test::jtx;
-
2239
-
2240 Account const alice{"alice"};
-
2241 Account const bob{"bob"};
-
2242
-
2243 Env env{*this, envconfig([](auto cfg) {
-
2244 cfg->START_UP = StartUpType::FRESH;
-
2245 return cfg;
-
2246 })};
-
2247
-
2248 env.close();
+
2241
+
2242 void
+
+ +
2244 {
+
2245 using namespace test::jtx;
+
2246
+
2247 Account const alice{"alice"};
+
2248 Account const bob{"bob"};
2249
-
2260 auto checkResult = [&](bool good,
-
2261 Json::Value const& jv,
-
2262 int expectedCount,
-
2263 std::optional<std::string> const& expectedError = {}) {
-
2264 if (good)
-
2265 {
-
2266 BEAST_EXPECTS(
-
2267 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
-
2268 jv[jss::result].isMember(jss::node) &&
-
2269 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
-
2270 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::LedgerHashes,
-
2271 to_string(jv));
-
2272 BEAST_EXPECTS(
-
2273 jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember("Hashes") &&
-
2274 jv[jss::result][jss::node]["Hashes"].size() == expectedCount,
-
2275 to_string(jv[jss::result][jss::node]["Hashes"].size()));
-
2276 }
-
2277 else
-
2278 {
+
2250 Env env{*this, envconfig([](auto cfg) {
+
2251 cfg->START_UP = StartUpType::FRESH;
+
2252 return cfg;
+
2253 })};
+
2254
+
2255 env.close();
+
2256
+
2267 auto checkResult = [&](bool good,
+
2268 Json::Value const& jv,
+
2269 int expectedCount,
+
2270 std::optional<std::string> const& expectedError = {}) {
+
2271 if (good)
+
2272 {
+
2273 BEAST_EXPECTS(
+
2274 jv.isObject() && jv.isMember(jss::result) && !jv[jss::result].isMember(jss::error) &&
+
2275 jv[jss::result].isMember(jss::node) &&
+
2276 jv[jss::result][jss::node].isMember(sfLedgerEntryType.jsonName) &&
+
2277 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] == jss::LedgerHashes,
+
2278 to_string(jv));
2279 BEAST_EXPECTS(
-
2280 jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) &&
-
2281 !jv[jss::result].isMember(jss::node) &&
-
2282 jv[jss::result][jss::error] == expectedError.value_or("entryNotFound"),
-
2283 to_string(jv));
-
2284 }
-
2285 };
-
2286
-
2297 auto test = [&](Json::Value ledger, Keylet const& expectedKey, bool good, int expectedCount = 0) {
-
2298 testcase << "LedgerHashes: seq: " << env.current()->header().seq << " \"hashes\":" << to_string(ledger)
-
2299 << (good ? "" : " not") << " found";
-
2300
-
2301 auto const hexKey = strHex(expectedKey.key);
-
2302
-
2303 {
-
2304 // Test bad values
-
2305 // "hashes":null
-
2306 Json::Value params;
-
2307 params[jss::ledger_index] = jss::validated;
-
2308 params[jss::hashes] = Json::nullValue;
-
2309 auto jv = env.rpc("json", "ledger_entry", to_string(params));
-
2310 checkResult(false, jv, 0, "malformedRequest");
-
2311 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2312 }
-
2313
-
2314 {
-
2315 Json::Value params;
-
2316 // "hashes":"non-uint string"
-
2317 params[jss::ledger_index] = jss::validated;
-
2318 params[jss::hashes] = "arbitrary string";
-
2319 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2320 checkResult(false, jv, 0, "malformedRequest");
-
2321 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2322 }
-
2323
-
2324 {
-
2325 Json::Value params;
-
2326 // "hashes":"uint string" is invalid, too
-
2327 params[jss::ledger_index] = jss::validated;
-
2328 params[jss::hashes] = "10";
-
2329 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2330 checkResult(false, jv, 0, "malformedRequest");
-
2331 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2332 }
-
2333
-
2334 {
-
2335 Json::Value params;
-
2336 // "hashes":false
-
2337 params[jss::ledger_index] = jss::validated;
-
2338 params[jss::hashes] = false;
-
2339 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2340 checkResult(false, jv, 0, "invalidParams");
-
2341 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2342 }
-
2343
-
2344 {
-
2345 Json::Value params;
-
2346 // "hashes":-1
-
2347 params[jss::ledger_index] = jss::validated;
-
2348 params[jss::hashes] = -1;
-
2349 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2350 checkResult(false, jv, 0, "internal");
-
2351 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
-
2352 }
-
2353
-
2354 // "hashes":[incorrect index hash]
-
2355 {
-
2356 Json::Value params;
-
2357 auto const badKey = strHex(expectedKey.key + uint256{1});
-
2358 params[jss::ledger_index] = jss::validated;
-
2359 params[jss::hashes] = badKey;
-
2360 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2361 checkResult(false, jv, 0, "entryNotFound");
-
2362 BEAST_EXPECT(jv[jss::result][jss::index] == badKey);
-
2363 }
-
2364
-
2365 {
-
2366 Json::Value params;
-
2367 // Test good values
-
2368 // Use the "hashes":ledger notation
-
2369 params[jss::ledger_index] = jss::validated;
-
2370 params[jss::hashes] = ledger;
-
2371 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2372 checkResult(good, jv, expectedCount);
-
2373 // Index will always be returned for valid parameters.
-
2374 std::string const pdIdx = jv[jss::result][jss::index].asString();
-
2375 BEAST_EXPECTS(hexKey == pdIdx, strHex(pdIdx));
-
2376 }
-
2377
-
2378 {
-
2379 Json::Value params;
-
2380 // "hashes":"[index hash]"
-
2381 params[jss::ledger_index] = jss::validated;
-
2382 params[jss::hashes] = hexKey;
-
2383 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2384 checkResult(good, jv, expectedCount);
-
2385 // Index is correct either way
-
2386 BEAST_EXPECTS(
-
2387 hexKey == jv[jss::result][jss::index].asString(), strHex(jv[jss::result][jss::index].asString()));
-
2388 }
-
2389
-
2390 {
-
2391 Json::Value params;
-
2392 // Use the "index":"[index hash]" notation
-
2393 params[jss::ledger_index] = jss::validated;
-
2394 params[jss::index] = hexKey;
-
2395 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
-
2396 checkResult(good, jv, expectedCount);
-
2397 // Index is correct either way
-
2398 BEAST_EXPECTS(
-
2399 hexKey == jv[jss::result][jss::index].asString(), strHex(jv[jss::result][jss::index].asString()));
-
2400 }
-
2401 };
-
2402
-
2403 // short skip list
-
2404 test(true, keylet::skip(), true, 2);
-
2405 // long skip list at index 0
-
2406 test(1, keylet::skip(1), false);
-
2407 // long skip list at index 1
-
2408 test(1 << 17, keylet::skip(1 << 17), false);
+
2280 jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember("Hashes") &&
+
2281 jv[jss::result][jss::node]["Hashes"].size() == expectedCount,
+
2282 to_string(jv[jss::result][jss::node]["Hashes"].size()));
+
2283 }
+
2284 else
+
2285 {
+
2286 BEAST_EXPECTS(
+
2287 jv.isObject() && jv.isMember(jss::result) && jv[jss::result].isMember(jss::error) &&
+
2288 !jv[jss::result].isMember(jss::node) &&
+
2289 jv[jss::result][jss::error] == expectedError.value_or("entryNotFound"),
+
2290 to_string(jv));
+
2291 }
+
2292 };
+
2293
+
2304 auto test = [&](Json::Value ledger, Keylet const& expectedKey, bool good, int expectedCount = 0) {
+
2305 testcase << "LedgerHashes: seq: " << env.current()->header().seq << " \"hashes\":" << to_string(ledger)
+
2306 << (good ? "" : " not") << " found";
+
2307
+
2308 auto const hexKey = strHex(expectedKey.key);
+
2309
+
2310 {
+
2311 // Test bad values
+
2312 // "hashes":null
+
2313 Json::Value params;
+
2314 params[jss::ledger_index] = jss::validated;
+
2315 params[jss::hashes] = Json::nullValue;
+
2316 auto jv = env.rpc("json", "ledger_entry", to_string(params));
+
2317 checkResult(false, jv, 0, "malformedRequest");
+
2318 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2319 }
+
2320
+
2321 {
+
2322 Json::Value params;
+
2323 // "hashes":"non-uint string"
+
2324 params[jss::ledger_index] = jss::validated;
+
2325 params[jss::hashes] = "arbitrary string";
+
2326 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2327 checkResult(false, jv, 0, "malformedRequest");
+
2328 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2329 }
+
2330
+
2331 {
+
2332 Json::Value params;
+
2333 // "hashes":"uint string" is invalid, too
+
2334 params[jss::ledger_index] = jss::validated;
+
2335 params[jss::hashes] = "10";
+
2336 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2337 checkResult(false, jv, 0, "malformedRequest");
+
2338 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2339 }
+
2340
+
2341 {
+
2342 Json::Value params;
+
2343 // "hashes":false
+
2344 params[jss::ledger_index] = jss::validated;
+
2345 params[jss::hashes] = false;
+
2346 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2347 checkResult(false, jv, 0, "invalidParams");
+
2348 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2349 }
+
2350
+
2351 {
+
2352 Json::Value params;
+
2353 // "hashes":-1
+
2354 params[jss::ledger_index] = jss::validated;
+
2355 params[jss::hashes] = -1;
+
2356 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2357 checkResult(false, jv, 0, "internal");
+
2358 BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
+
2359 }
+
2360
+
2361 // "hashes":[incorrect index hash]
+
2362 {
+
2363 Json::Value params;
+
2364 auto const badKey = strHex(expectedKey.key + uint256{1});
+
2365 params[jss::ledger_index] = jss::validated;
+
2366 params[jss::hashes] = badKey;
+
2367 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2368 checkResult(false, jv, 0, "entryNotFound");
+
2369 BEAST_EXPECT(jv[jss::result][jss::index] == badKey);
+
2370 }
+
2371
+
2372 {
+
2373 Json::Value params;
+
2374 // Test good values
+
2375 // Use the "hashes":ledger notation
+
2376 params[jss::ledger_index] = jss::validated;
+
2377 params[jss::hashes] = ledger;
+
2378 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2379 checkResult(good, jv, expectedCount);
+
2380 // Index will always be returned for valid parameters.
+
2381 std::string const pdIdx = jv[jss::result][jss::index].asString();
+
2382 BEAST_EXPECTS(hexKey == pdIdx, strHex(pdIdx));
+
2383 }
+
2384
+
2385 {
+
2386 Json::Value params;
+
2387 // "hashes":"[index hash]"
+
2388 params[jss::ledger_index] = jss::validated;
+
2389 params[jss::hashes] = hexKey;
+
2390 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2391 checkResult(good, jv, expectedCount);
+
2392 // Index is correct either way
+
2393 BEAST_EXPECTS(
+
2394 hexKey == jv[jss::result][jss::index].asString(), strHex(jv[jss::result][jss::index].asString()));
+
2395 }
+
2396
+
2397 {
+
2398 Json::Value params;
+
2399 // Use the "index":"[index hash]" notation
+
2400 params[jss::ledger_index] = jss::validated;
+
2401 params[jss::index] = hexKey;
+
2402 auto const jv = env.rpc("json", "ledger_entry", to_string(params));
+
2403 checkResult(good, jv, expectedCount);
+
2404 // Index is correct either way
+
2405 BEAST_EXPECTS(
+
2406 hexKey == jv[jss::result][jss::index].asString(), strHex(jv[jss::result][jss::index].asString()));
+
2407 }
+
2408 };
2409
-
2410 // Close more ledgers, but stop short of the flag ledger
-
2411 for (auto i = env.current()->seq(); i <= 250; ++i)
-
2412 env.close();
-
2413
-
2414 // short skip list
-
2415 test(true, keylet::skip(), true, 249);
-
2416 // long skip list at index 0
-
2417 test(1, keylet::skip(1), false);
-
2418 // long skip list at index 1
-
2419 test(1 << 17, keylet::skip(1 << 17), false);
+
2410 // short skip list
+
2411 test(true, keylet::skip(), true, 2);
+
2412 // long skip list at index 0
+
2413 test(1, keylet::skip(1), false);
+
2414 // long skip list at index 1
+
2415 test(1 << 17, keylet::skip(1 << 17), false);
+
2416
+
2417 // Close more ledgers, but stop short of the flag ledger
+
2418 for (auto i = env.current()->seq(); i <= 250; ++i)
+
2419 env.close();
2420
-
2421 // Close a flag ledger so the first "long" skip list is created
-
2422 for (auto i = env.current()->seq(); i <= 260; ++i)
-
2423 env.close();
-
2424
-
2425 // short skip list
-
2426 test(true, keylet::skip(), true, 256);
-
2427 // long skip list at index 0
-
2428 test(1, keylet::skip(1), true, 1);
-
2429 // long skip list at index 1
-
2430 test(1 << 17, keylet::skip(1 << 17), false);
-
2431 }
+
2421 // short skip list
+
2422 test(true, keylet::skip(), true, 249);
+
2423 // long skip list at index 0
+
2424 test(1, keylet::skip(1), false);
+
2425 // long skip list at index 1
+
2426 test(1 << 17, keylet::skip(1 << 17), false);
+
2427
+
2428 // Close a flag ledger so the first "long" skip list is created
+
2429 for (auto i = env.current()->seq(); i <= 260; ++i)
+
2430 env.close();
+
2431
+
2432 // short skip list
+
2433 test(true, keylet::skip(), true, 256);
+
2434 // long skip list at index 0
+
2435 test(1, keylet::skip(1), true, 1);
+
2436 // long skip list at index 1
+
2437 test(1 << 17, keylet::skip(1 << 17), false);
+
2438 }
-
2432
-
2433 void
-
- -
2435 {
-
2436 testcase("command-line");
-
2437 using namespace test::jtx;
-
2438
-
2439 Env env{*this};
-
2440 Account const alice{"alice"};
-
2441 env.fund(XRP(10000), alice);
-
2442 env.close();
-
2443
-
2444 auto const checkId = keylet::check(env.master, env.seq(env.master));
+
2439
+
2440 void
+
+ +
2442 {
+
2443 testcase("command-line");
+
2444 using namespace test::jtx;
2445
-
2446 env(check::create(env.master, alice, XRP(100)));
-
2447 env.close();
-
2448
-
2449 std::string const ledgerHash{to_string(env.closed()->header().hash)};
-
2450 {
-
2451 // Request a check.
-
2452 Json::Value const jrr = env.rpc("ledger_entry", to_string(checkId.key))[jss::result];
-
2453 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
-
2454 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
-
2455 }
-
2456 }
+
2446 Env env{*this};
+
2447 Account const alice{"alice"};
+
2448 env.fund(XRP(10000), alice);
+
2449 env.close();
+
2450
+
2451 auto const checkId = keylet::check(env.master, env.seq(env.master));
+
2452
+
2453 env(check::create(env.master, alice, XRP(100)));
+
2454 env.close();
+
2455
+
2456 std::string const ledgerHash{to_string(env.closed()->header().hash)};
+
2457 {
+
2458 // Request a check.
+
2459 Json::Value const jrr = env.rpc("ledger_entry", to_string(checkId.key))[jss::result];
+
2460 BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
+
2461 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
+
2462 }
+
2463 }
-
2457
-
2458public:
-
2459 void
-
-
2460 run() override
-
2461 {
-
2462 testInvalid();
- - -
2465 testAMM();
-
2466 testCheck();
- -
2468 testDelegate();
- - -
2471 testDirectory();
-
2472 testEscrow();
- - - - - -
2478 testOffer();
-
2479 testPayChan();
- - -
2482 testTicket();
-
2483 testDID();
- - -
2486 testMPT();
- -
2488 testFixed();
-
2489 testHashes();
-
2490 testCLI();
-
2491 }
+
2464
+
2465public:
+
2466 void
+
+
2467 run() override
+
2468 {
+
2469 testInvalid();
+ + +
2472 testAMM();
+
2473 testCheck();
+ +
2475 testDelegate();
+ + +
2478 testDirectory();
+
2479 testEscrow();
+ + + + + +
2485 testOffer();
+
2486 testPayChan();
+ + +
2489 testTicket();
+
2490 testDID();
+ + +
2493 testMPT();
+ +
2495 testFixed();
+
2496 testHashes();
+
2497 testCLI();
+
2498 }
-
2492};
+
2499};
-
2493
-
- -
2495{
-
2496 void
-
-
2497 checkErrorValue(Json::Value const& jv, std::string const& err, std::string const& msg)
-
2498 {
-
2499 if (BEAST_EXPECT(jv.isMember(jss::status)))
-
2500 BEAST_EXPECT(jv[jss::status] == "error");
-
2501 if (BEAST_EXPECT(jv.isMember(jss::error)))
-
2502 BEAST_EXPECT(jv[jss::error] == err);
-
2503 if (msg.empty())
-
2504 {
-
2505 BEAST_EXPECT(jv[jss::error_message] == Json::nullValue || jv[jss::error_message] == "");
-
2506 }
-
2507 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
-
2508 BEAST_EXPECT(jv[jss::error_message] == msg);
-
2509 }
+
2500
+
+ +
2502{
+
2503 void
+
+
2504 checkErrorValue(Json::Value const& jv, std::string const& err, std::string const& msg)
+
2505 {
+
2506 if (BEAST_EXPECT(jv.isMember(jss::status)))
+
2507 BEAST_EXPECT(jv[jss::status] == "error");
+
2508 if (BEAST_EXPECT(jv.isMember(jss::error)))
+
2509 BEAST_EXPECT(jv[jss::error] == err);
+
2510 if (msg.empty())
+
2511 {
+
2512 BEAST_EXPECT(jv[jss::error_message] == Json::nullValue || jv[jss::error_message] == "");
+
2513 }
+
2514 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
+
2515 BEAST_EXPECT(jv[jss::error_message] == msg);
+
2516 }
-
2510
-
2511 void
-
- -
2513 {
-
2514 testcase("ledger_entry: bridge");
-
2515 using namespace test::jtx;
-
2516
-
2517 Env mcEnv{*this, features};
-
2518 Env scEnv(*this, envconfig(), features);
-
2519
-
2520 createBridgeObjects(mcEnv, scEnv);
-
2521
-
2522 std::string const ledgerHash{to_string(mcEnv.closed()->header().hash)};
-
2523 std::string bridge_index;
-
2524 Json::Value mcBridge;
-
2525 {
-
2526 // request the bridge via RPC
-
2527 Json::Value jvParams;
-
2528 jvParams[jss::bridge_account] = mcDoor.human();
-
2529 jvParams[jss::bridge] = jvb;
-
2530 Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
2531
-
2532 BEAST_EXPECT(jrr.isMember(jss::node));
-
2533 auto r = jrr[jss::node];
-
2534
-
2535 BEAST_EXPECT(r.isMember(jss::Account));
-
2536 BEAST_EXPECT(r[jss::Account] == mcDoor.human());
-
2537
-
2538 BEAST_EXPECT(r.isMember(jss::Flags));
-
2539
-
2540 BEAST_EXPECT(r.isMember(sfLedgerEntryType.jsonName));
-
2541 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::Bridge);
-
2542
-
2543 // we not created an account yet
-
2544 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
-
2545 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 0);
+
2517
+
2518 void
+
+ +
2520 {
+
2521 testcase("ledger_entry: bridge");
+
2522 using namespace test::jtx;
+
2523
+
2524 Env mcEnv{*this, features};
+
2525 Env scEnv(*this, envconfig(), features);
+
2526
+
2527 createBridgeObjects(mcEnv, scEnv);
+
2528
+
2529 std::string const ledgerHash{to_string(mcEnv.closed()->header().hash)};
+
2530 std::string bridge_index;
+
2531 Json::Value mcBridge;
+
2532 {
+
2533 // request the bridge via RPC
+
2534 Json::Value jvParams;
+
2535 jvParams[jss::bridge_account] = mcDoor.human();
+
2536 jvParams[jss::bridge] = jvb;
+
2537 Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2538
+
2539 BEAST_EXPECT(jrr.isMember(jss::node));
+
2540 auto r = jrr[jss::node];
+
2541
+
2542 BEAST_EXPECT(r.isMember(jss::Account));
+
2543 BEAST_EXPECT(r[jss::Account] == mcDoor.human());
+
2544
+
2545 BEAST_EXPECT(r.isMember(jss::Flags));
2546
-
2547 // we have not claimed a locking chain tx yet
-
2548 BEAST_EXPECT(r.isMember(sfXChainAccountClaimCount.jsonName));
-
2549 BEAST_EXPECT(r[sfXChainAccountClaimCount.jsonName].asInt() == 0);
-
2550
-
2551 BEAST_EXPECT(r.isMember(jss::index));
-
2552 bridge_index = r[jss::index].asString();
-
2553 mcBridge = r;
-
2554 }
-
2555 {
-
2556 // request the bridge via RPC by index
-
2557 Json::Value jvParams;
-
2558 jvParams[jss::index] = bridge_index;
-
2559 Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
2560
-
2561 BEAST_EXPECT(jrr.isMember(jss::node));
-
2562 BEAST_EXPECT(jrr[jss::node] == mcBridge);
-
2563 }
-
2564 {
-
2565 // swap door accounts and make sure we get an error value
-
2566 Json::Value jvParams;
-
2567 // Sidechain door account is "master", not scDoor
-
2568 jvParams[jss::bridge_account] = Account::master.human();
-
2569 jvParams[jss::bridge] = jvb;
-
2570 jvParams[jss::ledger_hash] = ledgerHash;
-
2571 Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
2572
-
2573 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
-
2574 }
-
2575 {
-
2576 // create two claim ids and verify that the bridge counter was
-
2577 // incremented
- -
2579 mcEnv.close();
- -
2581 mcEnv.close();
-
2582
-
2583 // request the bridge via RPC
-
2584 Json::Value jvParams;
-
2585 jvParams[jss::bridge_account] = mcDoor.human();
-
2586 jvParams[jss::bridge] = jvb;
-
2587 Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
2588
-
2589 BEAST_EXPECT(jrr.isMember(jss::node));
-
2590 auto r = jrr[jss::node];
-
2591
-
2592 // we executed two create claim id txs
-
2593 BEAST_EXPECT(r.isMember(sfXChainClaimID.jsonName));
-
2594 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
-
2595 }
-
2596 }
+
2547 BEAST_EXPECT(r.isMember(sfLedgerEntryType.jsonName));
+
2548 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::Bridge);
+
2549
+
2550 // we not created an account yet
+
2551 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
+
2552 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 0);
+
2553
+
2554 // we have not claimed a locking chain tx yet
+
2555 BEAST_EXPECT(r.isMember(sfXChainAccountClaimCount.jsonName));
+
2556 BEAST_EXPECT(r[sfXChainAccountClaimCount.jsonName].asInt() == 0);
+
2557
+
2558 BEAST_EXPECT(r.isMember(jss::index));
+
2559 bridge_index = r[jss::index].asString();
+
2560 mcBridge = r;
+
2561 }
+
2562 {
+
2563 // request the bridge via RPC by index
+
2564 Json::Value jvParams;
+
2565 jvParams[jss::index] = bridge_index;
+
2566 Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2567
+
2568 BEAST_EXPECT(jrr.isMember(jss::node));
+
2569 BEAST_EXPECT(jrr[jss::node] == mcBridge);
+
2570 }
+
2571 {
+
2572 // swap door accounts and make sure we get an error value
+
2573 Json::Value jvParams;
+
2574 // Sidechain door account is "master", not scDoor
+
2575 jvParams[jss::bridge_account] = Account::master.human();
+
2576 jvParams[jss::bridge] = jvb;
+
2577 jvParams[jss::ledger_hash] = ledgerHash;
+
2578 Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2579
+
2580 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
+
2581 }
+
2582 {
+
2583 // create two claim ids and verify that the bridge counter was
+
2584 // incremented
+ +
2586 mcEnv.close();
+ +
2588 mcEnv.close();
+
2589
+
2590 // request the bridge via RPC
+
2591 Json::Value jvParams;
+
2592 jvParams[jss::bridge_account] = mcDoor.human();
+
2593 jvParams[jss::bridge] = jvb;
+
2594 Json::Value const jrr = mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2595
+
2596 BEAST_EXPECT(jrr.isMember(jss::node));
+
2597 auto r = jrr[jss::node];
+
2598
+
2599 // we executed two create claim id txs
+
2600 BEAST_EXPECT(r.isMember(sfXChainClaimID.jsonName));
+
2601 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
+
2602 }
+
2603 }
-
2597
-
2598 void
-
- -
2600 {
-
2601 testcase("ledger_entry: xchain_claim_id");
-
2602 using namespace test::jtx;
-
2603
-
2604 Env mcEnv{*this, features};
-
2605 Env scEnv(*this, envconfig(), features);
-
2606
-
2607 createBridgeObjects(mcEnv, scEnv);
-
2608
- -
2610 scEnv.close();
- -
2612 scEnv.close();
+
2604
+
2605 void
+
+ +
2607 {
+
2608 testcase("ledger_entry: xchain_claim_id");
+
2609 using namespace test::jtx;
+
2610
+
2611 Env mcEnv{*this, features};
+
2612 Env scEnv(*this, envconfig(), features);
2613
-
2614 std::string bridge_index;
-
2615 {
-
2616 // request the xchain_claim_id via RPC
-
2617 Json::Value jvParams;
-
2618 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
-
2619 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] = 1;
-
2620 Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
2621
-
2622 BEAST_EXPECT(jrr.isMember(jss::node));
-
2623 auto r = jrr[jss::node];
-
2624
-
2625 BEAST_EXPECT(r.isMember(jss::Account));
-
2626 BEAST_EXPECT(r[jss::Account] == scAlice.human());
-
2627 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
-
2628 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 1);
-
2629 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
-
2630 }
+
2614 createBridgeObjects(mcEnv, scEnv);
+
2615
+ +
2617 scEnv.close();
+ +
2619 scEnv.close();
+
2620
+
2621 std::string bridge_index;
+
2622 {
+
2623 // request the xchain_claim_id via RPC
+
2624 Json::Value jvParams;
+
2625 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
+
2626 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] = 1;
+
2627 Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2628
+
2629 BEAST_EXPECT(jrr.isMember(jss::node));
+
2630 auto r = jrr[jss::node];
2631
-
2632 {
-
2633 // request the xchain_claim_id via RPC
-
2634 Json::Value jvParams;
-
2635 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
-
2636 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] = 2;
-
2637 Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2632 BEAST_EXPECT(r.isMember(jss::Account));
+
2633 BEAST_EXPECT(r[jss::Account] == scAlice.human());
+
2634 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
+
2635 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 1);
+
2636 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
+
2637 }
2638
-
2639 BEAST_EXPECT(jrr.isMember(jss::node));
-
2640 auto r = jrr[jss::node];
-
2641
-
2642 BEAST_EXPECT(r.isMember(jss::Account));
-
2643 BEAST_EXPECT(r[jss::Account] == scBob.human());
-
2644 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
-
2645 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
-
2646 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
-
2647 }
-
2648 }
+
2639 {
+
2640 // request the xchain_claim_id via RPC
+
2641 Json::Value jvParams;
+
2642 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
+
2643 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] = 2;
+
2644 Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2645
+
2646 BEAST_EXPECT(jrr.isMember(jss::node));
+
2647 auto r = jrr[jss::node];
+
2648
+
2649 BEAST_EXPECT(r.isMember(jss::Account));
+
2650 BEAST_EXPECT(r[jss::Account] == scBob.human());
+
2651 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
+
2652 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
+
2653 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
+
2654 }
+
2655 }
-
2649
-
2650 void
-
- -
2652 {
-
2653 testcase("ledger_entry: xchain_create_account_claim_id");
-
2654 using namespace test::jtx;
-
2655
-
2656 Env mcEnv{*this, features};
-
2657 Env scEnv(*this, envconfig(), features);
-
2658
-
2659 // note: signers.size() and quorum are both 5 in createBridgeObjects
-
2660 createBridgeObjects(mcEnv, scEnv);
-
2661
-
2662 auto scCarol = Account("scCarol"); // Don't fund it - it will be created with the
-
2663 // xchain transaction
-
2664 auto const amt = XRP(1000);
- -
2666 mcEnv.close();
-
2667
-
2668 // send less than quorum of attestations (otherwise funds are
-
2669 // immediately transferred and no "claim" object is created)
-
2670 size_t constexpr num_attest = 3;
-
2671 auto attestations = create_account_attestations(
-
2672 scAttester,
-
2673 jvb,
-
2674 mcAlice,
-
2675 amt,
-
2676 reward,
-
2677 payee,
-
2678 /*wasLockingChainSend*/ true,
-
2679 1,
-
2680 scCarol,
-
2681 signers,
- -
2683 for (size_t i = 0; i < num_attest; ++i)
-
2684 {
-
2685 scEnv(attestations[i]);
-
2686 }
-
2687 scEnv.close();
-
2688
-
2689 {
-
2690 // request the create account claim_id via RPC
-
2691 Json::Value jvParams;
-
2692 jvParams[jss::xchain_owned_create_account_claim_id] = jvXRPBridgeRPC;
-
2693 jvParams[jss::xchain_owned_create_account_claim_id][jss::xchain_owned_create_account_claim_id] = 1;
-
2694 Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2656
+
2657 void
+
+ +
2659 {
+
2660 testcase("ledger_entry: xchain_create_account_claim_id");
+
2661 using namespace test::jtx;
+
2662
+
2663 Env mcEnv{*this, features};
+
2664 Env scEnv(*this, envconfig(), features);
+
2665
+
2666 // note: signers.size() and quorum are both 5 in createBridgeObjects
+
2667 createBridgeObjects(mcEnv, scEnv);
+
2668
+
2669 auto scCarol = Account("scCarol"); // Don't fund it - it will be created with the
+
2670 // xchain transaction
+
2671 auto const amt = XRP(1000);
+ +
2673 mcEnv.close();
+
2674
+
2675 // send less than quorum of attestations (otherwise funds are
+
2676 // immediately transferred and no "claim" object is created)
+
2677 size_t constexpr num_attest = 3;
+
2678 auto attestations = create_account_attestations(
+
2679 scAttester,
+
2680 jvb,
+
2681 mcAlice,
+
2682 amt,
+
2683 reward,
+
2684 payee,
+
2685 /*wasLockingChainSend*/ true,
+
2686 1,
+
2687 scCarol,
+
2688 signers,
+ +
2690 for (size_t i = 0; i < num_attest; ++i)
+
2691 {
+
2692 scEnv(attestations[i]);
+
2693 }
+
2694 scEnv.close();
2695
-
2696 BEAST_EXPECT(jrr.isMember(jss::node));
-
2697 auto r = jrr[jss::node];
-
2698
-
2699 BEAST_EXPECT(r.isMember(jss::Account));
-
2700 BEAST_EXPECT(r[jss::Account] == Account::master.human());
-
2701
-
2702 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
-
2703 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 1);
-
2704
-
2705 BEAST_EXPECT(r.isMember(sfXChainCreateAccountAttestations.jsonName));
-
2706 auto attest = r[sfXChainCreateAccountAttestations.jsonName];
-
2707 BEAST_EXPECT(attest.isArray());
-
2708 BEAST_EXPECT(attest.size() == 3);
-
2709 BEAST_EXPECT(attest[Json::Value::UInt(0)].isMember(sfXChainCreateAccountProofSig.jsonName));
-
2710 Json::Value a[num_attest];
-
2711 for (size_t i = 0; i < num_attest; ++i)
-
2712 {
-
2713 a[i] = attest[Json::Value::UInt(0)][sfXChainCreateAccountProofSig.jsonName];
-
2714 BEAST_EXPECT(a[i].isMember(jss::Amount) && a[i][jss::Amount].asInt() == 1000 * drop_per_xrp);
-
2715 BEAST_EXPECT(a[i].isMember(jss::Destination) && a[i][jss::Destination] == scCarol.human());
-
2716 BEAST_EXPECT(
-
2717 a[i].isMember(sfAttestationSignerAccount.jsonName) &&
-
2718 std::any_of(signers.begin(), signers.end(), [&](signer const& s) {
-
2719 return a[i][sfAttestationSignerAccount.jsonName] == s.account.human();
-
2720 }));
-
2721 BEAST_EXPECT(
-
2722 a[i].isMember(sfAttestationRewardAccount.jsonName) &&
-
2723 std::any_of(payee.begin(), payee.end(), [&](Account const& account) {
-
2724 return a[i][sfAttestationRewardAccount.jsonName] == account.human();
-
2725 }));
-
2726 BEAST_EXPECT(
-
2727 a[i].isMember(sfWasLockingChainSend.jsonName) && a[i][sfWasLockingChainSend.jsonName] == 1);
+
2696 {
+
2697 // request the create account claim_id via RPC
+
2698 Json::Value jvParams;
+
2699 jvParams[jss::xchain_owned_create_account_claim_id] = jvXRPBridgeRPC;
+
2700 jvParams[jss::xchain_owned_create_account_claim_id][jss::xchain_owned_create_account_claim_id] = 1;
+
2701 Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2702
+
2703 BEAST_EXPECT(jrr.isMember(jss::node));
+
2704 auto r = jrr[jss::node];
+
2705
+
2706 BEAST_EXPECT(r.isMember(jss::Account));
+
2707 BEAST_EXPECT(r[jss::Account] == Account::master.human());
+
2708
+
2709 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
+
2710 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 1);
+
2711
+
2712 BEAST_EXPECT(r.isMember(sfXChainCreateAccountAttestations.jsonName));
+
2713 auto attest = r[sfXChainCreateAccountAttestations.jsonName];
+
2714 BEAST_EXPECT(attest.isArray());
+
2715 BEAST_EXPECT(attest.size() == 3);
+
2716 BEAST_EXPECT(attest[Json::Value::UInt(0)].isMember(sfXChainCreateAccountProofSig.jsonName));
+
2717 Json::Value a[num_attest];
+
2718 for (size_t i = 0; i < num_attest; ++i)
+
2719 {
+
2720 a[i] = attest[Json::Value::UInt(0)][sfXChainCreateAccountProofSig.jsonName];
+
2721 BEAST_EXPECT(a[i].isMember(jss::Amount) && a[i][jss::Amount].asInt() == 1000 * drop_per_xrp);
+
2722 BEAST_EXPECT(a[i].isMember(jss::Destination) && a[i][jss::Destination] == scCarol.human());
+
2723 BEAST_EXPECT(
+
2724 a[i].isMember(sfAttestationSignerAccount.jsonName) &&
+
2725 std::any_of(signers.begin(), signers.end(), [&](signer const& s) {
+
2726 return a[i][sfAttestationSignerAccount.jsonName] == s.account.human();
+
2727 }));
2728 BEAST_EXPECT(
-
2729 a[i].isMember(sfSignatureReward.jsonName) &&
-
2730 a[i][sfSignatureReward.jsonName].asInt() == 1 * drop_per_xrp);
-
2731 }
-
2732 }
-
2733
-
2734 // complete attestations quorum - CreateAccountClaimID should not be
-
2735 // present anymore
-
2736 for (size_t i = num_attest; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS; ++i)
-
2737 {
-
2738 scEnv(attestations[i]);
+
2729 a[i].isMember(sfAttestationRewardAccount.jsonName) &&
+
2730 std::any_of(payee.begin(), payee.end(), [&](Account const& account) {
+
2731 return a[i][sfAttestationRewardAccount.jsonName] == account.human();
+
2732 }));
+
2733 BEAST_EXPECT(
+
2734 a[i].isMember(sfWasLockingChainSend.jsonName) && a[i][sfWasLockingChainSend.jsonName] == 1);
+
2735 BEAST_EXPECT(
+
2736 a[i].isMember(sfSignatureReward.jsonName) &&
+
2737 a[i][sfSignatureReward.jsonName].asInt() == 1 * drop_per_xrp);
+
2738 }
2739 }
-
2740 scEnv.close();
-
2741 {
-
2742 // request the create account claim_id via RPC
-
2743 Json::Value jvParams;
-
2744 jvParams[jss::xchain_owned_create_account_claim_id] = jvXRPBridgeRPC;
-
2745 jvParams[jss::xchain_owned_create_account_claim_id][jss::xchain_owned_create_account_claim_id] = 1;
-
2746 Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
-
2747 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
-
2748 }
-
2749 }
+
2740
+
2741 // complete attestations quorum - CreateAccountClaimID should not be
+
2742 // present anymore
+
2743 for (size_t i = num_attest; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS; ++i)
+
2744 {
+
2745 scEnv(attestations[i]);
+
2746 }
+
2747 scEnv.close();
+
2748 {
+
2749 // request the create account claim_id via RPC
+
2750 Json::Value jvParams;
+
2751 jvParams[jss::xchain_owned_create_account_claim_id] = jvXRPBridgeRPC;
+
2752 jvParams[jss::xchain_owned_create_account_claim_id][jss::xchain_owned_create_account_claim_id] = 1;
+
2753 Json::Value const jrr = scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
+
2754 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
+
2755 }
+
2756 }
-
2750
-
2751public:
-
2752 void
-
-
2753 run() override
-
2754 {
-
2755 testBridge();
-
2756 testClaimID();
- -
2758 }
+
2757
+
2758public:
+
2759 void
+
+
2760 run() override
+
2761 {
+
2762 testBridge();
+
2763 testClaimID();
+ +
2765 }
-
2759};
+
2766};
-
2760
-
2761BEAST_DEFINE_TESTSUITE(LedgerEntry, rpc, xrpl);
-
2762BEAST_DEFINE_TESTSUITE(LedgerEntry_XChain, rpc, xrpl);
-
2763
-
2764} // namespace test
-
2765} // namespace xrpl
+
2767
+
2768BEAST_DEFINE_TESTSUITE(LedgerEntry, rpc, xrpl);
+
2769BEAST_DEFINE_TESTSUITE(LedgerEntry_XChain, rpc, xrpl);
+
2770
+
2771} // namespace test
+
2772} // namespace xrpl
T any_of(T... args)
@@ -2912,16 +2919,16 @@ $(document).ready(function() { init_codefold(0); });
constexpr char const * c_str() const
Definition json_value.h:57
Represents a JSON value.
Definition json_value.h:130
Json::UInt UInt
Definition json_value.h:137
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
std::string toStyledString() const
-
void clear()
Remove all object members and array elements.
+
std::string toStyledString() const
+
void clear()
Remove all object members and array elements.
Int asInt() const
-
bool isObject() const
-
Value removeMember(char const *key)
Remove and return the named member.
+
bool isObject() const
+
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
-
bool isNull() const
isNull() tests to see if this field is null.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isNull() const
isNull() tests to see if this field is null.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -2938,49 +2945,49 @@ $(document).ready(function() { init_codefold(0); });
virtual OpenLedger & openLedger()=0
static base_uint fromVoid(void const *data)
Definition base_uint.h:291
- - -
void run() override
Runs the suite.
-
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
- - + + +
void run() override
Runs the suite.
+
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
+ + - +
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg, std::source_location const location=std::source_location::current())
- - + +
void testMalformedSubfield(test::jtx::Env &env, Json::Value correctRequest, Json::StaticString parentFieldName, Json::StaticString fieldName, FieldType typeID, std::string const &expectedError, bool required=true, std::source_location const location=std::source_location::current())
- - - -
void run() override
Runs the suite.
- - + + + +
void run() override
Runs the suite.
+ +
void testMalformedField(test::jtx::Env &env, Json::Value correctRequest, Json::StaticString const fieldName, FieldType const typeID, std::string const &expectedError, bool required=true, std::source_location const location=std::source_location::current())
- - - - + + + +
void runLedgerEntryTest(test::jtx::Env &env, Json::StaticString const &parentField, std::source_location const location=std::source_location::current())
- +
void runLedgerEntryTest(test::jtx::Env &env, Json::StaticString const &parentField, std::vector< Subfield > const &subfields, std::source_location const location=std::source_location::current())
- -
void testFixed()
Test the ledger entry types that don't take parameters.
- + +
void testFixed()
Test the ledger entry types that don't take parameters.
+ - - - - + + + +
Json::Value getCorrectValue(Json::StaticString fieldName)
- +
std::vector< Json::Value > getBadValues(FieldType fieldType)
- - + + - - - + + +
Convenience class to test AMM functionality.
Definition AMM.h:104
Immutable cryptographic account descriptor.
Definition Account.h:19
@@ -3000,7 +3007,7 @@ $(document).ready(function() { init_codefold(0); });
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:319
NetClock::time_point now()
Returns the current network time.
Definition Env.h:274
-
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:126
+
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:127
Oracle class facilitates unit-testing of the Price Oracle feature.
Definition Oracle.h:95
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
Sets the optional Destination field on an NFTokenOffer.
Definition token.h:140
@@ -3044,7 +3051,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value sidechain_xchain_account_create(Account const &acc, Json::Value const &bridge, Account const &dst, AnyAmount const &amt, AnyAmount const &reward)
constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
require_t required(Args const &... args)
Compose many condition functors into one.
Definition require.h:29
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
JValueVec create_account_attestations(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::AnyAmount const &rewardAmount, std::vector< jtx::Account > const &rewardAccounts, bool wasLockingChainSend, std::uint64_t createCount, jtx::Account const &dst, std::vector< jtx::signer > const &signers, std::size_t const numAtts, std::size_t const fromIdx)
@@ -3099,7 +3106,7 @@ $(document).ready(function() { init_codefold(0); });
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
uint256 key
Definition Keylet.h:20
- + diff --git a/LedgerHandler_8cpp_source.html b/LedgerHandler_8cpp_source.html index 8ed8df0945..51f48c8b29 100644 --- a/LedgerHandler_8cpp_source.html +++ b/LedgerHandler_8cpp_source.html @@ -380,8 +380,8 @@ $(document).ready(function() { init_codefold(0); });
290} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value & append(Value const &value)
Append value to array at the end.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
Stream error() const
Definition Journal.h:318
Stream warn() const
Definition Journal.h:312
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
diff --git a/LedgerHistory_8cpp_source.html b/LedgerHistory_8cpp_source.html index 58829c7413..c551615af6 100644 --- a/LedgerHistory_8cpp_source.html +++ b/LedgerHistory_8cpp_source.html @@ -566,7 +566,7 @@ $(document).ready(function() { init_codefold(0); });
459} // namespace xrpl
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
const_iterator begin() const
+
const_iterator begin() const
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:318
Stream debug() const
Definition Journal.h:300
diff --git a/LedgerHistory__test_8cpp_source.html b/LedgerHistory__test_8cpp_source.html index c0cdc551ba..b7495ec93a 100644 --- a/LedgerHistory__test_8cpp_source.html +++ b/LedgerHistory__test_8cpp_source.html @@ -271,7 +271,7 @@ $(document).ready(function() { init_codefold(0); });
T is_same_v
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
ApplyTransactionResult applyTransaction(Application &app, OpenView &view, STTx const &tx, bool retryAssured, ApplyFlags flags, beast::Journal journal)
Transaction application helper.
Definition apply.cpp:191
diff --git a/LedgerMaster_8cpp_source.html b/LedgerMaster_8cpp_source.html index 0273820306..8b4244b456 100644 --- a/LedgerMaster_8cpp_source.html +++ b/LedgerMaster_8cpp_source.html @@ -2313,7 +2313,7 @@ $(document).ready(function() { init_codefold(0); });
virtual LedgerIndex getMaxDisallowedLedger()=0
Ensure that a newly-started validator does not sign proposals older than the last ledger it persisted...
Holds transactions which were deferred to the next pass of consensus.
void insert(std::shared_ptr< STTx const > const &txn)
-
std::shared_ptr< STTx const > popAcctTransaction(std::shared_ptr< STTx const > const &tx)
+
std::shared_ptr< STTx const > popAcctTransaction(std::shared_ptr< STTx const > const &tx)
std::unordered_set< uint256, beast::uhash<> > features
Definition Config.h:257
bool LEDGER_REPLAY
Definition Config.h:204
@@ -2513,9 +2513,9 @@ $(document).ready(function() { init_codefold(0); });
std::vector< WrappedValidationType > getTrustedForLedger(ID const &ledgerID, Seq const &seq)
Get trusted full validations for a specific ledger.
std::vector< WrappedValidationType > currentTrusted()
Get the currently trusted full validations.
std::vector< std::uint32_t > fees(ID const &ledgerID, std::uint32_t baseFee)
Returns fees reported by trusted full validators in the given ledger.
-
QuorumKeys getQuorumKeys() const
Get the quorum and all of the trusted keys.
+
QuorumKeys getQuorumKeys() const
Get the quorum and all of the trusted keys.
std::size_t quorum() const
Get quorum value for current trusted key set.
-
std::vector< std::shared_ptr< STValidation > > negativeUNLFilter(std::vector< std::shared_ptr< STValidation > > &&validations) const
Remove validations that are from validators on the negative UNL.
+
std::vector< std::shared_ptr< STValidation > > negativeUNLFilter(std::vector< std::shared_ptr< STValidation > > &&validations) const
Remove validations that are from validators on the negative UNL.
bool isNonZero() const
Definition base_uint.h:513
Automatically unlocks and re-locks a unique_lock object.
Definition scope.h:197
diff --git a/LedgerMaster__test_8cpp_source.html b/LedgerMaster__test_8cpp_source.html index 89e6894bc6..78f4b6331e 100644 --- a/LedgerMaster__test_8cpp_source.html +++ b/LedgerMaster__test_8cpp_source.html @@ -166,61 +166,63 @@ $(document).ready(function() { init_codefold(0); });
79 auto result = env.app().getLedgerMaster().txnIdFromIndex(startLegSeq, txnIndex);
80 BEAST_EXPECT(
81 *result ==
-
82 uint256("277F4FD89C20B92457FEF05FF63F6405563AD0563C73D967A29727"
-
83 "72679ADC65"));
-
84 }
-
85 // success (second tx)
-
86 {
-
87 uint32_t txnIndex = metas[1]->getFieldU32(sfTransactionIndex);
-
88 auto result = env.app().getLedgerMaster().txnIdFromIndex(startLegSeq + 1, txnIndex);
-
89 BEAST_EXPECT(
-
90 *result ==
-
91 uint256("293DF7335EBBAF4420D52E70ABF470EB4C5792CAEA2F91F76193C2"
-
92 "819F538FDE"));
-
93 }
-
94 }
+
82 uint256(
+
83 "277F4FD89C20B92457FEF05FF63F6405563AD0563C73D967A29727"
+
84 "72679ADC65"));
+
85 }
+
86 // success (second tx)
+
87 {
+
88 uint32_t txnIndex = metas[1]->getFieldU32(sfTransactionIndex);
+
89 auto result = env.app().getLedgerMaster().txnIdFromIndex(startLegSeq + 1, txnIndex);
+
90 BEAST_EXPECT(
+
91 *result ==
+
92 uint256(
+
93 "293DF7335EBBAF4420D52E70ABF470EB4C5792CAEA2F91F76193C2"
+
94 "819F538FDE"));
+
95 }
+
96 }
-
95
-
96public:
-
97 void
-
-
98 run() override
-
99 {
-
100 using namespace test::jtx;
- - -
103 }
+
97
+
98public:
+
99 void
+
+
100 run() override
+
101 {
+
102 using namespace test::jtx;
+ + +
105 }
-
104
-
105 void
-
- -
107 {
-
108 testTxnIdFromIndex(features);
-
109 }
+
106
+
107 void
+
+ +
109 {
+
110 testTxnIdFromIndex(features);
+
111 }
-
110};
+
112};
-
111
-
112BEAST_DEFINE_TESTSUITE(LedgerMaster, app, xrpl);
113
-
114} // namespace test
-
115} // namespace xrpl
+
114BEAST_DEFINE_TESTSUITE(LedgerMaster, app, xrpl);
+
115
+
116} // namespace test
+
117} // namespace xrpl
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
-
void testWithFeats(FeatureBitset features)
+
void testWithFeats(FeatureBitset features)
void testTxnIdFromIndex(FeatureBitset features)
-
void run() override
Runs the suite.
+
void run() override
Runs the suite.
std::unique_ptr< Config > makeNetworkConfig(uint32_t networkID)
Immutable cryptographic account descriptor.
Definition Account.h:19
A transaction testing environment.
Definition Env.h:119
T emplace_back(T... args)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
FeatureBitset testable_amendments()
Definition Env.h:76
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/LedgerRPC__test_8cpp_source.html b/LedgerRPC__test_8cpp_source.html index db797daeaf..9991be31cd 100644 --- a/LedgerRPC__test_8cpp_source.html +++ b/LedgerRPC__test_8cpp_source.html @@ -792,7 +792,7 @@ $(document).ready(function() { init_codefold(0); });
685} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -828,7 +828,7 @@ $(document).ready(function() { init_codefold(0); });
T empty(T... args)
@ nullValue
'null' value
Definition json_value.h:19
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
std::unique_ptr< Config > no_admin(std::unique_ptr< Config >)
adjust config so no admin ports are enabled
Definition envconfig.cpp:57
diff --git a/LedgerReplayTask_8cpp_source.html b/LedgerReplayTask_8cpp_source.html index 9adc9f9e48..cb40abf3c0 100644 --- a/LedgerReplayTask_8cpp_source.html +++ b/LedgerReplayTask_8cpp_source.html @@ -162,217 +162,218 @@ $(document).ready(function() { init_codefold(0); });
72 , inboundLedgers_(inboundLedgers)
73 , replayer_(replayer)
74 , parameter_(parameter)
-
75 , maxTimeouts_(std::max(
- - -
78 , skipListAcquirer_(skipListAcquirer)
-
79{
-
80 JLOG(journal_.trace()) << "Create " << hash_;
-
81}
+
75 , maxTimeouts_(
+ + + +
79 , skipListAcquirer_(skipListAcquirer)
+
80{
+
81 JLOG(journal_.trace()) << "Create " << hash_;
+
82}
-
82
-
- -
84{
-
85 JLOG(journal_.trace()) << "Destroy " << hash_;
-
86}
+
83
+
+ +
85{
+
86 JLOG(journal_.trace()) << "Destroy " << hash_;
+
87}
-
87
-
88void
-
- -
90{
-
91 JLOG(journal_.debug()) << "Task start " << hash_;
-
92
- -
94 skipListAcquirer_->addDataCallback([wptr](bool good, uint256 const& hash) {
-
95 if (auto sptr = wptr.lock(); sptr)
-
96 {
-
97 if (!good)
-
98 {
-
99 sptr->cancel();
-
100 }
-
101 else
-
102 {
-
103 auto const skipListData = sptr->skipListAcquirer_->getData();
-
104 sptr->updateSkipList(hash, skipListData->ledgerSeq, skipListData->skipList);
-
105 }
-
106 }
-
107 });
-
108
- -
110 if (!isDone())
-
111 {
-
112 trigger(sl);
-
113 setTimer(sl);
-
114 }
-
115}
+
88
+
89void
+
+ +
91{
+
92 JLOG(journal_.debug()) << "Task start " << hash_;
+
93
+ +
95 skipListAcquirer_->addDataCallback([wptr](bool good, uint256 const& hash) {
+
96 if (auto sptr = wptr.lock(); sptr)
+
97 {
+
98 if (!good)
+
99 {
+
100 sptr->cancel();
+
101 }
+
102 else
+
103 {
+
104 auto const skipListData = sptr->skipListAcquirer_->getData();
+
105 sptr->updateSkipList(hash, skipListData->ledgerSeq, skipListData->skipList);
+
106 }
+
107 }
+
108 });
+
109
+ +
111 if (!isDone())
+
112 {
+
113 trigger(sl);
+
114 setTimer(sl);
+
115 }
+
116}
-
116
-
117void
-
- -
119{
-
120 JLOG(journal_.trace()) << "trigger " << hash_;
-
121 if (!parameter_.full_)
-
122 return;
-
123
-
124 if (!parent_)
-
125 {
- -
127 if (!parent_)
-
128 {
-
129 parent_ =
- -
131 }
-
132 if (parent_)
-
133 {
-
134 JLOG(journal_.trace()) << "Got start ledger " << parameter_.startHash_ << " for task " << hash_;
-
135 }
-
136 }
-
137
-
138 tryAdvance(sl);
-
139}
+
117
+
118void
+
+ +
120{
+
121 JLOG(journal_.trace()) << "trigger " << hash_;
+
122 if (!parameter_.full_)
+
123 return;
+
124
+
125 if (!parent_)
+
126 {
+ +
128 if (!parent_)
+
129 {
+
130 parent_ =
+ +
132 }
+
133 if (parent_)
+
134 {
+
135 JLOG(journal_.trace()) << "Got start ledger " << parameter_.startHash_ << " for task " << hash_;
+
136 }
+
137 }
+
138
+
139 tryAdvance(sl);
+
140}
-
140
-
141void
-
- -
143{
-
144 JLOG(journal_.trace()) << "Delta " << deltaHash << " ready for task " << hash_;
- -
146 if (!isDone())
-
147 tryAdvance(sl);
-
148}
+
141
+
142void
+
+ +
144{
+
145 JLOG(journal_.trace()) << "Delta " << deltaHash << " ready for task " << hash_;
+ +
147 if (!isDone())
+
148 tryAdvance(sl);
+
149}
-
149
-
150void
-
- -
152{
-
153 JLOG(journal_.trace()) << "tryAdvance task " << hash_
-
154 << (parameter_.full_ ? ", full parameter" : ", waiting to fill parameter")
-
155 << ", deltaIndex=" << deltaToBuild_ << ", totalDeltas=" << deltas_.size() << ", parent "
-
156 << (parent_ ? parent_->header().hash : uint256());
-
157
-
158 bool shouldTry = parent_ && parameter_.full_ && parameter_.totalLedgers_ - 1 == deltas_.size();
-
159 if (!shouldTry)
-
160 return;
-
161
-
162 try
-
163 {
-
164 for (; deltaToBuild_ < deltas_.size(); ++deltaToBuild_)
-
165 {
-
166 auto& delta = deltas_[deltaToBuild_];
-
167 XRPL_ASSERT(
-
168 parent_->seq() + 1 == delta->ledgerSeq_, "xrpl::LedgerReplayTask::tryAdvance : consecutive sequence");
-
169 if (auto l = delta->tryBuild(parent_); l)
-
170 {
-
171 JLOG(journal_.debug()) << "Task " << hash_ << " got ledger " << l->header().hash
-
172 << " deltaIndex=" << deltaToBuild_ << " totalDeltas=" << deltas_.size();
-
173 parent_ = l;
-
174 }
-
175 else
-
176 return;
-
177 }
-
178
-
179 complete_ = true;
-
180 JLOG(journal_.info()) << "Completed " << hash_;
-
181 }
-
182 catch (std::runtime_error const&)
-
183 {
-
184 failed_ = true;
-
185 }
-
186}
+
150
+
151void
+
+ +
153{
+
154 JLOG(journal_.trace()) << "tryAdvance task " << hash_
+
155 << (parameter_.full_ ? ", full parameter" : ", waiting to fill parameter")
+
156 << ", deltaIndex=" << deltaToBuild_ << ", totalDeltas=" << deltas_.size() << ", parent "
+
157 << (parent_ ? parent_->header().hash : uint256());
+
158
+
159 bool shouldTry = parent_ && parameter_.full_ && parameter_.totalLedgers_ - 1 == deltas_.size();
+
160 if (!shouldTry)
+
161 return;
+
162
+
163 try
+
164 {
+
165 for (; deltaToBuild_ < deltas_.size(); ++deltaToBuild_)
+
166 {
+
167 auto& delta = deltas_[deltaToBuild_];
+
168 XRPL_ASSERT(
+
169 parent_->seq() + 1 == delta->ledgerSeq_, "xrpl::LedgerReplayTask::tryAdvance : consecutive sequence");
+
170 if (auto l = delta->tryBuild(parent_); l)
+
171 {
+
172 JLOG(journal_.debug()) << "Task " << hash_ << " got ledger " << l->header().hash
+
173 << " deltaIndex=" << deltaToBuild_ << " totalDeltas=" << deltas_.size();
+
174 parent_ = l;
+
175 }
+
176 else
+
177 return;
+
178 }
+
179
+
180 complete_ = true;
+
181 JLOG(journal_.info()) << "Completed " << hash_;
+
182 }
+
183 catch (std::runtime_error const&)
+
184 {
+
185 failed_ = true;
+
186 }
+
187}
-
187
-
188void
-
- -
190{
-
191 {
- -
193 if (isDone())
-
194 return;
-
195 if (!parameter_.update(hash, seq, sList))
-
196 {
-
197 JLOG(journal_.error()) << "Parameter update failed " << hash_;
-
198 failed_ = true;
-
199 return;
-
200 }
-
201 }
-
202
- - -
205 if (!isDone())
-
206 trigger(sl);
-
207}
+
188
+
189void
+
+ +
191{
+
192 {
+ +
194 if (isDone())
+
195 return;
+
196 if (!parameter_.update(hash, seq, sList))
+
197 {
+
198 JLOG(journal_.error()) << "Parameter update failed " << hash_;
+
199 failed_ = true;
+
200 return;
+
201 }
+
202 }
+
203
+ + +
206 if (!isDone())
+
207 trigger(sl);
+
208}
-
208
-
209void
-
- -
211{
-
212 JLOG(journal_.trace()) << "mTimeouts=" << timeouts_ << " for " << hash_;
- -
214 {
-
215 failed_ = true;
-
216 JLOG(journal_.debug()) << "LedgerReplayTask Failed, too many timeouts " << hash_;
-
217 }
-
218 else
-
219 {
-
220 trigger(sl);
-
221 }
-
222}
+
209
+
210void
+
+ +
212{
+
213 JLOG(journal_.trace()) << "mTimeouts=" << timeouts_ << " for " << hash_;
+ +
215 {
+
216 failed_ = true;
+
217 JLOG(journal_.debug()) << "LedgerReplayTask Failed, too many timeouts " << hash_;
+
218 }
+
219 else
+
220 {
+
221 trigger(sl);
+
222 }
+
223}
-
223
- -
- -
226{
-
227 return shared_from_this();
-
228}
+
224
+ +
+ +
227{
+
228 return shared_from_this();
+
229}
-
229
-
230void
-
- -
232{
- -
234 delta->addDataCallback(parameter_.reason_, [wptr](bool good, uint256 const& hash) {
-
235 if (auto sptr = wptr.lock(); sptr)
-
236 {
-
237 if (!good)
-
238 sptr->cancel();
-
239 else
-
240 sptr->deltaReady(hash);
-
241 }
-
242 });
-
243
-
244 ScopedLockType sl(mtx_);
-
245 if (!isDone())
-
246 {
-
247 JLOG(journal_.trace()) << "addDelta task " << hash_ << " deltaIndex=" << deltaToBuild_
-
248 << " totalDeltas=" << deltas_.size();
-
249 XRPL_ASSERT(
-
250 deltas_.empty() || deltas_.back()->ledgerSeq_ + 1 == delta->ledgerSeq_,
-
251 "xrpl::LedgerReplayTask::addDelta : no deltas or consecutive "
-
252 "sequence");
-
253 deltas_.push_back(delta);
-
254 }
-
255}
+
230
+
231void
+
+ +
233{
+ +
235 delta->addDataCallback(parameter_.reason_, [wptr](bool good, uint256 const& hash) {
+
236 if (auto sptr = wptr.lock(); sptr)
+
237 {
+
238 if (!good)
+
239 sptr->cancel();
+
240 else
+
241 sptr->deltaReady(hash);
+
242 }
+
243 });
+
244
+
245 ScopedLockType sl(mtx_);
+
246 if (!isDone())
+
247 {
+
248 JLOG(journal_.trace()) << "addDelta task " << hash_ << " deltaIndex=" << deltaToBuild_
+
249 << " totalDeltas=" << deltas_.size();
+
250 XRPL_ASSERT(
+
251 deltas_.empty() || deltas_.back()->ledgerSeq_ + 1 == delta->ledgerSeq_,
+
252 "xrpl::LedgerReplayTask::addDelta : no deltas or consecutive "
+
253 "sequence");
+
254 deltas_.push_back(delta);
+
255 }
+
256}
-
256
-
257bool
-
-
258LedgerReplayTask::finished() const
-
259{
-
260 ScopedLockType sl(mtx_);
-
261 return isDone();
-
262}
+
257
+
258bool
+
+
259LedgerReplayTask::finished() const
+
260{
+
261 ScopedLockType sl(mtx_);
+
262 return isDone();
+
263}
-
263
-
264} // namespace xrpl
+
264
+
265} // namespace xrpl
Stream error() const
Definition Journal.h:318
Stream debug() const
Definition Journal.h:300
Stream info() const
Definition Journal.h:306
@@ -394,23 +395,23 @@ $(document).ready(function() { init_codefold(0); }); -
void trigger(ScopedLockType &sl)
Trigger another round.
-
void onTimer(bool progress, ScopedLockType &sl) override
Hook called from invokeOnTimer().
+
void trigger(ScopedLockType &sl)
Trigger another round.
+
void onTimer(bool progress, ScopedLockType &sl) override
Hook called from invokeOnTimer().
LedgerReplayer & replayer_
LedgerReplayTask(Application &app, InboundLedgers &inboundLedgers, LedgerReplayer &replayer, std::shared_ptr< SkipListAcquire > &skipListAcquirer, TaskParameter &&parameter)
Constructor.
- +
InboundLedgers & inboundLedgers_
-
void updateSkipList(uint256 const &hash, std::uint32_t seq, std::vector< uint256 > const &sList)
Update this task (by a SkipListAcquire subtask) when skip list is ready.
-
void tryAdvance(ScopedLockType &sl)
Try to build more ledgers.
-
void deltaReady(uint256 const &deltaHash)
Notify this task (by a LedgerDeltaAcquire subtask) that a delta is ready.
+
void updateSkipList(uint256 const &hash, std::uint32_t seq, std::vector< uint256 > const &sList)
Update this task (by a SkipListAcquire subtask) when skip list is ready.
+
void tryAdvance(ScopedLockType &sl)
Try to build more ledgers.
+
void deltaReady(uint256 const &deltaHash)
Notify this task (by a LedgerDeltaAcquire subtask) that a delta is ready.
-
std::weak_ptr< TimeoutCounter > pmDowncast() override
Return a weak pointer to this.
+
std::weak_ptr< TimeoutCounter > pmDowncast() override
Return a weak pointer to this.
std::vector< std::shared_ptr< LedgerDeltaAcquire > > deltas_
-
void addDelta(std::shared_ptr< LedgerDeltaAcquire > const &delta)
add a new LedgerDeltaAcquire subtask
+
void addDelta(std::shared_ptr< LedgerDeltaAcquire > const &delta)
add a new LedgerDeltaAcquire subtask
std::shared_ptr< Ledger const > parent_
std::shared_ptr< SkipListAcquire > skipListAcquirer_
-
void init()
Start the task.
+
void init()
Start the task.
Manages the lifetime of ledger replay tasks.
void createDeltas(std::shared_ptr< LedgerReplayTask > task)
Create LedgerDeltaAcquire subtasks for the LedgerReplayTask task.
diff --git a/LedgerReplayTask_8h_source.html b/LedgerReplayTask_8h_source.html index 6fb97a9c64..f3bac9012f 100644 --- a/LedgerReplayTask_8h_source.html +++ b/LedgerReplayTask_8h_source.html @@ -206,24 +206,24 @@ $(document).ready(function() { init_codefold(0); }); -
void trigger(ScopedLockType &sl)
Trigger another round.
-
void onTimer(bool progress, ScopedLockType &sl) override
Hook called from invokeOnTimer().
+
void trigger(ScopedLockType &sl)
Trigger another round.
+
void onTimer(bool progress, ScopedLockType &sl) override
Hook called from invokeOnTimer().
LedgerReplayer & replayer_
- +
InboundLedgers & inboundLedgers_
-
void updateSkipList(uint256 const &hash, std::uint32_t seq, std::vector< uint256 > const &sList)
Update this task (by a SkipListAcquire subtask) when skip list is ready.
-
void tryAdvance(ScopedLockType &sl)
Try to build more ledgers.
-
void deltaReady(uint256 const &deltaHash)
Notify this task (by a LedgerDeltaAcquire subtask) that a delta is ready.
+
void updateSkipList(uint256 const &hash, std::uint32_t seq, std::vector< uint256 > const &sList)
Update this task (by a SkipListAcquire subtask) when skip list is ready.
+
void tryAdvance(ScopedLockType &sl)
Try to build more ledgers.
+
void deltaReady(uint256 const &deltaHash)
Notify this task (by a LedgerDeltaAcquire subtask) that a delta is ready.
-
std::weak_ptr< TimeoutCounter > pmDowncast() override
Return a weak pointer to this.
+
std::weak_ptr< TimeoutCounter > pmDowncast() override
Return a weak pointer to this.
TaskParameter const & getTaskParameter() const
std::vector< std::shared_ptr< LedgerDeltaAcquire > > deltas_
-
bool finished() const
return if the task is finished
-
void addDelta(std::shared_ptr< LedgerDeltaAcquire > const &delta)
add a new LedgerDeltaAcquire subtask
+
bool finished() const
return if the task is finished
+
void addDelta(std::shared_ptr< LedgerDeltaAcquire > const &delta)
add a new LedgerDeltaAcquire subtask
std::shared_ptr< Ledger const > parent_
std::shared_ptr< SkipListAcquire > skipListAcquirer_
-
void init()
Start the task.
+
void init()
Start the task.
Manages the lifetime of ledger replay tasks.
This class is an "active" object.
diff --git a/LedgerReplay__test_8cpp_source.html b/LedgerReplay__test_8cpp_source.html index d308ec96bb..66a54107b7 100644 --- a/LedgerReplay__test_8cpp_source.html +++ b/LedgerReplay__test_8cpp_source.html @@ -1834,7 +1834,7 @@ $(document).ready(function() { init_codefold(0); });
STL namespace.
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition Indexes.cpp:172
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
static autofill_t const autofill
Definition tags.h:22
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
diff --git a/LedgerRequest__test_8cpp_source.html b/LedgerRequest__test_8cpp_source.html index ef7d3fbe28..3d8a81c993 100644 --- a/LedgerRequest__test_8cpp_source.html +++ b/LedgerRequest__test_8cpp_source.html @@ -439,7 +439,7 @@ $(document).ready(function() { init_codefold(0); });
T bind_front(T... args)
Represents a JSON value.
Definition json_value.h:130
-
std::string toStyledString() const
+
std::string toStyledString() const
A testsuite class.
Definition suite.h:51
diff --git a/LedgerStateFix_8cpp_source.html b/LedgerStateFix_8cpp_source.html index d6e51e2490..cee5270dec 100644 --- a/LedgerStateFix_8cpp_source.html +++ b/LedgerStateFix_8cpp_source.html @@ -183,7 +183,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecINTERNAL
Definition TER.h:291
@ tecFAILED_PROCESSING
Definition TER.h:267
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
ReadView const & view
Definition Transactor.h:56
diff --git a/LedgerToJson_8cpp_source.html b/LedgerToJson_8cpp_source.html index deff8b10e9..aa9f9cd612 100644 --- a/LedgerToJson_8cpp_source.html +++ b/LedgerToJson_8cpp_source.html @@ -410,8 +410,8 @@ $(document).ready(function() { init_codefold(0); });
321
322} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isObjectOrNull() const
-
Members getMemberNames() const
Return a list of the member names.
+
bool isObjectOrNull() const
+
Members getMemberNames() const
Return a list of the member names.
A generic endpoint for log messages.
Definition Journal.h:40
std::chrono::time_point< NetClock > time_point
Definition chrono.h:45
diff --git a/LedgerTrie_8h_source.html b/LedgerTrie_8h_source.html index e3e8dc2fa7..ed9bab91f4 100644 --- a/LedgerTrie_8h_source.html +++ b/LedgerTrie_8h_source.html @@ -779,7 +779,7 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Ancestry trie of ledgers.
Definition LedgerTrie.h:323
std::uint32_t tipSupport(Ledger const &ledger) const
Return count of tip support for the specific ledger.
Definition LedgerTrie.h:564
Node * findByLedgerID(Ledger const &ledger, Node *parent=nullptr) const
Find the node in the trie with an exact match to the given ledger ID.
Definition LedgerTrie.h:382
diff --git a/Ledger_8cpp_source.html b/Ledger_8cpp_source.html index 747b121cab..9a4b780f3f 100644 --- a/Ledger_8cpp_source.html +++ b/Ledger_8cpp_source.html @@ -1377,7 +1377,7 @@ $(document).ready(function() { init_codefold(0); });
uint256 const & key() const
Returns the 'key' (or 'index') of this item.
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:760
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:718
-
Serializer getSerializer() const
Definition STObject.h:966
+
Serializer getSerializer() const
Definition STObject.h:972
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:72
diff --git a/LendingHelpers_8cpp_source.html b/LendingHelpers_8cpp_source.html index 4361397638..ec2a7e0d9c 100644 --- a/LendingHelpers_8cpp_source.html +++ b/LendingHelpers_8cpp_source.html @@ -438,1513 +438,1514 @@ $(document).ready(function() { init_codefold(0); });
328 "xrpl::detail::doPayment",
329 "fee outstanding stays valid");
330
-
331 return LoanPaymentParts{// Principal paid is straightforward - it's the tracked delta
- -
333
-
334 // Interest paid combines:
-
335 // 1. Tracked interest from the amortization schedule
-
336 // (derived from the tracked deltas)
-
337 // 2. Untracked interest (e.g., late payment penalties)
-
338 .interestPaid = payment.trackedInterestPart() + payment.untrackedInterest,
-
339
-
340 // Value change represents how the loan's total value changed beyond
-
341 // normal amortization.
-
342 .valueChange = payment.untrackedInterest,
-
343
-
344 // Fee paid combines:
-
345 // 1. Tracked management fees from the amortization schedule
-
346 // 2. Untracked fees (e.g., late payment fees, service fees)
-
347 .feePaid = payment.trackedManagementFeeDelta + payment.untrackedManagementFee};
-
348}
+
331 return LoanPaymentParts{
+
332 // Principal paid is straightforward - it's the tracked delta
+ +
334
+
335 // Interest paid combines:
+
336 // 1. Tracked interest from the amortization schedule
+
337 // (derived from the tracked deltas)
+
338 // 2. Untracked interest (e.g., late payment penalties)
+
339 .interestPaid = payment.trackedInterestPart() + payment.untrackedInterest,
+
340
+
341 // Value change represents how the loan's total value changed beyond
+
342 // normal amortization.
+
343 .valueChange = payment.untrackedInterest,
+
344
+
345 // Fee paid combines:
+
346 // 1. Tracked management fees from the amortization schedule
+
347 // 2. Untracked fees (e.g., late payment fees, service fees)
+
348 .feePaid = payment.trackedManagementFeeDelta + payment.untrackedManagementFee};
+
349}
-
349
-
350/* Simulates an overpayment to validate it won't break the loan's amortization.
-
351 *
-
352 * When a borrower pays more than the scheduled amount, the loan needs to be
-
353 * re-amortized with a lower principal. This function performs that calculation
-
354 * in a "sandbox" using temporary variables, allowing the caller to validate
-
355 * the result before committing changes to the actual ledger.
-
356 *
-
357 * The function preserves accumulated rounding errors across the re-amortization
-
358 * to ensure the loan state remains consistent with its payment history.
-
359 */
- -
- -
362 Asset const& asset,
-
363 std::int32_t loanScale,
-
364 ExtendedPaymentComponents const& overpaymentComponents,
-
365 LoanState const& roundedOldState,
-
366 Number const& periodicPayment,
-
367 Number const& periodicRate,
-
368 std::uint32_t paymentRemaining,
-
369 TenthBips16 const managementFeeRate,
- -
371{
-
372 // Calculate what the loan state SHOULD be theoretically (at full precision)
-
373 auto const theoreticalState =
-
374 computeTheoreticalLoanState(periodicPayment, periodicRate, paymentRemaining, managementFeeRate);
-
375
-
376 // Calculate the accumulated rounding errors. These need to be preserved
-
377 // across the re-amortization to maintain consistency with the loan's
-
378 // payment history. Without preserving these errors, the loan could end
-
379 // up with a different total value than what the borrower has actually paid.
-
380 auto const errors = roundedOldState - theoreticalState;
-
381
-
382 // Compute the new principal by applying the overpayment to the theoretical
-
383 // principal. Use max with 0 to ensure we never go negative.
-
384 auto const newTheoreticalPrincipal =
-
385 std::max(theoreticalState.principalOutstanding - overpaymentComponents.trackedPrincipalDelta, Number{0});
-
386
-
387 // Compute new loan properties based on the reduced principal. This
-
388 // recalculates the periodic payment, total value, and management fees
-
389 // for the remaining payment schedule.
-
390 auto newLoanProperties = computeLoanProperties(
-
391 asset, newTheoreticalPrincipal, periodicRate, paymentRemaining, managementFeeRate, loanScale);
-
392
-
393 JLOG(j.debug()) << "new periodic payment: " << newLoanProperties.periodicPayment
-
394 << ", new total value: " << newLoanProperties.loanState.valueOutstanding
-
395 << ", first payment principal: " << newLoanProperties.firstPaymentPrincipal;
-
396
-
397 // Calculate what the new loan state should be with the new periodic payment
-
398 // including rounding errors
-
399 auto const newTheoreticalState =
- -
401 newLoanProperties.periodicPayment, periodicRate, paymentRemaining, managementFeeRate) +
-
402 errors;
-
403
-
404 JLOG(j.debug()) << "new theoretical value: " << newTheoreticalState.valueOutstanding
-
405 << ", principal: " << newTheoreticalState.principalOutstanding
-
406 << ", interest gross: " << newTheoreticalState.interestOutstanding();
-
407
-
408 // Update the loan state variables with the new values that include the
-
409 // preserved rounding errors. This ensures the loan's tracked state remains
-
410 // consistent with its payment history.
-
411 auto const principalOutstanding = std::clamp(
-
412 roundToAsset(asset, newTheoreticalState.principalOutstanding, loanScale, Number::upward),
-
413 numZero,
-
414 roundedOldState.principalOutstanding);
-
415 auto const totalValueOutstanding = std::clamp(
- -
417 asset, principalOutstanding + newTheoreticalState.interestOutstanding(), loanScale, Number::upward),
-
418 numZero,
-
419 roundedOldState.valueOutstanding);
-
420 auto const managementFeeOutstanding = std::clamp(
-
421 roundToAsset(asset, newTheoreticalState.managementFeeDue, loanScale),
-
422 numZero,
-
423 roundedOldState.managementFeeDue);
-
424
-
425 auto const roundedNewState =
-
426 constructLoanState(totalValueOutstanding, principalOutstanding, managementFeeOutstanding);
-
427
-
428 // Update newLoanProperties so that checkLoanGuards can make an accurate
-
429 // evaluation.
-
430 newLoanProperties.loanState = roundedNewState;
-
431
-
432 JLOG(j.debug()) << "new rounded value: " << roundedNewState.valueOutstanding
-
433 << ", principal: " << roundedNewState.principalOutstanding
-
434 << ", interest gross: " << roundedNewState.interestOutstanding();
-
435
-
436 // check that the loan is still valid
-
437 if (auto const ter = checkLoanGuards(
-
438 asset,
-
439 principalOutstanding,
-
440 // The loan may have been created with interest, but for
-
441 // small interest amounts, that may have already been paid
-
442 // off. Check what's still outstanding. This should
-
443 // guarantee that the interest checks pass.
-
444 roundedNewState.interestOutstanding() != beast::zero,
-
445 paymentRemaining,
-
446 newLoanProperties,
-
447 j))
-
448 {
-
449 JLOG(j.warn()) << "Principal overpayment would cause the loan to be in "
-
450 "an invalid state. Ignore the overpayment";
-
451
-
452 return Unexpected(tesSUCCESS);
-
453 }
-
454
-
455 // Validate that all computed properties are reasonable. These checks should
-
456 // never fail under normal circumstances, but we validate defensively.
-
457 if (newLoanProperties.periodicPayment <= 0 || newLoanProperties.loanState.valueOutstanding <= 0 ||
-
458 newLoanProperties.loanState.managementFeeDue < 0)
-
459 {
-
460 // LCOV_EXCL_START
-
461 JLOG(j.warn()) << "Overpayment not allowed: Computed loan "
-
462 "properties are invalid. Does "
-
463 "not compute. TotalValueOutstanding: "
-
464 << newLoanProperties.loanState.valueOutstanding
-
465 << ", PeriodicPayment : " << newLoanProperties.periodicPayment
-
466 << ", ManagementFeeOwedToBroker: " << newLoanProperties.loanState.managementFeeDue;
-
467 return Unexpected(tesSUCCESS);
-
468 // LCOV_EXCL_STOP
-
469 }
-
470
-
471 auto const deltas = roundedOldState - roundedNewState;
-
472
-
473 // The change in loan management fee is equal to the change between the old
-
474 // and the new outstanding management fees
-
475 XRPL_ASSERT_PARTS(
-
476 deltas.managementFee == roundedOldState.managementFeeDue - managementFeeOutstanding,
-
477 "xrpl::detail::tryOverpayment",
-
478 "no fee change");
-
479
-
480 // Calculate how the loan's value changed due to the overpayment.
-
481 // This should be negative (value decreased) or zero. A principal
-
482 // overpayment should never increase the loan's value.
-
483 // The value change is derived from the reduction in interest due to
-
484 // the lower principal.
-
485 // We do not consider the change in management fee here, since
-
486 // management fees are excluded from the valueOutstanding.
-
487 auto const valueChange = -deltas.interest;
-
488 if (valueChange > 0)
-
489 {
-
490 JLOG(j.warn()) << "Principal overpayment would increase the value of "
-
491 "the loan. Ignore the overpayment";
-
492 return Unexpected(tesSUCCESS);
-
493 }
-
494
-
495 return std::make_pair(
- -
497 // Principal paid is the reduction in principal outstanding
-
498 .principalPaid = deltas.principal,
-
499 // Interest paid is the reduction in interest due
-
500 .interestPaid = overpaymentComponents.untrackedInterest,
-
501 // Value change includes both the reduction from paying down
-
502 // principal (negative) and any untracked interest penalties
-
503 // (positive, e.g., if the overpayment itself incurs a fee)
-
504 .valueChange = valueChange + overpaymentComponents.untrackedInterest,
-
505 // Fee paid includes both the reduction in tracked management fees
-
506 // and any untracked fees on the overpayment itself
-
507 .feePaid = overpaymentComponents.untrackedManagementFee + overpaymentComponents.trackedManagementFeeDelta,
-
508 },
-
509 newLoanProperties);
-
510}
+
350
+
351/* Simulates an overpayment to validate it won't break the loan's amortization.
+
352 *
+
353 * When a borrower pays more than the scheduled amount, the loan needs to be
+
354 * re-amortized with a lower principal. This function performs that calculation
+
355 * in a "sandbox" using temporary variables, allowing the caller to validate
+
356 * the result before committing changes to the actual ledger.
+
357 *
+
358 * The function preserves accumulated rounding errors across the re-amortization
+
359 * to ensure the loan state remains consistent with its payment history.
+
360 */
+ +
+ +
363 Asset const& asset,
+
364 std::int32_t loanScale,
+
365 ExtendedPaymentComponents const& overpaymentComponents,
+
366 LoanState const& roundedOldState,
+
367 Number const& periodicPayment,
+
368 Number const& periodicRate,
+
369 std::uint32_t paymentRemaining,
+
370 TenthBips16 const managementFeeRate,
+ +
372{
+
373 // Calculate what the loan state SHOULD be theoretically (at full precision)
+
374 auto const theoreticalState =
+
375 computeTheoreticalLoanState(periodicPayment, periodicRate, paymentRemaining, managementFeeRate);
+
376
+
377 // Calculate the accumulated rounding errors. These need to be preserved
+
378 // across the re-amortization to maintain consistency with the loan's
+
379 // payment history. Without preserving these errors, the loan could end
+
380 // up with a different total value than what the borrower has actually paid.
+
381 auto const errors = roundedOldState - theoreticalState;
+
382
+
383 // Compute the new principal by applying the overpayment to the theoretical
+
384 // principal. Use max with 0 to ensure we never go negative.
+
385 auto const newTheoreticalPrincipal =
+
386 std::max(theoreticalState.principalOutstanding - overpaymentComponents.trackedPrincipalDelta, Number{0});
+
387
+
388 // Compute new loan properties based on the reduced principal. This
+
389 // recalculates the periodic payment, total value, and management fees
+
390 // for the remaining payment schedule.
+
391 auto newLoanProperties = computeLoanProperties(
+
392 asset, newTheoreticalPrincipal, periodicRate, paymentRemaining, managementFeeRate, loanScale);
+
393
+
394 JLOG(j.debug()) << "new periodic payment: " << newLoanProperties.periodicPayment
+
395 << ", new total value: " << newLoanProperties.loanState.valueOutstanding
+
396 << ", first payment principal: " << newLoanProperties.firstPaymentPrincipal;
+
397
+
398 // Calculate what the new loan state should be with the new periodic payment
+
399 // including rounding errors
+
400 auto const newTheoreticalState =
+ +
402 newLoanProperties.periodicPayment, periodicRate, paymentRemaining, managementFeeRate) +
+
403 errors;
+
404
+
405 JLOG(j.debug()) << "new theoretical value: " << newTheoreticalState.valueOutstanding
+
406 << ", principal: " << newTheoreticalState.principalOutstanding
+
407 << ", interest gross: " << newTheoreticalState.interestOutstanding();
+
408
+
409 // Update the loan state variables with the new values that include the
+
410 // preserved rounding errors. This ensures the loan's tracked state remains
+
411 // consistent with its payment history.
+
412 auto const principalOutstanding = std::clamp(
+
413 roundToAsset(asset, newTheoreticalState.principalOutstanding, loanScale, Number::upward),
+
414 numZero,
+
415 roundedOldState.principalOutstanding);
+
416 auto const totalValueOutstanding = std::clamp(
+ +
418 asset, principalOutstanding + newTheoreticalState.interestOutstanding(), loanScale, Number::upward),
+
419 numZero,
+
420 roundedOldState.valueOutstanding);
+
421 auto const managementFeeOutstanding = std::clamp(
+
422 roundToAsset(asset, newTheoreticalState.managementFeeDue, loanScale),
+
423 numZero,
+
424 roundedOldState.managementFeeDue);
+
425
+
426 auto const roundedNewState =
+
427 constructLoanState(totalValueOutstanding, principalOutstanding, managementFeeOutstanding);
+
428
+
429 // Update newLoanProperties so that checkLoanGuards can make an accurate
+
430 // evaluation.
+
431 newLoanProperties.loanState = roundedNewState;
+
432
+
433 JLOG(j.debug()) << "new rounded value: " << roundedNewState.valueOutstanding
+
434 << ", principal: " << roundedNewState.principalOutstanding
+
435 << ", interest gross: " << roundedNewState.interestOutstanding();
+
436
+
437 // check that the loan is still valid
+
438 if (auto const ter = checkLoanGuards(
+
439 asset,
+
440 principalOutstanding,
+
441 // The loan may have been created with interest, but for
+
442 // small interest amounts, that may have already been paid
+
443 // off. Check what's still outstanding. This should
+
444 // guarantee that the interest checks pass.
+
445 roundedNewState.interestOutstanding() != beast::zero,
+
446 paymentRemaining,
+
447 newLoanProperties,
+
448 j))
+
449 {
+
450 JLOG(j.warn()) << "Principal overpayment would cause the loan to be in "
+
451 "an invalid state. Ignore the overpayment";
+
452
+
453 return Unexpected(tesSUCCESS);
+
454 }
+
455
+
456 // Validate that all computed properties are reasonable. These checks should
+
457 // never fail under normal circumstances, but we validate defensively.
+
458 if (newLoanProperties.periodicPayment <= 0 || newLoanProperties.loanState.valueOutstanding <= 0 ||
+
459 newLoanProperties.loanState.managementFeeDue < 0)
+
460 {
+
461 // LCOV_EXCL_START
+
462 JLOG(j.warn()) << "Overpayment not allowed: Computed loan "
+
463 "properties are invalid. Does "
+
464 "not compute. TotalValueOutstanding: "
+
465 << newLoanProperties.loanState.valueOutstanding
+
466 << ", PeriodicPayment : " << newLoanProperties.periodicPayment
+
467 << ", ManagementFeeOwedToBroker: " << newLoanProperties.loanState.managementFeeDue;
+
468 return Unexpected(tesSUCCESS);
+
469 // LCOV_EXCL_STOP
+
470 }
+
471
+
472 auto const deltas = roundedOldState - roundedNewState;
+
473
+
474 // The change in loan management fee is equal to the change between the old
+
475 // and the new outstanding management fees
+
476 XRPL_ASSERT_PARTS(
+
477 deltas.managementFee == roundedOldState.managementFeeDue - managementFeeOutstanding,
+
478 "xrpl::detail::tryOverpayment",
+
479 "no fee change");
+
480
+
481 // Calculate how the loan's value changed due to the overpayment.
+
482 // This should be negative (value decreased) or zero. A principal
+
483 // overpayment should never increase the loan's value.
+
484 // The value change is derived from the reduction in interest due to
+
485 // the lower principal.
+
486 // We do not consider the change in management fee here, since
+
487 // management fees are excluded from the valueOutstanding.
+
488 auto const valueChange = -deltas.interest;
+
489 if (valueChange > 0)
+
490 {
+
491 JLOG(j.warn()) << "Principal overpayment would increase the value of "
+
492 "the loan. Ignore the overpayment";
+
493 return Unexpected(tesSUCCESS);
+
494 }
+
495
+
496 return std::make_pair(
+ +
498 // Principal paid is the reduction in principal outstanding
+
499 .principalPaid = deltas.principal,
+
500 // Interest paid is the reduction in interest due
+
501 .interestPaid = overpaymentComponents.untrackedInterest,
+
502 // Value change includes both the reduction from paying down
+
503 // principal (negative) and any untracked interest penalties
+
504 // (positive, e.g., if the overpayment itself incurs a fee)
+
505 .valueChange = valueChange + overpaymentComponents.untrackedInterest,
+
506 // Fee paid includes both the reduction in tracked management fees
+
507 // and any untracked fees on the overpayment itself
+
508 .feePaid = overpaymentComponents.untrackedManagementFee + overpaymentComponents.trackedManagementFeeDelta,
+
509 },
+
510 newLoanProperties);
+
511}
-
511
-
512/* Validates and applies an overpayment to the loan state.
-
513 *
-
514 * This function acts as a wrapper around tryOverpayment(), performing the
-
515 * re-amortization calculation in a sandbox (using temporary copies of the
-
516 * loan state), then validating the results before committing them to the
-
517 * actual ledger via the proxy objects.
-
518 *
-
519 * The two-step process (try in sandbox, then commit) ensures that if the
-
520 * overpayment would leave the loan in an invalid state, we can reject it
-
521 * gracefully without corrupting the ledger data.
-
522 */
-
523template <class NumberProxy>
- -
- -
526 Asset const& asset,
-
527 std::int32_t loanScale,
-
528 ExtendedPaymentComponents const& overpaymentComponents,
-
529 NumberProxy& totalValueOutstandingProxy,
-
530 NumberProxy& principalOutstandingProxy,
-
531 NumberProxy& managementFeeOutstandingProxy,
-
532 NumberProxy& periodicPaymentProxy,
-
533 Number const& periodicRate,
-
534 std::uint32_t const paymentRemaining,
-
535 TenthBips16 const managementFeeRate,
- -
537{
-
538 auto const loanState =
-
539 constructLoanState(totalValueOutstandingProxy, principalOutstandingProxy, managementFeeOutstandingProxy);
-
540 auto const periodicPayment = periodicPaymentProxy;
-
541 JLOG(j.debug()) << "overpayment components:"
-
542 << ", totalValue before: " << *totalValueOutstandingProxy
-
543 << ", valueDelta: " << overpaymentComponents.trackedValueDelta
-
544 << ", principalDelta: " << overpaymentComponents.trackedPrincipalDelta
-
545 << ", managementFeeDelta: " << overpaymentComponents.trackedManagementFeeDelta
-
546 << ", interestPart: " << overpaymentComponents.trackedInterestPart()
-
547 << ", untrackedInterest: " << overpaymentComponents.untrackedInterest
-
548 << ", totalDue: " << overpaymentComponents.totalDue << ", payments remaining :" << paymentRemaining;
-
549
-
550 // Attempt to re-amortize the loan with the overpayment applied.
-
551 // This modifies the temporary copies, leaving the proxies unchanged.
-
552 auto const ret = tryOverpayment(
-
553 asset,
-
554 loanScale,
-
555 overpaymentComponents,
-
556 loanState,
-
557 periodicPayment,
-
558 periodicRate,
-
559 paymentRemaining,
-
560 managementFeeRate,
-
561 j);
-
562 if (!ret)
-
563 return Unexpected(ret.error());
-
564
-
565 auto const& [loanPaymentParts, newLoanProperties] = *ret;
-
566 auto const newRoundedLoanState = newLoanProperties.loanState;
-
567
-
568 // Safety check: the principal must have decreased. If it didn't (or
-
569 // increased!), something went wrong in the calculation and we should
-
570 // reject the overpayment.
-
571 if (principalOutstandingProxy <= newRoundedLoanState.principalOutstanding)
-
572 {
-
573 // LCOV_EXCL_START
-
574 JLOG(j.warn()) << "Overpayment not allowed: principal "
-
575 << "outstanding did not decrease. Before: " << *principalOutstandingProxy
-
576 << ". After: " << newRoundedLoanState.principalOutstanding;
-
577 return Unexpected(tesSUCCESS);
-
578 // LCOV_EXCL_STOP
-
579 }
-
580
-
581 // The proxies still hold the original (pre-overpayment) values, which
-
582 // allows us to compute deltas and verify they match what we expect
-
583 // from the overpaymentComponents and loanPaymentParts.
-
584
-
585 XRPL_ASSERT_PARTS(
-
586 overpaymentComponents.trackedPrincipalDelta ==
-
587 principalOutstandingProxy - newRoundedLoanState.principalOutstanding,
-
588 "xrpl::detail::doOverpayment",
-
589 "principal change agrees");
-
590
-
591 // I'm not 100% sure the following asserts are correct. If in doubt, and
-
592 // everything else works, remove any that cause trouble.
-
593
-
594 JLOG(j.debug()) << "valueChange: " << loanPaymentParts.valueChange
-
595 << ", totalValue before: " << *totalValueOutstandingProxy
-
596 << ", totalValue after: " << newRoundedLoanState.valueOutstanding
-
597 << ", totalValue delta: " << (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding)
-
598 << ", principalDelta: " << overpaymentComponents.trackedPrincipalDelta
-
599 << ", principalPaid: " << loanPaymentParts.principalPaid << ", Computed difference: "
-
600 << overpaymentComponents.trackedPrincipalDelta -
-
601 (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding);
-
602
-
603 XRPL_ASSERT_PARTS(
-
604 loanPaymentParts.valueChange ==
-
605 newRoundedLoanState.valueOutstanding -
-
606 (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta) +
-
607 overpaymentComponents.trackedInterestPart(),
-
608 "xrpl::detail::doOverpayment",
-
609 "interest paid agrees");
-
610
-
611 XRPL_ASSERT_PARTS(
-
612 overpaymentComponents.trackedPrincipalDelta == loanPaymentParts.principalPaid,
-
613 "xrpl::detail::doOverpayment",
-
614 "principal payment matches");
-
615
-
616 // All validations passed, so update the proxy objects (which will
-
617 // modify the actual Loan ledger object)
-
618 totalValueOutstandingProxy = newRoundedLoanState.valueOutstanding;
-
619 principalOutstandingProxy = newRoundedLoanState.principalOutstanding;
-
620 managementFeeOutstandingProxy = newRoundedLoanState.managementFeeDue;
-
621 periodicPaymentProxy = newLoanProperties.periodicPayment;
-
622
-
623 return loanPaymentParts;
-
624}
+
512
+
513/* Validates and applies an overpayment to the loan state.
+
514 *
+
515 * This function acts as a wrapper around tryOverpayment(), performing the
+
516 * re-amortization calculation in a sandbox (using temporary copies of the
+
517 * loan state), then validating the results before committing them to the
+
518 * actual ledger via the proxy objects.
+
519 *
+
520 * The two-step process (try in sandbox, then commit) ensures that if the
+
521 * overpayment would leave the loan in an invalid state, we can reject it
+
522 * gracefully without corrupting the ledger data.
+
523 */
+
524template <class NumberProxy>
+ +
+ +
527 Asset const& asset,
+
528 std::int32_t loanScale,
+
529 ExtendedPaymentComponents const& overpaymentComponents,
+
530 NumberProxy& totalValueOutstandingProxy,
+
531 NumberProxy& principalOutstandingProxy,
+
532 NumberProxy& managementFeeOutstandingProxy,
+
533 NumberProxy& periodicPaymentProxy,
+
534 Number const& periodicRate,
+
535 std::uint32_t const paymentRemaining,
+
536 TenthBips16 const managementFeeRate,
+ +
538{
+
539 auto const loanState =
+
540 constructLoanState(totalValueOutstandingProxy, principalOutstandingProxy, managementFeeOutstandingProxy);
+
541 auto const periodicPayment = periodicPaymentProxy;
+
542 JLOG(j.debug()) << "overpayment components:"
+
543 << ", totalValue before: " << *totalValueOutstandingProxy
+
544 << ", valueDelta: " << overpaymentComponents.trackedValueDelta
+
545 << ", principalDelta: " << overpaymentComponents.trackedPrincipalDelta
+
546 << ", managementFeeDelta: " << overpaymentComponents.trackedManagementFeeDelta
+
547 << ", interestPart: " << overpaymentComponents.trackedInterestPart()
+
548 << ", untrackedInterest: " << overpaymentComponents.untrackedInterest
+
549 << ", totalDue: " << overpaymentComponents.totalDue << ", payments remaining :" << paymentRemaining;
+
550
+
551 // Attempt to re-amortize the loan with the overpayment applied.
+
552 // This modifies the temporary copies, leaving the proxies unchanged.
+
553 auto const ret = tryOverpayment(
+
554 asset,
+
555 loanScale,
+
556 overpaymentComponents,
+
557 loanState,
+
558 periodicPayment,
+
559 periodicRate,
+
560 paymentRemaining,
+
561 managementFeeRate,
+
562 j);
+
563 if (!ret)
+
564 return Unexpected(ret.error());
+
565
+
566 auto const& [loanPaymentParts, newLoanProperties] = *ret;
+
567 auto const newRoundedLoanState = newLoanProperties.loanState;
+
568
+
569 // Safety check: the principal must have decreased. If it didn't (or
+
570 // increased!), something went wrong in the calculation and we should
+
571 // reject the overpayment.
+
572 if (principalOutstandingProxy <= newRoundedLoanState.principalOutstanding)
+
573 {
+
574 // LCOV_EXCL_START
+
575 JLOG(j.warn()) << "Overpayment not allowed: principal "
+
576 << "outstanding did not decrease. Before: " << *principalOutstandingProxy
+
577 << ". After: " << newRoundedLoanState.principalOutstanding;
+
578 return Unexpected(tesSUCCESS);
+
579 // LCOV_EXCL_STOP
+
580 }
+
581
+
582 // The proxies still hold the original (pre-overpayment) values, which
+
583 // allows us to compute deltas and verify they match what we expect
+
584 // from the overpaymentComponents and loanPaymentParts.
+
585
+
586 XRPL_ASSERT_PARTS(
+
587 overpaymentComponents.trackedPrincipalDelta ==
+
588 principalOutstandingProxy - newRoundedLoanState.principalOutstanding,
+
589 "xrpl::detail::doOverpayment",
+
590 "principal change agrees");
+
591
+
592 // I'm not 100% sure the following asserts are correct. If in doubt, and
+
593 // everything else works, remove any that cause trouble.
+
594
+
595 JLOG(j.debug()) << "valueChange: " << loanPaymentParts.valueChange
+
596 << ", totalValue before: " << *totalValueOutstandingProxy
+
597 << ", totalValue after: " << newRoundedLoanState.valueOutstanding
+
598 << ", totalValue delta: " << (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding)
+
599 << ", principalDelta: " << overpaymentComponents.trackedPrincipalDelta
+
600 << ", principalPaid: " << loanPaymentParts.principalPaid << ", Computed difference: "
+
601 << overpaymentComponents.trackedPrincipalDelta -
+
602 (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding);
+
603
+
604 XRPL_ASSERT_PARTS(
+
605 loanPaymentParts.valueChange ==
+
606 newRoundedLoanState.valueOutstanding -
+
607 (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta) +
+
608 overpaymentComponents.trackedInterestPart(),
+
609 "xrpl::detail::doOverpayment",
+
610 "interest paid agrees");
+
611
+
612 XRPL_ASSERT_PARTS(
+
613 overpaymentComponents.trackedPrincipalDelta == loanPaymentParts.principalPaid,
+
614 "xrpl::detail::doOverpayment",
+
615 "principal payment matches");
+
616
+
617 // All validations passed, so update the proxy objects (which will
+
618 // modify the actual Loan ledger object)
+
619 totalValueOutstandingProxy = newRoundedLoanState.valueOutstanding;
+
620 principalOutstandingProxy = newRoundedLoanState.principalOutstanding;
+
621 managementFeeOutstandingProxy = newRoundedLoanState.managementFeeDue;
+
622 periodicPaymentProxy = newLoanProperties.periodicPayment;
+
623
+
624 return loanPaymentParts;
+
625}
-
625
-
626/* Computes the payment components for a late payment.
-
627 *
-
628 * A late payment is made after the grace period has expired and includes:
-
629 * 1. All components of a regular periodic payment
-
630 * 2. Late payment penalty interest (accrued since the due date)
-
631 * 3. Late payment fee charged by the broker
-
632 *
-
633 * The late penalty interest increases the loan's total value (the borrower
-
634 * owes more than scheduled), while the regular payment components follow
-
635 * the normal amortization schedule.
-
636 *
-
637 * Implements equation (15) from XLS-66 spec, Section A-2 Equation Glossary
-
638 */
- -
- -
641 Asset const& asset,
-
642 ApplyView const& view,
-
643 Number const& principalOutstanding,
-
644 std::int32_t nextDueDate,
-
645 ExtendedPaymentComponents const& periodic,
-
646 TenthBips32 lateInterestRate,
-
647 std::int32_t loanScale,
-
648 Number const& latePaymentFee,
-
649 STAmount const& amount,
-
650 TenthBips16 managementFeeRate,
- -
652{
-
653 // Check if the due date has passed. If not, reject the payment as
-
654 // being too soon
-
655 if (!hasExpired(view, nextDueDate))
-
656 return Unexpected(tecTOO_SOON);
-
657
-
658 // Calculate the penalty interest based on how long the payment is overdue.
-
659 auto const latePaymentInterest =
-
660 loanLatePaymentInterest(principalOutstanding, lateInterestRate, view.parentCloseTime(), nextDueDate);
-
661
-
662 // Round the late interest and split it between the vault (net interest)
-
663 // and the broker (management fee portion). This lambda ensures we
-
664 // round before splitting to maintain precision.
-
665 auto const [roundedLateInterest, roundedLateManagementFee] = [&]() {
-
666 auto const interest = roundToAsset(asset, latePaymentInterest, loanScale);
-
667 return computeInterestAndFeeParts(asset, interest, managementFeeRate, loanScale);
-
668 }();
-
669
-
670 XRPL_ASSERT(roundedLateInterest >= 0, "xrpl::detail::computeLatePayment : valid late interest");
-
671 XRPL_ASSERT_PARTS(
- -
673 "xrpl::detail::computeLatePayment",
-
674 "no extra parts to this payment");
-
675
-
676 // Create the late payment components by copying the regular periodic
-
677 // payment and adding the late penalties. We use a lambda to construct
-
678 // this to keep the logic clear. This preserves all the other fields without
-
679 // having to enumerate them.
-
680
- -
682 periodic,
-
683 // Untracked management fee includes:
-
684 // 1. Regular service fee (from periodic.untrackedManagementFee)
-
685 // 2. Late payment fee (fixed penalty)
-
686 // 3. Management fee portion of late interest
-
687 periodic.untrackedManagementFee + latePaymentFee + roundedLateManagementFee,
-
688
-
689 // Untracked interest includes:
-
690 // 1. Any untracked interest from the regular payment (usually 0)
-
691 // 2. Late penalty interest (increases loan value)
-
692 // This positive value indicates the loan's value increased due
-
693 // to the late payment.
-
694 periodic.untrackedInterest + roundedLateInterest};
-
695
-
696 XRPL_ASSERT_PARTS(
-
697 isRounded(asset, late.totalDue, loanScale), "xrpl::detail::computeLatePayment", "total due is rounded");
-
698
-
699 // Check that the borrower provided enough funds to cover the late payment.
-
700 // The late payment is more expensive than a regular payment due to the
-
701 // penalties.
-
702 if (amount < late.totalDue)
-
703 {
-
704 JLOG(j.warn()) << "Late loan payment amount is insufficient. Due: " << late.totalDue << ", paid: " << amount;
- -
706 }
-
707
-
708 return late;
-
709}
+
626
+
627/* Computes the payment components for a late payment.
+
628 *
+
629 * A late payment is made after the grace period has expired and includes:
+
630 * 1. All components of a regular periodic payment
+
631 * 2. Late payment penalty interest (accrued since the due date)
+
632 * 3. Late payment fee charged by the broker
+
633 *
+
634 * The late penalty interest increases the loan's total value (the borrower
+
635 * owes more than scheduled), while the regular payment components follow
+
636 * the normal amortization schedule.
+
637 *
+
638 * Implements equation (15) from XLS-66 spec, Section A-2 Equation Glossary
+
639 */
+ +
+ +
642 Asset const& asset,
+
643 ApplyView const& view,
+
644 Number const& principalOutstanding,
+
645 std::int32_t nextDueDate,
+
646 ExtendedPaymentComponents const& periodic,
+
647 TenthBips32 lateInterestRate,
+
648 std::int32_t loanScale,
+
649 Number const& latePaymentFee,
+
650 STAmount const& amount,
+
651 TenthBips16 managementFeeRate,
+ +
653{
+
654 // Check if the due date has passed. If not, reject the payment as
+
655 // being too soon
+
656 if (!hasExpired(view, nextDueDate))
+
657 return Unexpected(tecTOO_SOON);
+
658
+
659 // Calculate the penalty interest based on how long the payment is overdue.
+
660 auto const latePaymentInterest =
+
661 loanLatePaymentInterest(principalOutstanding, lateInterestRate, view.parentCloseTime(), nextDueDate);
+
662
+
663 // Round the late interest and split it between the vault (net interest)
+
664 // and the broker (management fee portion). This lambda ensures we
+
665 // round before splitting to maintain precision.
+
666 auto const [roundedLateInterest, roundedLateManagementFee] = [&]() {
+
667 auto const interest = roundToAsset(asset, latePaymentInterest, loanScale);
+
668 return computeInterestAndFeeParts(asset, interest, managementFeeRate, loanScale);
+
669 }();
+
670
+
671 XRPL_ASSERT(roundedLateInterest >= 0, "xrpl::detail::computeLatePayment : valid late interest");
+
672 XRPL_ASSERT_PARTS(
+ +
674 "xrpl::detail::computeLatePayment",
+
675 "no extra parts to this payment");
+
676
+
677 // Create the late payment components by copying the regular periodic
+
678 // payment and adding the late penalties. We use a lambda to construct
+
679 // this to keep the logic clear. This preserves all the other fields without
+
680 // having to enumerate them.
+
681
+ +
683 periodic,
+
684 // Untracked management fee includes:
+
685 // 1. Regular service fee (from periodic.untrackedManagementFee)
+
686 // 2. Late payment fee (fixed penalty)
+
687 // 3. Management fee portion of late interest
+
688 periodic.untrackedManagementFee + latePaymentFee + roundedLateManagementFee,
+
689
+
690 // Untracked interest includes:
+
691 // 1. Any untracked interest from the regular payment (usually 0)
+
692 // 2. Late penalty interest (increases loan value)
+
693 // This positive value indicates the loan's value increased due
+
694 // to the late payment.
+
695 periodic.untrackedInterest + roundedLateInterest};
+
696
+
697 XRPL_ASSERT_PARTS(
+
698 isRounded(asset, late.totalDue, loanScale), "xrpl::detail::computeLatePayment", "total due is rounded");
+
699
+
700 // Check that the borrower provided enough funds to cover the late payment.
+
701 // The late payment is more expensive than a regular payment due to the
+
702 // penalties.
+
703 if (amount < late.totalDue)
+
704 {
+
705 JLOG(j.warn()) << "Late loan payment amount is insufficient. Due: " << late.totalDue << ", paid: " << amount;
+ +
707 }
+
708
+
709 return late;
+
710}
-
710
-
711/* Computes payment components for paying off a loan early (before final
-
712 * payment).
-
713 *
-
714 * A full payment closes the loan immediately, paying off all outstanding
-
715 * balances plus a prepayment penalty and any accrued interest since the last
-
716 * payment. This is different from the final scheduled payment, which has no
-
717 * prepayment penalty.
-
718 *
-
719 * The function calculates:
-
720 * - Accrued interest since last payment (time-based)
-
721 * - Prepayment penalty (percentage of remaining principal)
-
722 * - Close payment fee (fixed fee for early closure)
-
723 * - All remaining principal and outstanding fees
-
724 *
-
725 * The loan's value may increase or decrease depending on whether the prepayment
-
726 * penalty exceeds the scheduled interest that would have been paid.
-
727 *
-
728 * Implements equation (26) from XLS-66 spec, Section A-2 Equation Glossary
-
729 */
- -
- -
732 Asset const& asset,
-
733 ApplyView& view,
-
734 Number const& principalOutstanding,
-
735 Number const& managementFeeOutstanding,
-
736 Number const& periodicPayment,
-
737 std::uint32_t paymentRemaining,
-
738 std::uint32_t prevPaymentDate,
-
739 std::uint32_t const startDate,
-
740 std::uint32_t const paymentInterval,
-
741 TenthBips32 const closeInterestRate,
-
742 std::int32_t loanScale,
-
743 Number const& totalInterestOutstanding,
-
744 Number const& periodicRate,
-
745 Number const& closePaymentFee,
-
746 STAmount const& amount,
-
747 TenthBips16 managementFeeRate,
- -
749{
-
750 // Full payment must be made before the final scheduled payment.
-
751 if (paymentRemaining <= 1)
-
752 {
-
753 // If this is the last payment, it has to be a regular payment
-
754 JLOG(j.warn()) << "Last payment cannot be a full payment.";
-
755 return Unexpected(tecKILLED);
-
756 }
-
757
-
758 // Calculate the theoretical principal based on the payment schedule.
-
759 // This theoretical (unrounded) value is used to compute interest and
-
760 // penalties accurately.
-
761 Number const theoreticalPrincipalOutstanding =
-
762 loanPrincipalFromPeriodicPayment(periodicPayment, periodicRate, paymentRemaining);
-
763
-
764 // Full payment interest includes both accrued interest (time since last
-
765 // payment) and prepayment penalty (for closing early).
-
766 auto const fullPaymentInterest = computeFullPaymentInterest(
-
767 theoreticalPrincipalOutstanding,
-
768 periodicRate,
-
769 view.parentCloseTime(),
-
770 paymentInterval,
-
771 prevPaymentDate,
-
772 startDate,
-
773 closeInterestRate);
-
774
-
775 // Split the full payment interest into net interest (to vault) and
-
776 // management fee (to broker), applying proper rounding.
-
777 auto const [roundedFullInterest, roundedFullManagementFee] = [&]() {
-
778 auto const interest = roundToAsset(asset, fullPaymentInterest, loanScale, Number::downward);
-
779 return computeInterestAndFeeParts(asset, interest, managementFeeRate, loanScale);
-
780 }();
-
781
- - -
784 // Pay off all tracked outstanding balances: principal, interest,
-
785 // and fees.
-
786 // This marks the loan as complete (final payment).
-
787 .trackedValueDelta = principalOutstanding + totalInterestOutstanding + managementFeeOutstanding,
-
788 .trackedPrincipalDelta = principalOutstanding,
-
789
-
790 // All outstanding management fees are paid. This zeroes out the
-
791 // tracked fee balance.
-
792 .trackedManagementFeeDelta = managementFeeOutstanding,
-
793 .specialCase = PaymentSpecialCase::final,
-
794 },
-
795
-
796 // Untracked management fee includes:
-
797 // 1. Close payment fee (fixed fee for early closure)
-
798 // 2. Management fee on the full payment interest
-
799 // 3. Minus the outstanding tracked fee (already accounted for above)
-
800 // This can be negative because the outstanding fee is subtracted, but
-
801 // it gets combined with trackedManagementFeeDelta in the final
-
802 // accounting.
-
803 closePaymentFee + roundedFullManagementFee - managementFeeOutstanding,
-
804
-
805 // Value change represents the difference between what the loan was
-
806 // expected to earn (totalInterestOutstanding) and what it actually
-
807 // earns (roundedFullInterest with prepayment penalty).
-
808 // - Positive: Prepayment penalty exceeds scheduled interest (loan value
-
809 // increases)
-
810 // - Negative: Prepayment penalty is less than scheduled interest (loan
-
811 // value decreases)
-
812 roundedFullInterest - totalInterestOutstanding,
-
813 };
-
814
-
815 XRPL_ASSERT_PARTS(
-
816 isRounded(asset, full.totalDue, loanScale), "xrpl::detail::computeFullPayment", "total due is rounded");
-
817
-
818 JLOG(j.trace()) << "computeFullPayment result: periodicPayment: " << periodicPayment
-
819 << ", periodicRate: " << periodicRate << ", paymentRemaining: " << paymentRemaining
-
820 << ", theoreticalPrincipalOutstanding: " << theoreticalPrincipalOutstanding
-
821 << ", fullPaymentInterest: " << fullPaymentInterest
-
822 << ", roundedFullInterest: " << roundedFullInterest
-
823 << ", roundedFullManagementFee: " << roundedFullManagementFee
-
824 << ", untrackedInterest: " << full.untrackedInterest;
-
825
-
826 if (amount < full.totalDue)
-
827 // If the payment is less than the full payment amount, it's not
-
828 // sufficient to be a full payment.
- -
830
-
831 return full;
-
832}
+
711
+
712/* Computes payment components for paying off a loan early (before final
+
713 * payment).
+
714 *
+
715 * A full payment closes the loan immediately, paying off all outstanding
+
716 * balances plus a prepayment penalty and any accrued interest since the last
+
717 * payment. This is different from the final scheduled payment, which has no
+
718 * prepayment penalty.
+
719 *
+
720 * The function calculates:
+
721 * - Accrued interest since last payment (time-based)
+
722 * - Prepayment penalty (percentage of remaining principal)
+
723 * - Close payment fee (fixed fee for early closure)
+
724 * - All remaining principal and outstanding fees
+
725 *
+
726 * The loan's value may increase or decrease depending on whether the prepayment
+
727 * penalty exceeds the scheduled interest that would have been paid.
+
728 *
+
729 * Implements equation (26) from XLS-66 spec, Section A-2 Equation Glossary
+
730 */
+ +
+ +
733 Asset const& asset,
+
734 ApplyView& view,
+
735 Number const& principalOutstanding,
+
736 Number const& managementFeeOutstanding,
+
737 Number const& periodicPayment,
+
738 std::uint32_t paymentRemaining,
+
739 std::uint32_t prevPaymentDate,
+
740 std::uint32_t const startDate,
+
741 std::uint32_t const paymentInterval,
+
742 TenthBips32 const closeInterestRate,
+
743 std::int32_t loanScale,
+
744 Number const& totalInterestOutstanding,
+
745 Number const& periodicRate,
+
746 Number const& closePaymentFee,
+
747 STAmount const& amount,
+
748 TenthBips16 managementFeeRate,
+ +
750{
+
751 // Full payment must be made before the final scheduled payment.
+
752 if (paymentRemaining <= 1)
+
753 {
+
754 // If this is the last payment, it has to be a regular payment
+
755 JLOG(j.warn()) << "Last payment cannot be a full payment.";
+
756 return Unexpected(tecKILLED);
+
757 }
+
758
+
759 // Calculate the theoretical principal based on the payment schedule.
+
760 // This theoretical (unrounded) value is used to compute interest and
+
761 // penalties accurately.
+
762 Number const theoreticalPrincipalOutstanding =
+
763 loanPrincipalFromPeriodicPayment(periodicPayment, periodicRate, paymentRemaining);
+
764
+
765 // Full payment interest includes both accrued interest (time since last
+
766 // payment) and prepayment penalty (for closing early).
+
767 auto const fullPaymentInterest = computeFullPaymentInterest(
+
768 theoreticalPrincipalOutstanding,
+
769 periodicRate,
+
770 view.parentCloseTime(),
+
771 paymentInterval,
+
772 prevPaymentDate,
+
773 startDate,
+
774 closeInterestRate);
+
775
+
776 // Split the full payment interest into net interest (to vault) and
+
777 // management fee (to broker), applying proper rounding.
+
778 auto const [roundedFullInterest, roundedFullManagementFee] = [&]() {
+
779 auto const interest = roundToAsset(asset, fullPaymentInterest, loanScale, Number::downward);
+
780 return computeInterestAndFeeParts(asset, interest, managementFeeRate, loanScale);
+
781 }();
+
782
+ + +
785 // Pay off all tracked outstanding balances: principal, interest,
+
786 // and fees.
+
787 // This marks the loan as complete (final payment).
+
788 .trackedValueDelta = principalOutstanding + totalInterestOutstanding + managementFeeOutstanding,
+
789 .trackedPrincipalDelta = principalOutstanding,
+
790
+
791 // All outstanding management fees are paid. This zeroes out the
+
792 // tracked fee balance.
+
793 .trackedManagementFeeDelta = managementFeeOutstanding,
+
794 .specialCase = PaymentSpecialCase::final,
+
795 },
+
796
+
797 // Untracked management fee includes:
+
798 // 1. Close payment fee (fixed fee for early closure)
+
799 // 2. Management fee on the full payment interest
+
800 // 3. Minus the outstanding tracked fee (already accounted for above)
+
801 // This can be negative because the outstanding fee is subtracted, but
+
802 // it gets combined with trackedManagementFeeDelta in the final
+
803 // accounting.
+
804 closePaymentFee + roundedFullManagementFee - managementFeeOutstanding,
+
805
+
806 // Value change represents the difference between what the loan was
+
807 // expected to earn (totalInterestOutstanding) and what it actually
+
808 // earns (roundedFullInterest with prepayment penalty).
+
809 // - Positive: Prepayment penalty exceeds scheduled interest (loan value
+
810 // increases)
+
811 // - Negative: Prepayment penalty is less than scheduled interest (loan
+
812 // value decreases)
+
813 roundedFullInterest - totalInterestOutstanding,
+
814 };
+
815
+
816 XRPL_ASSERT_PARTS(
+
817 isRounded(asset, full.totalDue, loanScale), "xrpl::detail::computeFullPayment", "total due is rounded");
+
818
+
819 JLOG(j.trace()) << "computeFullPayment result: periodicPayment: " << periodicPayment
+
820 << ", periodicRate: " << periodicRate << ", paymentRemaining: " << paymentRemaining
+
821 << ", theoreticalPrincipalOutstanding: " << theoreticalPrincipalOutstanding
+
822 << ", fullPaymentInterest: " << fullPaymentInterest
+
823 << ", roundedFullInterest: " << roundedFullInterest
+
824 << ", roundedFullManagementFee: " << roundedFullManagementFee
+
825 << ", untrackedInterest: " << full.untrackedInterest;
+
826
+
827 if (amount < full.totalDue)
+
828 // If the payment is less than the full payment amount, it's not
+
829 // sufficient to be a full payment.
+ +
831
+
832 return full;
+
833}
-
833
-
834Number
-
- -
836{
- -
838}
+
834
+
835Number
+ -
839
-
840/* Computes the breakdown of a regular periodic payment into principal,
-
841 * interest, and management fee components.
-
842 *
-
843 * This function determines how a single scheduled payment should be split among
-
844 * the three tracked loan components. The calculation accounts for accumulated
-
845 * rounding errors.
-
846 *
-
847 * The algorithm:
-
848 * 1. Calculate what the loan state SHOULD be after this payment (target)
-
849 * 2. Compare current state to target to get deltas
-
850 * 3. Adjust deltas to handle rounding artifacts and edge cases
-
851 * 4. Ensure deltas don't exceed available balances or payment amount
-
852 *
-
853 * Special handling for the final payment: all remaining balances are paid off
-
854 * regardless of the periodic payment amount.
-
855 *
-
856 * Implements the pseudo-code function `compute_payment_due()`.
-
857 */
- -
- -
860 Asset const& asset,
-
861 std::int32_t scale,
-
862 Number const& totalValueOutstanding,
-
863 Number const& principalOutstanding,
-
864 Number const& managementFeeOutstanding,
-
865 Number const& periodicPayment,
-
866 Number const& periodicRate,
-
867 std::uint32_t paymentRemaining,
-
868 TenthBips16 managementFeeRate)
-
869{
-
870 XRPL_ASSERT_PARTS(
-
871 isRounded(asset, totalValueOutstanding, scale) && isRounded(asset, principalOutstanding, scale) &&
-
872 isRounded(asset, managementFeeOutstanding, scale),
-
873 "xrpl::detail::computePaymentComponents",
-
874 "Outstanding values are rounded");
-
875 XRPL_ASSERT_PARTS(paymentRemaining > 0, "xrpl::detail::computePaymentComponents", "some payments remaining");
-
876
-
877 auto const roundedPeriodicPayment = roundPeriodicPayment(asset, periodicPayment, scale);
-
878
-
879 // Final payment: pay off everything remaining, ignoring the normal
-
880 // periodic payment amount. This ensures the loan completes cleanly.
-
881 if (paymentRemaining == 1 || totalValueOutstanding <= roundedPeriodicPayment)
-
882 {
-
883 // If there's only one payment left, we need to pay off each of the loan
-
884 // parts.
-
885 return PaymentComponents{
-
886 .trackedValueDelta = totalValueOutstanding,
-
887 .trackedPrincipalDelta = principalOutstanding,
-
888 .trackedManagementFeeDelta = managementFeeOutstanding,
-
889 .specialCase = PaymentSpecialCase::final};
-
890 }
-
891
-
892 // Calculate what the loan state SHOULD be after this payment (the target).
-
893 // This is computed at full precision using the theoretical amortization.
-
894 LoanState const trueTarget =
-
895 computeTheoreticalLoanState(periodicPayment, periodicRate, paymentRemaining - 1, managementFeeRate);
-
896
-
897 // Round the target to the loan's scale to match how actual loan values
-
898 // are stored.
-
899 LoanState const roundedTarget = LoanState{
-
900 .valueOutstanding = roundToAsset(asset, trueTarget.valueOutstanding, scale),
-
901 .principalOutstanding = roundToAsset(asset, trueTarget.principalOutstanding, scale),
-
902 .interestDue = roundToAsset(asset, trueTarget.interestDue, scale),
-
903 .managementFeeDue = roundToAsset(asset, trueTarget.managementFeeDue, scale)};
-
904
-
905 // Get the current actual loan state from the ledger values
-
906 LoanState const currentLedgerState =
-
907 constructLoanState(totalValueOutstanding, principalOutstanding, managementFeeOutstanding);
-
908
-
909 // The difference between current and target states gives us the payment
-
910 // components. Any discrepancies from accumulated rounding are captured
-
911 // here.
-
912
-
913 LoanStateDeltas deltas = currentLedgerState - roundedTarget;
-
914
-
915 // Rounding can occasionally produce negative deltas. Zero them out.
-
916 deltas.nonNegative();
-
917
-
918 XRPL_ASSERT_PARTS(
-
919 deltas.principal <= currentLedgerState.principalOutstanding,
-
920 "xrpl::detail::computePaymentComponents",
-
921 "principal delta not greater than outstanding");
-
922
-
923 // Cap each component to never exceed what's actually outstanding
-
924 deltas.principal = std::min(deltas.principal, currentLedgerState.principalOutstanding);
-
925
-
926 XRPL_ASSERT_PARTS(
-
927 deltas.interest <= currentLedgerState.interestDue,
-
928 "xrpl::detail::computePaymentComponents",
-
929 "interest due delta not greater than outstanding");
-
930
-
931 // Cap interest to both the outstanding amount AND what's left of the
-
932 // periodic payment after principal is paid
-
933 deltas.interest = std::min(
-
934 {deltas.interest,
-
935 std::max(numZero, roundedPeriodicPayment - deltas.principal),
-
936 currentLedgerState.interestDue});
-
937
-
938 XRPL_ASSERT_PARTS(
-
939 deltas.managementFee <= currentLedgerState.managementFeeDue,
-
940 "xrpl::detail::computePaymentComponents",
-
941 "management fee due delta not greater than outstanding");
-
942
-
943 // Cap management fee to both the outstanding amount AND what's left of the
-
944 // periodic payment after principal and interest are paid
-
945 deltas.managementFee = std::min(
-
946 {deltas.managementFee,
-
947 roundedPeriodicPayment - (deltas.principal + deltas.interest),
-
948 currentLedgerState.managementFeeDue});
-
949
-
950 // The shortage must never be negative, which indicates that the parts are
-
951 // trying to take more than the whole payment. The excess can be positive,
-
952 // which indicates that we're not going to take the whole payment amount,
-
953 // but if so, it must be small.
-
954 auto takeFrom = [](Number& component, Number& excess) {
-
955 if (excess > beast::zero)
-
956 {
-
957 auto part = std::min(component, excess);
-
958 component -= part;
-
959 excess -= part;
-
960 }
-
961 XRPL_ASSERT_PARTS(excess >= beast::zero, "xrpl::detail::computePaymentComponents", "excess non-negative");
-
962 };
-
963 // Helper to reduce deltas when they collectively exceed a limit.
-
964 // Order matters: we prefer to reduce interest first (most flexible),
-
965 // then management fee, then principal (least flexible).
-
966 auto addressExcess = [&takeFrom](LoanStateDeltas& deltas, Number& excess) {
-
967 // This order is based on where errors are the least problematic
-
968 takeFrom(deltas.interest, excess);
-
969 takeFrom(deltas.managementFee, excess);
-
970 takeFrom(deltas.principal, excess);
-
971 };
-
972
-
973 // Check if deltas exceed the total outstanding value. This should never
-
974 // happen due to earlier caps, but handle it defensively.
-
975 Number totalOverpayment = deltas.total() - currentLedgerState.valueOutstanding;
-
976
-
977 if (totalOverpayment > beast::zero)
-
978 {
-
979 // LCOV_EXCL_START
-
980 UNREACHABLE(
-
981 "xrpl::detail::computePaymentComponents : payment exceeded loan "
-
982 "state");
-
983 addressExcess(deltas, totalOverpayment);
-
984 // LCOV_EXCL_STOP
-
985 }
-
986
-
987 // Check if deltas exceed the periodic payment amount. Reduce if needed.
-
988 Number shortage = roundedPeriodicPayment - deltas.total();
-
989
-
990 XRPL_ASSERT_PARTS(
-
991 isRounded(asset, shortage, scale), "xrpl::detail::computePaymentComponents", "shortage is rounded");
-
992
-
993 if (shortage < beast::zero)
-
994 {
-
995 // Deltas exceed payment amount - reduce them proportionally
-
996 Number excess = -shortage;
-
997 addressExcess(deltas, excess);
-
998 shortage = -excess;
-
999 }
-
1000
-
1001 // At this point, shortage >= 0 means we're paying less than the full
-
1002 // periodic payment (due to rounding or component caps).
-
1003 // shortage < 0 would mean we're trying to pay more than allowed (bug).
-
1004 XRPL_ASSERT_PARTS(shortage >= beast::zero, "xrpl::detail::computePaymentComponents", "no shortage or excess");
-
1005
-
1006 // Final validation that all components are valid
-
1007 XRPL_ASSERT_PARTS(
-
1008 deltas.total() == deltas.principal + deltas.interest + deltas.managementFee,
-
1009 "xrpl::detail::computePaymentComponents",
-
1010 "total value adds up");
-
1011
-
1012 XRPL_ASSERT_PARTS(
-
1013 deltas.principal >= beast::zero && deltas.principal <= currentLedgerState.principalOutstanding,
-
1014 "xrpl::detail::computePaymentComponents",
-
1015 "valid principal result");
-
1016 XRPL_ASSERT_PARTS(
-
1017 deltas.interest >= beast::zero && deltas.interest <= currentLedgerState.interestDue,
-
1018 "xrpl::detail::computePaymentComponents",
-
1019 "valid interest result");
-
1020 XRPL_ASSERT_PARTS(
-
1021 deltas.managementFee >= beast::zero && deltas.managementFee <= currentLedgerState.managementFeeDue,
-
1022 "xrpl::detail::computePaymentComponents",
-
1023 "valid fee result");
-
1024
-
1025 XRPL_ASSERT_PARTS(
-
1026 deltas.principal + deltas.interest + deltas.managementFee > beast::zero,
-
1027 "xrpl::detail::computePaymentComponents",
-
1028 "payment parts add to payment");
-
1029
-
1030 // Final safety clamp to ensure no value exceeds its outstanding balance
-
1031 return PaymentComponents{
-
1032 .trackedValueDelta = std::clamp(deltas.total(), numZero, currentLedgerState.valueOutstanding),
-
1033 .trackedPrincipalDelta = std::clamp(deltas.principal, numZero, currentLedgerState.principalOutstanding),
-
1034 .trackedManagementFeeDelta = std::clamp(deltas.managementFee, numZero, currentLedgerState.managementFeeDue),
-
1035 };
-
1036}
+
840
+
841/* Computes the breakdown of a regular periodic payment into principal,
+
842 * interest, and management fee components.
+
843 *
+
844 * This function determines how a single scheduled payment should be split among
+
845 * the three tracked loan components. The calculation accounts for accumulated
+
846 * rounding errors.
+
847 *
+
848 * The algorithm:
+
849 * 1. Calculate what the loan state SHOULD be after this payment (target)
+
850 * 2. Compare current state to target to get deltas
+
851 * 3. Adjust deltas to handle rounding artifacts and edge cases
+
852 * 4. Ensure deltas don't exceed available balances or payment amount
+
853 *
+
854 * Special handling for the final payment: all remaining balances are paid off
+
855 * regardless of the periodic payment amount.
+
856 *
+
857 * Implements the pseudo-code function `compute_payment_due()`.
+
858 */
+ +
+ +
861 Asset const& asset,
+
862 std::int32_t scale,
+
863 Number const& totalValueOutstanding,
+
864 Number const& principalOutstanding,
+
865 Number const& managementFeeOutstanding,
+
866 Number const& periodicPayment,
+
867 Number const& periodicRate,
+
868 std::uint32_t paymentRemaining,
+
869 TenthBips16 managementFeeRate)
+
870{
+
871 XRPL_ASSERT_PARTS(
+
872 isRounded(asset, totalValueOutstanding, scale) && isRounded(asset, principalOutstanding, scale) &&
+
873 isRounded(asset, managementFeeOutstanding, scale),
+
874 "xrpl::detail::computePaymentComponents",
+
875 "Outstanding values are rounded");
+
876 XRPL_ASSERT_PARTS(paymentRemaining > 0, "xrpl::detail::computePaymentComponents", "some payments remaining");
+
877
+
878 auto const roundedPeriodicPayment = roundPeriodicPayment(asset, periodicPayment, scale);
+
879
+
880 // Final payment: pay off everything remaining, ignoring the normal
+
881 // periodic payment amount. This ensures the loan completes cleanly.
+
882 if (paymentRemaining == 1 || totalValueOutstanding <= roundedPeriodicPayment)
+
883 {
+
884 // If there's only one payment left, we need to pay off each of the loan
+
885 // parts.
+
886 return PaymentComponents{
+
887 .trackedValueDelta = totalValueOutstanding,
+
888 .trackedPrincipalDelta = principalOutstanding,
+
889 .trackedManagementFeeDelta = managementFeeOutstanding,
+
890 .specialCase = PaymentSpecialCase::final};
+
891 }
+
892
+
893 // Calculate what the loan state SHOULD be after this payment (the target).
+
894 // This is computed at full precision using the theoretical amortization.
+
895 LoanState const trueTarget =
+
896 computeTheoreticalLoanState(periodicPayment, periodicRate, paymentRemaining - 1, managementFeeRate);
+
897
+
898 // Round the target to the loan's scale to match how actual loan values
+
899 // are stored.
+
900 LoanState const roundedTarget = LoanState{
+
901 .valueOutstanding = roundToAsset(asset, trueTarget.valueOutstanding, scale),
+
902 .principalOutstanding = roundToAsset(asset, trueTarget.principalOutstanding, scale),
+
903 .interestDue = roundToAsset(asset, trueTarget.interestDue, scale),
+
904 .managementFeeDue = roundToAsset(asset, trueTarget.managementFeeDue, scale)};
+
905
+
906 // Get the current actual loan state from the ledger values
+
907 LoanState const currentLedgerState =
+
908 constructLoanState(totalValueOutstanding, principalOutstanding, managementFeeOutstanding);
+
909
+
910 // The difference between current and target states gives us the payment
+
911 // components. Any discrepancies from accumulated rounding are captured
+
912 // here.
+
913
+
914 LoanStateDeltas deltas = currentLedgerState - roundedTarget;
+
915
+
916 // Rounding can occasionally produce negative deltas. Zero them out.
+
917 deltas.nonNegative();
+
918
+
919 XRPL_ASSERT_PARTS(
+
920 deltas.principal <= currentLedgerState.principalOutstanding,
+
921 "xrpl::detail::computePaymentComponents",
+
922 "principal delta not greater than outstanding");
+
923
+
924 // Cap each component to never exceed what's actually outstanding
+
925 deltas.principal = std::min(deltas.principal, currentLedgerState.principalOutstanding);
+
926
+
927 XRPL_ASSERT_PARTS(
+
928 deltas.interest <= currentLedgerState.interestDue,
+
929 "xrpl::detail::computePaymentComponents",
+
930 "interest due delta not greater than outstanding");
+
931
+
932 // Cap interest to both the outstanding amount AND what's left of the
+
933 // periodic payment after principal is paid
+
934 deltas.interest = std::min(
+
935 {deltas.interest,
+
936 std::max(numZero, roundedPeriodicPayment - deltas.principal),
+
937 currentLedgerState.interestDue});
+
938
+
939 XRPL_ASSERT_PARTS(
+
940 deltas.managementFee <= currentLedgerState.managementFeeDue,
+
941 "xrpl::detail::computePaymentComponents",
+
942 "management fee due delta not greater than outstanding");
+
943
+
944 // Cap management fee to both the outstanding amount AND what's left of the
+
945 // periodic payment after principal and interest are paid
+
946 deltas.managementFee = std::min(
+
947 {deltas.managementFee,
+
948 roundedPeriodicPayment - (deltas.principal + deltas.interest),
+
949 currentLedgerState.managementFeeDue});
+
950
+
951 // The shortage must never be negative, which indicates that the parts are
+
952 // trying to take more than the whole payment. The excess can be positive,
+
953 // which indicates that we're not going to take the whole payment amount,
+
954 // but if so, it must be small.
+
955 auto takeFrom = [](Number& component, Number& excess) {
+
956 if (excess > beast::zero)
+
957 {
+
958 auto part = std::min(component, excess);
+
959 component -= part;
+
960 excess -= part;
+
961 }
+
962 XRPL_ASSERT_PARTS(excess >= beast::zero, "xrpl::detail::computePaymentComponents", "excess non-negative");
+
963 };
+
964 // Helper to reduce deltas when they collectively exceed a limit.
+
965 // Order matters: we prefer to reduce interest first (most flexible),
+
966 // then management fee, then principal (least flexible).
+
967 auto addressExcess = [&takeFrom](LoanStateDeltas& deltas, Number& excess) {
+
968 // This order is based on where errors are the least problematic
+
969 takeFrom(deltas.interest, excess);
+
970 takeFrom(deltas.managementFee, excess);
+
971 takeFrom(deltas.principal, excess);
+
972 };
+
973
+
974 // Check if deltas exceed the total outstanding value. This should never
+
975 // happen due to earlier caps, but handle it defensively.
+
976 Number totalOverpayment = deltas.total() - currentLedgerState.valueOutstanding;
+
977
+
978 if (totalOverpayment > beast::zero)
+
979 {
+
980 // LCOV_EXCL_START
+
981 UNREACHABLE(
+
982 "xrpl::detail::computePaymentComponents : payment exceeded loan "
+
983 "state");
+
984 addressExcess(deltas, totalOverpayment);
+
985 // LCOV_EXCL_STOP
+
986 }
+
987
+
988 // Check if deltas exceed the periodic payment amount. Reduce if needed.
+
989 Number shortage = roundedPeriodicPayment - deltas.total();
+
990
+
991 XRPL_ASSERT_PARTS(
+
992 isRounded(asset, shortage, scale), "xrpl::detail::computePaymentComponents", "shortage is rounded");
+
993
+
994 if (shortage < beast::zero)
+
995 {
+
996 // Deltas exceed payment amount - reduce them proportionally
+
997 Number excess = -shortage;
+
998 addressExcess(deltas, excess);
+
999 shortage = -excess;
+
1000 }
+
1001
+
1002 // At this point, shortage >= 0 means we're paying less than the full
+
1003 // periodic payment (due to rounding or component caps).
+
1004 // shortage < 0 would mean we're trying to pay more than allowed (bug).
+
1005 XRPL_ASSERT_PARTS(shortage >= beast::zero, "xrpl::detail::computePaymentComponents", "no shortage or excess");
+
1006
+
1007 // Final validation that all components are valid
+
1008 XRPL_ASSERT_PARTS(
+
1009 deltas.total() == deltas.principal + deltas.interest + deltas.managementFee,
+
1010 "xrpl::detail::computePaymentComponents",
+
1011 "total value adds up");
+
1012
+
1013 XRPL_ASSERT_PARTS(
+
1014 deltas.principal >= beast::zero && deltas.principal <= currentLedgerState.principalOutstanding,
+
1015 "xrpl::detail::computePaymentComponents",
+
1016 "valid principal result");
+
1017 XRPL_ASSERT_PARTS(
+
1018 deltas.interest >= beast::zero && deltas.interest <= currentLedgerState.interestDue,
+
1019 "xrpl::detail::computePaymentComponents",
+
1020 "valid interest result");
+
1021 XRPL_ASSERT_PARTS(
+
1022 deltas.managementFee >= beast::zero && deltas.managementFee <= currentLedgerState.managementFeeDue,
+
1023 "xrpl::detail::computePaymentComponents",
+
1024 "valid fee result");
+
1025
+
1026 XRPL_ASSERT_PARTS(
+
1027 deltas.principal + deltas.interest + deltas.managementFee > beast::zero,
+
1028 "xrpl::detail::computePaymentComponents",
+
1029 "payment parts add to payment");
+
1030
+
1031 // Final safety clamp to ensure no value exceeds its outstanding balance
+
1032 return PaymentComponents{
+
1033 .trackedValueDelta = std::clamp(deltas.total(), numZero, currentLedgerState.valueOutstanding),
+
1034 .trackedPrincipalDelta = std::clamp(deltas.principal, numZero, currentLedgerState.principalOutstanding),
+
1035 .trackedManagementFeeDelta = std::clamp(deltas.managementFee, numZero, currentLedgerState.managementFeeDue),
+
1036 };
+
1037}
-
1037
-
1038/* Computes payment components for an overpayment scenario.
-
1039 *
-
1040 * An overpayment occurs when a borrower pays more than the scheduled periodic
-
1041 * payment amount. The overpayment is treated as extra principal reduction,
-
1042 * but incurs a fee and potentially a penalty interest charge.
-
1043 *
-
1044 * The calculation (Section 3.2.4.2.3 from XLS-66 spec):
-
1045 * 1. Calculate gross penalty interest on the overpayment amount
-
1046 * 2. Split the gross interest into net interest and management fee
-
1047 * 3. Calculate the penalty fee
-
1048 * 4. Determine the principal portion by subtracting the interest (gross) and
-
1049 * management fee from the overpayment amount
-
1050 *
-
1051 * Unlike regular payments which follow the amortization schedule, overpayments
-
1052 * apply to principal, reducing the loan balance and future interest costs.
-
1053 *
-
1054 * Equations (20), (21) and (22) from XLS-66 spec, Section A-2 Equation Glossary
-
1055 */
-
1056ExtendedPaymentComponents
-
- -
1058 Asset const& asset,
-
1059 int32_t const loanScale,
-
1060 Number const& overpayment,
-
1061 TenthBips32 const overpaymentInterestRate,
-
1062 TenthBips32 const overpaymentFeeRate,
-
1063 TenthBips16 const managementFeeRate)
-
1064{
-
1065 XRPL_ASSERT(
-
1066 overpayment > 0 && isRounded(asset, overpayment, loanScale),
-
1067 "xrpl::detail::computeOverpaymentComponents : valid overpayment "
-
1068 "amount");
-
1069
-
1070 // First, deduct the fixed overpayment fee from the total amount.
-
1071 // This reduces the effective payment that will be applied to the loan.
-
1072 // Equation (22) from XLS-66 spec, Section A-2 Equation Glossary
-
1073 Number const overpaymentFee = roundToAsset(asset, tenthBipsOfValue(overpayment, overpaymentFeeRate), loanScale);
-
1074
-
1075 // Calculate the penalty interest on the effective payment amount.
-
1076 // This interest doesn't follow the normal amortization schedule - it's
-
1077 // a one-time charge for paying early.
-
1078 // Equation (20) and (21) from XLS-66 spec, Section A-2 Equation Glossary
-
1079 auto const [roundedOverpaymentInterest, roundedOverpaymentManagementFee] = [&]() {
-
1080 auto const interest = roundToAsset(asset, tenthBipsOfValue(overpayment, overpaymentInterestRate), loanScale);
-
1081 return detail::computeInterestAndFeeParts(asset, interest, managementFeeRate, loanScale);
-
1082 }();
-
1083
-
1084 auto const result = detail::ExtendedPaymentComponents{
-
1085 // Build the payment components, after fees and penalty
-
1086 // interest are deducted, the remainder goes entirely to principal
-
1087 // reduction.
- -
1089 .trackedValueDelta = overpayment - overpaymentFee,
-
1090 .trackedPrincipalDelta =
-
1091 overpayment - roundedOverpaymentInterest - roundedOverpaymentManagementFee - overpaymentFee,
-
1092 .trackedManagementFeeDelta = roundedOverpaymentManagementFee,
-
1093 .specialCase = detail::PaymentSpecialCase::extra},
-
1094 // Untracked management fee is the fixed overpayment fee
-
1095 overpaymentFee,
-
1096 // Untracked interest is the penalty interest charged for overpaying.
-
1097 // This is positive, representing a one-time cost, but it's typically
-
1098 // much smaller than the interest savings from reducing principal.
-
1099 // It is equal to the paymentComponents.trackedInterestPart()
-
1100 // but is kept separate for clarity.
-
1101 roundedOverpaymentInterest};
-
1102 XRPL_ASSERT_PARTS(
-
1103 result.trackedInterestPart() == roundedOverpaymentInterest,
-
1104 "xrpl::detail::computeOverpaymentComponents",
-
1105 "valid interest computation");
-
1106 return result;
-
1107}
+
1038
+
1039/* Computes payment components for an overpayment scenario.
+
1040 *
+
1041 * An overpayment occurs when a borrower pays more than the scheduled periodic
+
1042 * payment amount. The overpayment is treated as extra principal reduction,
+
1043 * but incurs a fee and potentially a penalty interest charge.
+
1044 *
+
1045 * The calculation (Section 3.2.4.2.3 from XLS-66 spec):
+
1046 * 1. Calculate gross penalty interest on the overpayment amount
+
1047 * 2. Split the gross interest into net interest and management fee
+
1048 * 3. Calculate the penalty fee
+
1049 * 4. Determine the principal portion by subtracting the interest (gross) and
+
1050 * management fee from the overpayment amount
+
1051 *
+
1052 * Unlike regular payments which follow the amortization schedule, overpayments
+
1053 * apply to principal, reducing the loan balance and future interest costs.
+
1054 *
+
1055 * Equations (20), (21) and (22) from XLS-66 spec, Section A-2 Equation Glossary
+
1056 */
+
1057ExtendedPaymentComponents
+
+ +
1059 Asset const& asset,
+
1060 int32_t const loanScale,
+
1061 Number const& overpayment,
+
1062 TenthBips32 const overpaymentInterestRate,
+
1063 TenthBips32 const overpaymentFeeRate,
+
1064 TenthBips16 const managementFeeRate)
+
1065{
+
1066 XRPL_ASSERT(
+
1067 overpayment > 0 && isRounded(asset, overpayment, loanScale),
+
1068 "xrpl::detail::computeOverpaymentComponents : valid overpayment "
+
1069 "amount");
+
1070
+
1071 // First, deduct the fixed overpayment fee from the total amount.
+
1072 // This reduces the effective payment that will be applied to the loan.
+
1073 // Equation (22) from XLS-66 spec, Section A-2 Equation Glossary
+
1074 Number const overpaymentFee = roundToAsset(asset, tenthBipsOfValue(overpayment, overpaymentFeeRate), loanScale);
+
1075
+
1076 // Calculate the penalty interest on the effective payment amount.
+
1077 // This interest doesn't follow the normal amortization schedule - it's
+
1078 // a one-time charge for paying early.
+
1079 // Equation (20) and (21) from XLS-66 spec, Section A-2 Equation Glossary
+
1080 auto const [roundedOverpaymentInterest, roundedOverpaymentManagementFee] = [&]() {
+
1081 auto const interest = roundToAsset(asset, tenthBipsOfValue(overpayment, overpaymentInterestRate), loanScale);
+
1082 return detail::computeInterestAndFeeParts(asset, interest, managementFeeRate, loanScale);
+
1083 }();
+
1084
+
1085 auto const result = detail::ExtendedPaymentComponents{
+
1086 // Build the payment components, after fees and penalty
+
1087 // interest are deducted, the remainder goes entirely to principal
+
1088 // reduction.
+ +
1090 .trackedValueDelta = overpayment - overpaymentFee,
+
1091 .trackedPrincipalDelta =
+
1092 overpayment - roundedOverpaymentInterest - roundedOverpaymentManagementFee - overpaymentFee,
+
1093 .trackedManagementFeeDelta = roundedOverpaymentManagementFee,
+
1094 .specialCase = detail::PaymentSpecialCase::extra},
+
1095 // Untracked management fee is the fixed overpayment fee
+
1096 overpaymentFee,
+
1097 // Untracked interest is the penalty interest charged for overpaying.
+
1098 // This is positive, representing a one-time cost, but it's typically
+
1099 // much smaller than the interest savings from reducing principal.
+
1100 // It is equal to the paymentComponents.trackedInterestPart()
+
1101 // but is kept separate for clarity.
+
1102 roundedOverpaymentInterest};
+
1103 XRPL_ASSERT_PARTS(
+
1104 result.trackedInterestPart() == roundedOverpaymentInterest,
+
1105 "xrpl::detail::computeOverpaymentComponents",
+
1106 "valid interest computation");
+
1107 return result;
+
1108}
-
1108
-
1109} // namespace detail
-
1110
-
1111detail::LoanStateDeltas
-
-
1112operator-(LoanState const& lhs, LoanState const& rhs)
-
1113{
- - -
1116 .interest = lhs.interestDue - rhs.interestDue,
-
1117 .managementFee = lhs.managementFeeDue - rhs.managementFeeDue,
-
1118 };
-
1119
-
1120 return result;
-
1121}
+
1109
+
1110} // namespace detail
+
1111
+
1112detail::LoanStateDeltas
+
+
1113operator-(LoanState const& lhs, LoanState const& rhs)
+
1114{
+ + +
1117 .interest = lhs.interestDue - rhs.interestDue,
+
1118 .managementFee = lhs.managementFeeDue - rhs.managementFeeDue,
+
1119 };
+
1120
+
1121 return result;
+
1122}
-
1122
-
1123LoanState
-
- -
1125{
-
1126 LoanState result{
- -
1128 .principalOutstanding = lhs.principalOutstanding - rhs.principal,
-
1129 .interestDue = lhs.interestDue - rhs.interest,
-
1130 .managementFeeDue = lhs.managementFeeDue - rhs.managementFee,
-
1131 };
-
1132
-
1133 return result;
-
1134}
+
1123
+
1124LoanState
+
+ +
1126{
+
1127 LoanState result{
+ +
1129 .principalOutstanding = lhs.principalOutstanding - rhs.principal,
+
1130 .interestDue = lhs.interestDue - rhs.interest,
+
1131 .managementFeeDue = lhs.managementFeeDue - rhs.managementFee,
+
1132 };
+
1133
+
1134 return result;
+
1135}
-
1135
-
1136LoanState
-
- -
1138{
-
1139 LoanState result{
- -
1141 .principalOutstanding = lhs.principalOutstanding + rhs.principal,
-
1142 .interestDue = lhs.interestDue + rhs.interest,
-
1143 .managementFeeDue = lhs.managementFeeDue + rhs.managementFee,
-
1144 };
-
1145
-
1146 return result;
-
1147}
+
1136
+
1137LoanState
+
+ +
1139{
+
1140 LoanState result{
+ +
1142 .principalOutstanding = lhs.principalOutstanding + rhs.principal,
+
1143 .interestDue = lhs.interestDue + rhs.interest,
+
1144 .managementFeeDue = lhs.managementFeeDue + rhs.managementFee,
+
1145 };
+
1146
+
1147 return result;
+
1148}
-
1148
-
1149TER
-
- -
1151 Asset const& vaultAsset,
-
1152 Number const& principalRequested,
-
1153 bool expectInterest,
-
1154 std::uint32_t paymentTotal,
-
1155 LoanProperties const& properties,
- -
1157{
-
1158 auto const totalInterestOutstanding = properties.loanState.valueOutstanding - principalRequested;
-
1159 // Guard 1: if there is no computed total interest over the life of the
-
1160 // loan for a non-zero interest rate, we cannot properly amortize the
-
1161 // loan
-
1162 if (expectInterest && totalInterestOutstanding <= 0)
-
1163 {
-
1164 // Unless this is a zero-interest loan, there must be some interest
-
1165 // due on the loan, even if it's (measurable) dust
-
1166 JLOG(j.warn()) << "Loan for " << principalRequested << " with interest has no interest due";
-
1167 return tecPRECISION_LOSS;
-
1168 }
-
1169 // Guard 1a: If there is any interest computed over the life of the
-
1170 // loan, for a zero interest rate, something went sideways.
-
1171 if (!expectInterest && totalInterestOutstanding > 0)
-
1172 {
-
1173 // LCOV_EXCL_START
-
1174 JLOG(j.warn()) << "Loan for " << principalRequested << " with no interest has interest due";
-
1175 return tecINTERNAL;
-
1176 // LCOV_EXCL_STOP
-
1177 }
-
1178
-
1179 // Guard 2: if the principal portion of the first periodic payment is
-
1180 // too small to be accurately represented with the given rounding mode,
-
1181 // raise an error
-
1182 if (properties.firstPaymentPrincipal <= 0)
-
1183 {
-
1184 // Check that some true (unrounded) principal is paid each period.
-
1185 // Since the first payment pays the least principal, if it's good,
-
1186 // they'll all be good. Note that the outstanding principal is
-
1187 // rounded, and may not change right away.
-
1188 JLOG(j.warn()) << "Loan is unable to pay principal.";
-
1189 return tecPRECISION_LOSS;
-
1190 }
-
1191
-
1192 // Guard 3: If the periodic payment is so small that it can't even be
-
1193 // rounded to a representable value, then the loan can't be paid. Also,
-
1194 // avoids dividing by 0.
-
1195 auto const roundedPayment = roundPeriodicPayment(vaultAsset, properties.periodicPayment, properties.loanScale);
-
1196 if (roundedPayment == beast::zero)
-
1197 {
-
1198 JLOG(j.warn()) << "Loan Periodic payment (" << properties.periodicPayment << ") rounds to 0. ";
-
1199 return tecPRECISION_LOSS;
-
1200 }
-
1201
-
1202 // Guard 4: if the rounded periodic payment is large enough that the
-
1203 // loan can't be amortized in the specified number of payments, raise an
-
1204 // error
-
1205 {
- -
1207
-
1208 if (std::int64_t const computedPayments{properties.loanState.valueOutstanding / roundedPayment};
-
1209 computedPayments != paymentTotal)
-
1210 {
-
1211 JLOG(j.warn()) << "Loan Periodic payment (" << properties.periodicPayment << ") rounding ("
-
1212 << roundedPayment << ") on a total value of " << properties.loanState.valueOutstanding
-
1213 << " can not complete the loan in the specified "
-
1214 "number of payments ("
-
1215 << computedPayments << " != " << paymentTotal << ")";
-
1216 return tecPRECISION_LOSS;
-
1217 }
-
1218 }
-
1219 return tesSUCCESS;
-
1220}
+
1149
+
1150TER
+
+ +
1152 Asset const& vaultAsset,
+
1153 Number const& principalRequested,
+
1154 bool expectInterest,
+
1155 std::uint32_t paymentTotal,
+
1156 LoanProperties const& properties,
+ +
1158{
+
1159 auto const totalInterestOutstanding = properties.loanState.valueOutstanding - principalRequested;
+
1160 // Guard 1: if there is no computed total interest over the life of the
+
1161 // loan for a non-zero interest rate, we cannot properly amortize the
+
1162 // loan
+
1163 if (expectInterest && totalInterestOutstanding <= 0)
+
1164 {
+
1165 // Unless this is a zero-interest loan, there must be some interest
+
1166 // due on the loan, even if it's (measurable) dust
+
1167 JLOG(j.warn()) << "Loan for " << principalRequested << " with interest has no interest due";
+
1168 return tecPRECISION_LOSS;
+
1169 }
+
1170 // Guard 1a: If there is any interest computed over the life of the
+
1171 // loan, for a zero interest rate, something went sideways.
+
1172 if (!expectInterest && totalInterestOutstanding > 0)
+
1173 {
+
1174 // LCOV_EXCL_START
+
1175 JLOG(j.warn()) << "Loan for " << principalRequested << " with no interest has interest due";
+
1176 return tecINTERNAL;
+
1177 // LCOV_EXCL_STOP
+
1178 }
+
1179
+
1180 // Guard 2: if the principal portion of the first periodic payment is
+
1181 // too small to be accurately represented with the given rounding mode,
+
1182 // raise an error
+
1183 if (properties.firstPaymentPrincipal <= 0)
+
1184 {
+
1185 // Check that some true (unrounded) principal is paid each period.
+
1186 // Since the first payment pays the least principal, if it's good,
+
1187 // they'll all be good. Note that the outstanding principal is
+
1188 // rounded, and may not change right away.
+
1189 JLOG(j.warn()) << "Loan is unable to pay principal.";
+
1190 return tecPRECISION_LOSS;
+
1191 }
+
1192
+
1193 // Guard 3: If the periodic payment is so small that it can't even be
+
1194 // rounded to a representable value, then the loan can't be paid. Also,
+
1195 // avoids dividing by 0.
+
1196 auto const roundedPayment = roundPeriodicPayment(vaultAsset, properties.periodicPayment, properties.loanScale);
+
1197 if (roundedPayment == beast::zero)
+
1198 {
+
1199 JLOG(j.warn()) << "Loan Periodic payment (" << properties.periodicPayment << ") rounds to 0. ";
+
1200 return tecPRECISION_LOSS;
+
1201 }
+
1202
+
1203 // Guard 4: if the rounded periodic payment is large enough that the
+
1204 // loan can't be amortized in the specified number of payments, raise an
+
1205 // error
+
1206 {
+ +
1208
+
1209 if (std::int64_t const computedPayments{properties.loanState.valueOutstanding / roundedPayment};
+
1210 computedPayments != paymentTotal)
+
1211 {
+
1212 JLOG(j.warn()) << "Loan Periodic payment (" << properties.periodicPayment << ") rounding ("
+
1213 << roundedPayment << ") on a total value of " << properties.loanState.valueOutstanding
+
1214 << " can not complete the loan in the specified "
+
1215 "number of payments ("
+
1216 << computedPayments << " != " << paymentTotal << ")";
+
1217 return tecPRECISION_LOSS;
+
1218 }
+
1219 }
+
1220 return tesSUCCESS;
+
1221}
-
1221
-
1222/*
-
1223 * This function calculates the full payment interest accrued since the last
-
1224 * payment, plus any prepayment penalty.
-
1225 *
-
1226 * Equations (27) and (28) from XLS-66 spec, Section A-2 Equation Glossary
-
1227 */
-
1228Number
-
- -
1230 Number const& theoreticalPrincipalOutstanding,
-
1231 Number const& periodicRate,
-
1232 NetClock::time_point parentCloseTime,
-
1233 std::uint32_t paymentInterval,
-
1234 std::uint32_t prevPaymentDate,
-
1235 std::uint32_t startDate,
-
1236 TenthBips32 closeInterestRate)
-
1237{
-
1238 auto const accruedInterest = detail::loanAccruedInterest(
-
1239 theoreticalPrincipalOutstanding, periodicRate, parentCloseTime, startDate, prevPaymentDate, paymentInterval);
-
1240 XRPL_ASSERT(
-
1241 accruedInterest >= 0,
-
1242 "xrpl::detail::computeFullPaymentInterest : valid accrued "
-
1243 "interest");
-
1244
-
1245 // Equation (28) from XLS-66 spec, Section A-2 Equation Glossary
-
1246 auto const prepaymentPenalty = closeInterestRate == beast::zero
-
1247 ? Number{}
-
1248 : tenthBipsOfValue(theoreticalPrincipalOutstanding, closeInterestRate);
-
1249
-
1250 XRPL_ASSERT(
-
1251 prepaymentPenalty >= 0,
-
1252 "xrpl::detail::computeFullPaymentInterest : valid prepayment "
-
1253 "interest");
-
1254
-
1255 // Part of equation (27) from XLS-66 spec, Section A-2 Equation Glossary
-
1256 return accruedInterest + prepaymentPenalty;
-
1257}
+
1222
+
1223/*
+
1224 * This function calculates the full payment interest accrued since the last
+
1225 * payment, plus any prepayment penalty.
+
1226 *
+
1227 * Equations (27) and (28) from XLS-66 spec, Section A-2 Equation Glossary
+
1228 */
+
1229Number
+
+ +
1231 Number const& theoreticalPrincipalOutstanding,
+
1232 Number const& periodicRate,
+
1233 NetClock::time_point parentCloseTime,
+
1234 std::uint32_t paymentInterval,
+
1235 std::uint32_t prevPaymentDate,
+
1236 std::uint32_t startDate,
+
1237 TenthBips32 closeInterestRate)
+
1238{
+
1239 auto const accruedInterest = detail::loanAccruedInterest(
+
1240 theoreticalPrincipalOutstanding, periodicRate, parentCloseTime, startDate, prevPaymentDate, paymentInterval);
+
1241 XRPL_ASSERT(
+
1242 accruedInterest >= 0,
+
1243 "xrpl::detail::computeFullPaymentInterest : valid accrued "
+
1244 "interest");
+
1245
+
1246 // Equation (28) from XLS-66 spec, Section A-2 Equation Glossary
+
1247 auto const prepaymentPenalty = closeInterestRate == beast::zero
+
1248 ? Number{}
+
1249 : tenthBipsOfValue(theoreticalPrincipalOutstanding, closeInterestRate);
+
1250
+
1251 XRPL_ASSERT(
+
1252 prepaymentPenalty >= 0,
+
1253 "xrpl::detail::computeFullPaymentInterest : valid prepayment "
+
1254 "interest");
+
1255
+
1256 // Part of equation (27) from XLS-66 spec, Section A-2 Equation Glossary
+
1257 return accruedInterest + prepaymentPenalty;
+
1258}
-
1258
-
1259/* Calculates the theoretical loan state at maximum precision for a given point
-
1260 * in the amortization schedule.
-
1261 *
-
1262 * This function computes what the loan's outstanding balances should be based
-
1263 * on the periodic payment amount and number of payments remaining,
-
1264 * without considering any rounding that may have been applied to the actual
-
1265 * Loan object's state. This "theoretical" (unrounded) state is used as a target
-
1266 * for computing payment components and validating that the loan's tracked state
-
1267 * hasn't drifted too far from the theoretical values.
-
1268 *
-
1269 * The theoretical state serves several purposes:
-
1270 * 1. Computing the expected payment breakdown (principal, interest, fees)
-
1271 * 2. Detecting and correcting rounding errors that accumulate over time
-
1272 * 3. Validating that overpayments are calculated correctly
-
1273 * 4. Ensuring the loan will be fully paid off at the end of its term
-
1274 *
-
1275 * If paymentRemaining is 0, returns a fully zeroed-out LoanState,
-
1276 * representing a completely paid-off loan.
-
1277 *
-
1278 * Implements the `calculate_true_loan_state` function from the XLS-66 spec
-
1279 * section 3.2.4.4 Transaction Pseudo-code
-
1280 */
-
1281LoanState
-
- -
1283 Number const& periodicPayment,
-
1284 Number const& periodicRate,
-
1285 std::uint32_t const paymentRemaining,
-
1286 TenthBips32 const managementFeeRate)
-
1287{
-
1288 if (paymentRemaining == 0)
-
1289 {
-
1290 return LoanState{.valueOutstanding = 0, .principalOutstanding = 0, .interestDue = 0, .managementFeeDue = 0};
-
1291 }
-
1292
-
1293 // Equation (30) from XLS-66 spec, Section A-2 Equation Glossary
-
1294 Number const totalValueOutstanding = periodicPayment * paymentRemaining;
-
1295
-
1296 Number const principalOutstanding =
-
1297 detail::loanPrincipalFromPeriodicPayment(periodicPayment, periodicRate, paymentRemaining);
-
1298
-
1299 // Equation (31) from XLS-66 spec, Section A-2 Equation Glossary
-
1300 Number const interestOutstandingGross = totalValueOutstanding - principalOutstanding;
-
1301
-
1302 // Equation (32) from XLS-66 spec, Section A-2 Equation Glossary
-
1303 Number const managementFeeOutstanding = tenthBipsOfValue(interestOutstandingGross, managementFeeRate);
-
1304
-
1305 // Equation (33) from XLS-66 spec, Section A-2 Equation Glossary
-
1306 Number const interestOutstandingNet = interestOutstandingGross - managementFeeOutstanding;
-
1307
-
1308 return LoanState{
-
1309 .valueOutstanding = totalValueOutstanding,
-
1310 .principalOutstanding = principalOutstanding,
-
1311 .interestDue = interestOutstandingNet,
-
1312 .managementFeeDue = managementFeeOutstanding,
-
1313 };
-
1314};
+
1259
+
1260/* Calculates the theoretical loan state at maximum precision for a given point
+
1261 * in the amortization schedule.
+
1262 *
+
1263 * This function computes what the loan's outstanding balances should be based
+
1264 * on the periodic payment amount and number of payments remaining,
+
1265 * without considering any rounding that may have been applied to the actual
+
1266 * Loan object's state. This "theoretical" (unrounded) state is used as a target
+
1267 * for computing payment components and validating that the loan's tracked state
+
1268 * hasn't drifted too far from the theoretical values.
+
1269 *
+
1270 * The theoretical state serves several purposes:
+
1271 * 1. Computing the expected payment breakdown (principal, interest, fees)
+
1272 * 2. Detecting and correcting rounding errors that accumulate over time
+
1273 * 3. Validating that overpayments are calculated correctly
+
1274 * 4. Ensuring the loan will be fully paid off at the end of its term
+
1275 *
+
1276 * If paymentRemaining is 0, returns a fully zeroed-out LoanState,
+
1277 * representing a completely paid-off loan.
+
1278 *
+
1279 * Implements the `calculate_true_loan_state` function from the XLS-66 spec
+
1280 * section 3.2.4.4 Transaction Pseudo-code
+
1281 */
+
1282LoanState
+
+ +
1284 Number const& periodicPayment,
+
1285 Number const& periodicRate,
+
1286 std::uint32_t const paymentRemaining,
+
1287 TenthBips32 const managementFeeRate)
+
1288{
+
1289 if (paymentRemaining == 0)
+
1290 {
+
1291 return LoanState{.valueOutstanding = 0, .principalOutstanding = 0, .interestDue = 0, .managementFeeDue = 0};
+
1292 }
+
1293
+
1294 // Equation (30) from XLS-66 spec, Section A-2 Equation Glossary
+
1295 Number const totalValueOutstanding = periodicPayment * paymentRemaining;
+
1296
+
1297 Number const principalOutstanding =
+
1298 detail::loanPrincipalFromPeriodicPayment(periodicPayment, periodicRate, paymentRemaining);
+
1299
+
1300 // Equation (31) from XLS-66 spec, Section A-2 Equation Glossary
+
1301 Number const interestOutstandingGross = totalValueOutstanding - principalOutstanding;
+
1302
+
1303 // Equation (32) from XLS-66 spec, Section A-2 Equation Glossary
+
1304 Number const managementFeeOutstanding = tenthBipsOfValue(interestOutstandingGross, managementFeeRate);
+
1305
+
1306 // Equation (33) from XLS-66 spec, Section A-2 Equation Glossary
+
1307 Number const interestOutstandingNet = interestOutstandingGross - managementFeeOutstanding;
+
1308
+
1309 return LoanState{
+
1310 .valueOutstanding = totalValueOutstanding,
+
1311 .principalOutstanding = principalOutstanding,
+
1312 .interestDue = interestOutstandingNet,
+
1313 .managementFeeDue = managementFeeOutstanding,
+
1314 };
+
1315};
-
1315
-
1316/* Constructs a LoanState from rounded Loan ledger object values.
-
1317 *
-
1318 * This function creates a LoanState structure from the three tracked values
-
1319 * stored in a Loan ledger object. Unlike calculateTheoreticalLoanState(), which
-
1320 * computes theoretical unrounded values, this function works with values
-
1321 * that have already been rounded to the loan's scale.
-
1322 *
-
1323 * The key difference from calculateTheoreticalLoanState():
-
1324 * - calculateTheoreticalLoanState: Computes theoretical values at full
-
1325 * precision
-
1326 * - constructRoundedLoanState: Builds state from actual rounded ledger values
-
1327 *
-
1328 * The interestDue field is derived from the other three values rather than
-
1329 * stored directly, since it can be calculated as:
-
1330 * interestDue = totalValueOutstanding - principalOutstanding -
-
1331 * managementFeeOutstanding
-
1332 *
-
1333 * This ensures consistency across the codebase and prevents copy-paste errors
-
1334 * when creating LoanState objects from Loan ledger data.
-
1335 */
-
1336LoanState
-
- -
1338 Number const& totalValueOutstanding,
-
1339 Number const& principalOutstanding,
-
1340 Number const& managementFeeOutstanding)
-
1341{
-
1342 // This implementation is pretty trivial, but ensures the calculations
-
1343 // are consistent everywhere, and reduces copy/paste errors.
-
1344 return LoanState{
-
1345 .valueOutstanding = totalValueOutstanding,
-
1346 .principalOutstanding = principalOutstanding,
-
1347 .interestDue = totalValueOutstanding - principalOutstanding - managementFeeOutstanding,
-
1348 .managementFeeDue = managementFeeOutstanding};
-
1349}
+
1316
+
1317/* Constructs a LoanState from rounded Loan ledger object values.
+
1318 *
+
1319 * This function creates a LoanState structure from the three tracked values
+
1320 * stored in a Loan ledger object. Unlike calculateTheoreticalLoanState(), which
+
1321 * computes theoretical unrounded values, this function works with values
+
1322 * that have already been rounded to the loan's scale.
+
1323 *
+
1324 * The key difference from calculateTheoreticalLoanState():
+
1325 * - calculateTheoreticalLoanState: Computes theoretical values at full
+
1326 * precision
+
1327 * - constructRoundedLoanState: Builds state from actual rounded ledger values
+
1328 *
+
1329 * The interestDue field is derived from the other three values rather than
+
1330 * stored directly, since it can be calculated as:
+
1331 * interestDue = totalValueOutstanding - principalOutstanding -
+
1332 * managementFeeOutstanding
+
1333 *
+
1334 * This ensures consistency across the codebase and prevents copy-paste errors
+
1335 * when creating LoanState objects from Loan ledger data.
+
1336 */
+
1337LoanState
+
+ +
1339 Number const& totalValueOutstanding,
+
1340 Number const& principalOutstanding,
+
1341 Number const& managementFeeOutstanding)
+
1342{
+
1343 // This implementation is pretty trivial, but ensures the calculations
+
1344 // are consistent everywhere, and reduces copy/paste errors.
+
1345 return LoanState{
+
1346 .valueOutstanding = totalValueOutstanding,
+
1347 .principalOutstanding = principalOutstanding,
+
1348 .interestDue = totalValueOutstanding - principalOutstanding - managementFeeOutstanding,
+
1349 .managementFeeDue = managementFeeOutstanding};
+
1350}
-
1350
-
1351LoanState
-
- -
1353{
-
1354 return constructLoanState(
-
1355 loan->at(sfTotalValueOutstanding), loan->at(sfPrincipalOutstanding), loan->at(sfManagementFeeOutstanding));
-
1356}
+
1351
+
1352LoanState
+
+ +
1354{
+
1355 return constructLoanState(
+
1356 loan->at(sfTotalValueOutstanding), loan->at(sfPrincipalOutstanding), loan->at(sfManagementFeeOutstanding));
+
1357}
-
1357
-
1358/*
-
1359 * This function calculates the fee owed to the broker based on the asset,
-
1360 * value, and management fee rate.
-
1361 *
-
1362 * Equation (32) from XLS-66 spec, Section A-2 Equation Glossary
-
1363 */
-
1364Number
-
-
1365computeManagementFee(Asset const& asset, Number const& value, TenthBips32 managementFeeRate, std::int32_t scale)
-
1366{
-
1367 return roundToAsset(asset, tenthBipsOfValue(value, managementFeeRate), scale, Number::downward);
-
1368}
+
1358
+
1359/*
+
1360 * This function calculates the fee owed to the broker based on the asset,
+
1361 * value, and management fee rate.
+
1362 *
+
1363 * Equation (32) from XLS-66 spec, Section A-2 Equation Glossary
+
1364 */
+
1365Number
+
+
1366computeManagementFee(Asset const& asset, Number const& value, TenthBips32 managementFeeRate, std::int32_t scale)
+
1367{
+
1368 return roundToAsset(asset, tenthBipsOfValue(value, managementFeeRate), scale, Number::downward);
+
1369}
-
1369
-
1370/*
-
1371 * Given the loan parameters, compute the derived properties of the loan.
-
1372 *
-
1373 * Pulls together several formulas from the XLS-66 spec, which are noted at each
-
1374 * step, plus the concepts from 3.2.4.3 Conceptual Loan Value. They are used for
-
1375 * to check some of the conditions in 3.2.1.5 Failure Conditions for the LoanSet
-
1376 * transaction.
-
1377 */
-
1378LoanProperties
-
- -
1380 Asset const& asset,
-
1381 Number const& principalOutstanding,
-
1382 TenthBips32 interestRate,
-
1383 std::uint32_t paymentInterval,
-
1384 std::uint32_t paymentsRemaining,
-
1385 TenthBips32 managementFeeRate,
-
1386 std::int32_t minimumScale)
-
1387{
-
1388 auto const periodicRate = loanPeriodicRate(interestRate, paymentInterval);
-
1389 XRPL_ASSERT(interestRate == 0 || periodicRate > 0, "xrpl::computeLoanProperties : valid rate");
-
1390 return computeLoanProperties(
-
1391 asset, principalOutstanding, periodicRate, paymentsRemaining, managementFeeRate, minimumScale);
-
1392}
+
1370
+
1371/*
+
1372 * Given the loan parameters, compute the derived properties of the loan.
+
1373 *
+
1374 * Pulls together several formulas from the XLS-66 spec, which are noted at each
+
1375 * step, plus the concepts from 3.2.4.3 Conceptual Loan Value. They are used for
+
1376 * to check some of the conditions in 3.2.1.5 Failure Conditions for the LoanSet
+
1377 * transaction.
+
1378 */
+
1379LoanProperties
+
+ +
1381 Asset const& asset,
+
1382 Number const& principalOutstanding,
+
1383 TenthBips32 interestRate,
+
1384 std::uint32_t paymentInterval,
+
1385 std::uint32_t paymentsRemaining,
+
1386 TenthBips32 managementFeeRate,
+
1387 std::int32_t minimumScale)
+
1388{
+
1389 auto const periodicRate = loanPeriodicRate(interestRate, paymentInterval);
+
1390 XRPL_ASSERT(interestRate == 0 || periodicRate > 0, "xrpl::computeLoanProperties : valid rate");
+
1391 return computeLoanProperties(
+
1392 asset, principalOutstanding, periodicRate, paymentsRemaining, managementFeeRate, minimumScale);
+
1393}
-
1393
-
1394/*
-
1395 * Given the loan parameters, compute the derived properties of the loan.
-
1396 *
-
1397 * Pulls together several formulas from the XLS-66 spec, which are noted at each
-
1398 * step, plus the concepts from 3.2.4.3 Conceptual Loan Value. They are used for
-
1399 * to check some of the conditions in 3.2.1.5 Failure Conditions for the LoanSet
-
1400 * transaction.
-
1401 */
-
1402LoanProperties
-
- -
1404 Asset const& asset,
-
1405 Number const& principalOutstanding,
-
1406 Number const& periodicRate,
-
1407 std::uint32_t paymentsRemaining,
-
1408 TenthBips32 managementFeeRate,
-
1409 std::int32_t minimumScale)
-
1410{
-
1411 auto const periodicPayment = detail::loanPeriodicPayment(principalOutstanding, periodicRate, paymentsRemaining);
-
1412
-
1413 auto const [totalValueOutstanding, loanScale] = [&]() {
-
1414 // only round up if there should be interest
-
1415 NumberRoundModeGuard mg(periodicRate == 0 ? Number::to_nearest : Number::upward);
-
1416 // Use STAmount's internal rounding instead of roundToAsset, because
-
1417 // we're going to use this result to determine the scale for all the
-
1418 // other rounding.
-
1419
-
1420 // Equation (30) from XLS-66 spec, Section A-2 Equation Glossary
-
1421 STAmount amount{asset, periodicPayment * paymentsRemaining};
-
1422
-
1423 // Base the loan scale on the total value, since that's going to be
-
1424 // the biggest number involved (barring unusual parameters for late,
-
1425 // full, or over payments)
-
1426 auto const loanScale = std::max(minimumScale, amount.exponent());
-
1427 XRPL_ASSERT_PARTS(
-
1428 (amount.integral() && loanScale == 0) ||
-
1429 (!amount.integral() && loanScale >= static_cast<Number>(amount).exponent()),
-
1430 "xrpl::computeLoanProperties",
-
1431 "loanScale value fits expectations");
-
1432
-
1433 // We may need to truncate the total value because of the minimum
-
1434 // scale
-
1435 amount = roundToAsset(asset, amount, loanScale);
-
1436
-
1437 return std::make_pair(amount, loanScale);
-
1438 }();
-
1439
-
1440 // Since we just figured out the loan scale, we haven't been able to
-
1441 // validate that the principal fits in it, so to allow this function to
-
1442 // succeed, round it here, and let the caller do the validation.
-
1443 auto const roundedPrincipalOutstanding = roundToAsset(asset, principalOutstanding, loanScale, Number::to_nearest);
-
1444
-
1445 // Equation (31) from XLS-66 spec, Section A-2 Equation Glossary
-
1446 auto const totalInterestOutstanding = totalValueOutstanding - roundedPrincipalOutstanding;
-
1447 auto const feeOwedToBroker = computeManagementFee(asset, totalInterestOutstanding, managementFeeRate, loanScale);
-
1448
-
1449 // Compute the principal part of the first payment. This is needed
-
1450 // because the principal part may be rounded down to zero, which
-
1451 // would prevent the principal from ever being paid down.
-
1452 auto const firstPaymentPrincipal = [&]() {
-
1453 // Compute the parts for the first payment. Ensure that the
-
1454 // principal payment will actually change the principal.
-
1455 auto const startingState =
-
1456 computeTheoreticalLoanState(periodicPayment, periodicRate, paymentsRemaining, managementFeeRate);
-
1457
-
1458 auto const firstPaymentState =
-
1459 computeTheoreticalLoanState(periodicPayment, periodicRate, paymentsRemaining - 1, managementFeeRate);
-
1460
-
1461 // The unrounded principal part needs to be large enough to affect
-
1462 // the principal. What to do if not is left to the caller
-
1463 return startingState.principalOutstanding - firstPaymentState.principalOutstanding;
-
1464 }();
-
1465
-
1466 return LoanProperties{
-
1467 .periodicPayment = periodicPayment,
-
1468 .loanState = constructLoanState(totalValueOutstanding, roundedPrincipalOutstanding, feeOwedToBroker),
-
1469 .loanScale = loanScale,
-
1470 .firstPaymentPrincipal = firstPaymentPrincipal,
-
1471 };
-
1472}
+
1394
+
1395/*
+
1396 * Given the loan parameters, compute the derived properties of the loan.
+
1397 *
+
1398 * Pulls together several formulas from the XLS-66 spec, which are noted at each
+
1399 * step, plus the concepts from 3.2.4.3 Conceptual Loan Value. They are used for
+
1400 * to check some of the conditions in 3.2.1.5 Failure Conditions for the LoanSet
+
1401 * transaction.
+
1402 */
+
1403LoanProperties
+
+ +
1405 Asset const& asset,
+
1406 Number const& principalOutstanding,
+
1407 Number const& periodicRate,
+
1408 std::uint32_t paymentsRemaining,
+
1409 TenthBips32 managementFeeRate,
+
1410 std::int32_t minimumScale)
+
1411{
+
1412 auto const periodicPayment = detail::loanPeriodicPayment(principalOutstanding, periodicRate, paymentsRemaining);
+
1413
+
1414 auto const [totalValueOutstanding, loanScale] = [&]() {
+
1415 // only round up if there should be interest
+
1416 NumberRoundModeGuard mg(periodicRate == 0 ? Number::to_nearest : Number::upward);
+
1417 // Use STAmount's internal rounding instead of roundToAsset, because
+
1418 // we're going to use this result to determine the scale for all the
+
1419 // other rounding.
+
1420
+
1421 // Equation (30) from XLS-66 spec, Section A-2 Equation Glossary
+
1422 STAmount amount{asset, periodicPayment * paymentsRemaining};
+
1423
+
1424 // Base the loan scale on the total value, since that's going to be
+
1425 // the biggest number involved (barring unusual parameters for late,
+
1426 // full, or over payments)
+
1427 auto const loanScale = std::max(minimumScale, amount.exponent());
+
1428 XRPL_ASSERT_PARTS(
+
1429 (amount.integral() && loanScale == 0) ||
+
1430 (!amount.integral() && loanScale >= static_cast<Number>(amount).exponent()),
+
1431 "xrpl::computeLoanProperties",
+
1432 "loanScale value fits expectations");
+
1433
+
1434 // We may need to truncate the total value because of the minimum
+
1435 // scale
+
1436 amount = roundToAsset(asset, amount, loanScale);
+
1437
+
1438 return std::make_pair(amount, loanScale);
+
1439 }();
+
1440
+
1441 // Since we just figured out the loan scale, we haven't been able to
+
1442 // validate that the principal fits in it, so to allow this function to
+
1443 // succeed, round it here, and let the caller do the validation.
+
1444 auto const roundedPrincipalOutstanding = roundToAsset(asset, principalOutstanding, loanScale, Number::to_nearest);
+
1445
+
1446 // Equation (31) from XLS-66 spec, Section A-2 Equation Glossary
+
1447 auto const totalInterestOutstanding = totalValueOutstanding - roundedPrincipalOutstanding;
+
1448 auto const feeOwedToBroker = computeManagementFee(asset, totalInterestOutstanding, managementFeeRate, loanScale);
+
1449
+
1450 // Compute the principal part of the first payment. This is needed
+
1451 // because the principal part may be rounded down to zero, which
+
1452 // would prevent the principal from ever being paid down.
+
1453 auto const firstPaymentPrincipal = [&]() {
+
1454 // Compute the parts for the first payment. Ensure that the
+
1455 // principal payment will actually change the principal.
+
1456 auto const startingState =
+
1457 computeTheoreticalLoanState(periodicPayment, periodicRate, paymentsRemaining, managementFeeRate);
+
1458
+
1459 auto const firstPaymentState =
+
1460 computeTheoreticalLoanState(periodicPayment, periodicRate, paymentsRemaining - 1, managementFeeRate);
+
1461
+
1462 // The unrounded principal part needs to be large enough to affect
+
1463 // the principal. What to do if not is left to the caller
+
1464 return startingState.principalOutstanding - firstPaymentState.principalOutstanding;
+
1465 }();
+
1466
+
1467 return LoanProperties{
+
1468 .periodicPayment = periodicPayment,
+
1469 .loanState = constructLoanState(totalValueOutstanding, roundedPrincipalOutstanding, feeOwedToBroker),
+
1470 .loanScale = loanScale,
+
1471 .firstPaymentPrincipal = firstPaymentPrincipal,
+
1472 };
+
1473}
-
1473
-
1474/*
-
1475 * This is the main function to make a loan payment.
-
1476 * This function handles regular, late, full, and overpayments.
-
1477 * It is an implementation of the make_payment function from the XLS-66
-
1478 * spec. Section 3.2.4.4
-
1479 */
-
1480Expected<LoanPaymentParts, TER>
-
- -
1482 Asset const& asset,
-
1483 ApplyView& view,
-
1484 SLE::ref loan,
-
1485 SLE::const_ref brokerSle,
-
1486 STAmount const& amount,
-
1487 LoanPaymentType const paymentType,
- -
1489{
-
1490 using namespace Lending;
-
1491
-
1492 auto principalOutstandingProxy = loan->at(sfPrincipalOutstanding);
-
1493 auto paymentRemainingProxy = loan->at(sfPaymentRemaining);
-
1494
-
1495 if (paymentRemainingProxy == 0 || principalOutstandingProxy == 0)
-
1496 {
-
1497 // Loan complete this is already checked in LoanPay::preclaim()
-
1498 // LCOV_EXCL_START
-
1499 JLOG(j.warn()) << "Loan is already paid off.";
-
1500 return Unexpected(tecKILLED);
-
1501 // LCOV_EXCL_STOP
-
1502 }
-
1503
-
1504 auto totalValueOutstandingProxy = loan->at(sfTotalValueOutstanding);
-
1505 auto managementFeeOutstandingProxy = loan->at(sfManagementFeeOutstanding);
-
1506
-
1507 // Next payment due date must be set unless the loan is complete
-
1508 auto nextDueDateProxy = loan->at(sfNextPaymentDueDate);
-
1509 if (*nextDueDateProxy == 0)
-
1510 {
-
1511 JLOG(j.warn()) << "Loan next payment due date is not set.";
-
1512 return Unexpected(tecINTERNAL);
-
1513 }
-
1514
-
1515 std::int32_t const loanScale = loan->at(sfLoanScale);
-
1516
-
1517 TenthBips32 const interestRate{loan->at(sfInterestRate)};
-
1518
-
1519 Number const serviceFee = loan->at(sfLoanServiceFee);
-
1520 TenthBips16 const managementFeeRate{brokerSle->at(sfManagementFeeRate)};
-
1521
-
1522 Number const periodicPayment = loan->at(sfPeriodicPayment);
-
1523
-
1524 auto prevPaymentDateProxy = loan->at(sfPreviousPaymentDueDate);
-
1525 std::uint32_t const startDate = loan->at(sfStartDate);
-
1526
-
1527 std::uint32_t const paymentInterval = loan->at(sfPaymentInterval);
-
1528
-
1529 // Compute the periodic rate that will be used for calculations
-
1530 // throughout
-
1531 Number const periodicRate = loanPeriodicRate(interestRate, paymentInterval);
-
1532 XRPL_ASSERT(interestRate == 0 || periodicRate > 0, "xrpl::loanMakePayment : valid rate");
-
1533
-
1534 XRPL_ASSERT(*totalValueOutstandingProxy > 0, "xrpl::loanMakePayment : valid total value");
-
1535
-
1536 view.update(loan);
-
1537
-
1538 // -------------------------------------------------------------
-
1539 // A late payment not flagged as late overrides all other options.
-
1540 if (paymentType != LoanPaymentType::late && hasExpired(view, nextDueDateProxy))
-
1541 {
-
1542 // If the payment is late, and the late flag was not set, it's not
-
1543 // valid
-
1544 JLOG(j.warn()) << "Loan payment is overdue. Use the tfLoanLatePayment "
-
1545 "transaction "
-
1546 "flag to make a late payment. Loan was created on "
-
1547 << startDate << ", prev payment due date is " << prevPaymentDateProxy
-
1548 << ", next payment due date is " << nextDueDateProxy << ", ledger time is "
-
1549 << view.parentCloseTime().time_since_epoch().count();
-
1550 return Unexpected(tecEXPIRED);
-
1551 }
-
1552
-
1553 // -------------------------------------------------------------
-
1554 // full payment handling
-
1555 if (paymentType == LoanPaymentType::full)
-
1556 {
-
1557 TenthBips32 const closeInterestRate{loan->at(sfCloseInterestRate)};
-
1558 Number const closePaymentFee = roundToAsset(asset, loan->at(sfClosePaymentFee), loanScale);
-
1559
-
1560 LoanState const roundedLoanState =
-
1561 constructLoanState(totalValueOutstandingProxy, principalOutstandingProxy, managementFeeOutstandingProxy);
-
1562
-
1563 if (auto const fullPaymentComponents = detail::computeFullPayment(
-
1564 asset,
-
1565 view,
-
1566 principalOutstandingProxy,
-
1567 managementFeeOutstandingProxy,
-
1568 periodicPayment,
-
1569 paymentRemainingProxy,
-
1570 prevPaymentDateProxy,
-
1571 startDate,
-
1572 paymentInterval,
-
1573 closeInterestRate,
-
1574 loanScale,
-
1575 roundedLoanState.interestDue,
-
1576 periodicRate,
-
1577 closePaymentFee,
-
1578 amount,
-
1579 managementFeeRate,
-
1580 j))
-
1581 {
-
1582 return doPayment(
-
1583 *fullPaymentComponents,
-
1584 totalValueOutstandingProxy,
-
1585 principalOutstandingProxy,
-
1586 managementFeeOutstandingProxy,
-
1587 paymentRemainingProxy,
-
1588 prevPaymentDateProxy,
-
1589 nextDueDateProxy,
-
1590 paymentInterval);
-
1591 }
-
1592 else if (fullPaymentComponents.error())
-
1593 // error() will be the TER returned if a payment is not made. It
-
1594 // will only evaluate to true if it's unsuccessful. Otherwise,
-
1595 // tesSUCCESS means nothing was done, so continue.
-
1596 return Unexpected(fullPaymentComponents.error());
-
1597
-
1598 // LCOV_EXCL_START
-
1599 UNREACHABLE("xrpl::loanMakePayment : invalid full payment result");
-
1600 JLOG(j.error()) << "Full payment computation failed unexpectedly.";
-
1601 return Unexpected(tecINTERNAL);
-
1602 // LCOV_EXCL_STOP
-
1603 }
-
1604
-
1605 // -------------------------------------------------------------
-
1606 // compute the periodic payment info that will be needed whether the
-
1607 // payment is late or regular
- - -
1610 asset,
-
1611 loanScale,
-
1612 totalValueOutstandingProxy,
-
1613 principalOutstandingProxy,
-
1614 managementFeeOutstandingProxy,
-
1615 periodicPayment,
-
1616 periodicRate,
-
1617 paymentRemainingProxy,
-
1618 managementFeeRate),
-
1619 serviceFee};
-
1620 XRPL_ASSERT_PARTS(periodic.trackedPrincipalDelta >= 0, "xrpl::loanMakePayment", "regular payment valid principal");
-
1621
-
1622 // -------------------------------------------------------------
-
1623 // late payment handling
-
1624 if (paymentType == LoanPaymentType::late)
-
1625 {
-
1626 TenthBips32 const lateInterestRate{loan->at(sfLateInterestRate)};
-
1627 Number const latePaymentFee = loan->at(sfLatePaymentFee);
-
1628
-
1629 if (auto const latePaymentComponents = detail::computeLatePayment(
-
1630 asset,
-
1631 view,
-
1632 principalOutstandingProxy,
-
1633 nextDueDateProxy,
-
1634 periodic,
-
1635 lateInterestRate,
-
1636 loanScale,
-
1637 latePaymentFee,
-
1638 amount,
-
1639 managementFeeRate,
-
1640 j))
-
1641 {
-
1642 return doPayment(
-
1643 *latePaymentComponents,
-
1644 totalValueOutstandingProxy,
-
1645 principalOutstandingProxy,
-
1646 managementFeeOutstandingProxy,
-
1647 paymentRemainingProxy,
-
1648 prevPaymentDateProxy,
-
1649 nextDueDateProxy,
-
1650 paymentInterval);
-
1651 }
-
1652 else if (latePaymentComponents.error())
-
1653 {
-
1654 // error() will be the TER returned if a payment is not made. It
-
1655 // will only evaluate to true if it's unsuccessful.
-
1656 return Unexpected(latePaymentComponents.error());
-
1657 }
-
1658
-
1659 // LCOV_EXCL_START
-
1660 UNREACHABLE("xrpl::loanMakePayment : invalid late payment result");
-
1661 JLOG(j.error()) << "Late payment computation failed unexpectedly.";
-
1662 return Unexpected(tecINTERNAL);
-
1663 // LCOV_EXCL_STOP
-
1664 }
-
1665
-
1666 // -------------------------------------------------------------
-
1667 // regular periodic payment handling
-
1668
-
1669 XRPL_ASSERT_PARTS(
-
1670 paymentType == LoanPaymentType::regular || paymentType == LoanPaymentType::overpayment,
-
1671 "xrpl::loanMakePayment",
-
1672 "regular payment type");
-
1673
-
1674 // Keep a running total of the actual parts paid
-
1675 LoanPaymentParts totalParts;
-
1676 Number totalPaid;
-
1677 std::size_t numPayments = 0;
-
1678
-
1679 while ((amount >= (totalPaid + periodic.totalDue)) && paymentRemainingProxy > 0 &&
-
1680 numPayments < loanMaximumPaymentsPerTransaction)
-
1681 {
-
1682 // Try to make more payments
-
1683 XRPL_ASSERT_PARTS(
-
1684 periodic.trackedPrincipalDelta >= 0, "xrpl::loanMakePayment", "payment pays non-negative principal");
-
1685
-
1686 totalPaid += periodic.totalDue;
-
1687 totalParts += detail::doPayment(
-
1688 periodic,
-
1689 totalValueOutstandingProxy,
-
1690 principalOutstandingProxy,
-
1691 managementFeeOutstandingProxy,
-
1692 paymentRemainingProxy,
-
1693 prevPaymentDateProxy,
-
1694 nextDueDateProxy,
-
1695 paymentInterval);
-
1696 ++numPayments;
-
1697
-
1698 XRPL_ASSERT_PARTS(
-
1699 (periodic.specialCase == detail::PaymentSpecialCase::final) == (paymentRemainingProxy == 0),
-
1700 "xrpl::loanMakePayment",
-
1701 "final payment is the final payment");
-
1702
-
1703 // Don't compute the next payment if this was the last payment
-
1704 if (periodic.specialCase == detail::PaymentSpecialCase::final)
-
1705 break;
-
1706
- - -
1709 asset,
-
1710 loanScale,
-
1711 totalValueOutstandingProxy,
-
1712 principalOutstandingProxy,
-
1713 managementFeeOutstandingProxy,
-
1714 periodicPayment,
-
1715 periodicRate,
-
1716 paymentRemainingProxy,
-
1717 managementFeeRate),
-
1718 serviceFee};
-
1719 }
-
1720
-
1721 if (numPayments == 0)
-
1722 {
-
1723 JLOG(j.warn()) << "Regular loan payment amount is insufficient. Due: " << periodic.totalDue
-
1724 << ", paid: " << amount;
- -
1726 }
-
1727
-
1728 XRPL_ASSERT_PARTS(
-
1729 totalParts.principalPaid + totalParts.interestPaid + totalParts.feePaid == totalPaid,
-
1730 "xrpl::loanMakePayment",
-
1731 "payment parts add up");
-
1732 XRPL_ASSERT_PARTS(totalParts.valueChange == 0, "xrpl::loanMakePayment", "no value change");
-
1733
-
1734 // -------------------------------------------------------------
-
1735 // overpayment handling
-
1736 if (paymentType == LoanPaymentType::overpayment && loan->isFlag(lsfLoanOverpayment) && paymentRemainingProxy > 0 &&
-
1737 totalPaid < amount && numPayments < loanMaximumPaymentsPerTransaction)
-
1738 {
-
1739 TenthBips32 const overpaymentInterestRate{loan->at(sfOverpaymentInterestRate)};
-
1740 TenthBips32 const overpaymentFeeRate{loan->at(sfOverpaymentFee)};
-
1741
-
1742 // It shouldn't be possible for the overpayment to be greater than
-
1743 // totalValueOutstanding, because that would have been processed as
-
1744 // another normal payment. But cap it just in case.
-
1745 Number const overpayment = std::min(amount - totalPaid, *totalValueOutstandingProxy);
-
1746
- -
1748 asset, loanScale, overpayment, overpaymentInterestRate, overpaymentFeeRate, managementFeeRate);
-
1749
-
1750 // Don't process an overpayment if the whole amount (or more!)
-
1751 // gets eaten by fees and interest.
-
1752 if (overpaymentComponents.trackedPrincipalDelta > 0)
-
1753 {
-
1754 XRPL_ASSERT_PARTS(
-
1755 overpaymentComponents.untrackedInterest >= beast::zero,
-
1756 "xrpl::loanMakePayment",
-
1757 "overpayment penalty did not reduce value of loan");
-
1758 // Can't just use `periodicPayment` here, because it might
-
1759 // change
-
1760 auto periodicPaymentProxy = loan->at(sfPeriodicPayment);
-
1761 if (auto const overResult = detail::doOverpayment(
-
1762 asset,
-
1763 loanScale,
-
1764 overpaymentComponents,
-
1765 totalValueOutstandingProxy,
-
1766 principalOutstandingProxy,
-
1767 managementFeeOutstandingProxy,
-
1768 periodicPaymentProxy,
-
1769 periodicRate,
-
1770 paymentRemainingProxy,
-
1771 managementFeeRate,
-
1772 j))
-
1773 totalParts += *overResult;
-
1774 else if (overResult.error())
-
1775 // error() will be the TER returned if a payment is not
-
1776 // made. It will only evaluate to true if it's unsuccessful.
-
1777 // Otherwise, tesSUCCESS means nothing was done, so
-
1778 // continue.
-
1779 return Unexpected(overResult.error());
-
1780 }
-
1781 }
-
1782
-
1783 // Check the final results are rounded, to double-check that the
-
1784 // intermediate steps were rounded.
-
1785 XRPL_ASSERT(
-
1786 isRounded(asset, totalParts.principalPaid, loanScale) && totalParts.principalPaid >= beast::zero,
-
1787 "xrpl::loanMakePayment : total principal paid is valid");
-
1788 XRPL_ASSERT(
-
1789 isRounded(asset, totalParts.interestPaid, loanScale) && totalParts.interestPaid >= beast::zero,
-
1790 "xrpl::loanMakePayment : total interest paid is valid");
-
1791 XRPL_ASSERT(
-
1792 isRounded(asset, totalParts.valueChange, loanScale), "xrpl::loanMakePayment : loan value change is valid");
-
1793 XRPL_ASSERT(
-
1794 isRounded(asset, totalParts.feePaid, loanScale) && totalParts.feePaid >= beast::zero,
-
1795 "xrpl::loanMakePayment : fee paid is valid");
-
1796 return totalParts;
-
1797}
+
1474
+
1475/*
+
1476 * This is the main function to make a loan payment.
+
1477 * This function handles regular, late, full, and overpayments.
+
1478 * It is an implementation of the make_payment function from the XLS-66
+
1479 * spec. Section 3.2.4.4
+
1480 */
+
1481Expected<LoanPaymentParts, TER>
+
+ +
1483 Asset const& asset,
+
1484 ApplyView& view,
+
1485 SLE::ref loan,
+
1486 SLE::const_ref brokerSle,
+
1487 STAmount const& amount,
+
1488 LoanPaymentType const paymentType,
+ +
1490{
+
1491 using namespace Lending;
+
1492
+
1493 auto principalOutstandingProxy = loan->at(sfPrincipalOutstanding);
+
1494 auto paymentRemainingProxy = loan->at(sfPaymentRemaining);
+
1495
+
1496 if (paymentRemainingProxy == 0 || principalOutstandingProxy == 0)
+
1497 {
+
1498 // Loan complete this is already checked in LoanPay::preclaim()
+
1499 // LCOV_EXCL_START
+
1500 JLOG(j.warn()) << "Loan is already paid off.";
+
1501 return Unexpected(tecKILLED);
+
1502 // LCOV_EXCL_STOP
+
1503 }
+
1504
+
1505 auto totalValueOutstandingProxy = loan->at(sfTotalValueOutstanding);
+
1506 auto managementFeeOutstandingProxy = loan->at(sfManagementFeeOutstanding);
+
1507
+
1508 // Next payment due date must be set unless the loan is complete
+
1509 auto nextDueDateProxy = loan->at(sfNextPaymentDueDate);
+
1510 if (*nextDueDateProxy == 0)
+
1511 {
+
1512 JLOG(j.warn()) << "Loan next payment due date is not set.";
+
1513 return Unexpected(tecINTERNAL);
+
1514 }
+
1515
+
1516 std::int32_t const loanScale = loan->at(sfLoanScale);
+
1517
+
1518 TenthBips32 const interestRate{loan->at(sfInterestRate)};
+
1519
+
1520 Number const serviceFee = loan->at(sfLoanServiceFee);
+
1521 TenthBips16 const managementFeeRate{brokerSle->at(sfManagementFeeRate)};
+
1522
+
1523 Number const periodicPayment = loan->at(sfPeriodicPayment);
+
1524
+
1525 auto prevPaymentDateProxy = loan->at(sfPreviousPaymentDueDate);
+
1526 std::uint32_t const startDate = loan->at(sfStartDate);
+
1527
+
1528 std::uint32_t const paymentInterval = loan->at(sfPaymentInterval);
+
1529
+
1530 // Compute the periodic rate that will be used for calculations
+
1531 // throughout
+
1532 Number const periodicRate = loanPeriodicRate(interestRate, paymentInterval);
+
1533 XRPL_ASSERT(interestRate == 0 || periodicRate > 0, "xrpl::loanMakePayment : valid rate");
+
1534
+
1535 XRPL_ASSERT(*totalValueOutstandingProxy > 0, "xrpl::loanMakePayment : valid total value");
+
1536
+
1537 view.update(loan);
+
1538
+
1539 // -------------------------------------------------------------
+
1540 // A late payment not flagged as late overrides all other options.
+
1541 if (paymentType != LoanPaymentType::late && hasExpired(view, nextDueDateProxy))
+
1542 {
+
1543 // If the payment is late, and the late flag was not set, it's not
+
1544 // valid
+
1545 JLOG(j.warn()) << "Loan payment is overdue. Use the tfLoanLatePayment "
+
1546 "transaction "
+
1547 "flag to make a late payment. Loan was created on "
+
1548 << startDate << ", prev payment due date is " << prevPaymentDateProxy
+
1549 << ", next payment due date is " << nextDueDateProxy << ", ledger time is "
+
1550 << view.parentCloseTime().time_since_epoch().count();
+
1551 return Unexpected(tecEXPIRED);
+
1552 }
+
1553
+
1554 // -------------------------------------------------------------
+
1555 // full payment handling
+
1556 if (paymentType == LoanPaymentType::full)
+
1557 {
+
1558 TenthBips32 const closeInterestRate{loan->at(sfCloseInterestRate)};
+
1559 Number const closePaymentFee = roundToAsset(asset, loan->at(sfClosePaymentFee), loanScale);
+
1560
+
1561 LoanState const roundedLoanState =
+
1562 constructLoanState(totalValueOutstandingProxy, principalOutstandingProxy, managementFeeOutstandingProxy);
+
1563
+
1564 if (auto const fullPaymentComponents = detail::computeFullPayment(
+
1565 asset,
+
1566 view,
+
1567 principalOutstandingProxy,
+
1568 managementFeeOutstandingProxy,
+
1569 periodicPayment,
+
1570 paymentRemainingProxy,
+
1571 prevPaymentDateProxy,
+
1572 startDate,
+
1573 paymentInterval,
+
1574 closeInterestRate,
+
1575 loanScale,
+
1576 roundedLoanState.interestDue,
+
1577 periodicRate,
+
1578 closePaymentFee,
+
1579 amount,
+
1580 managementFeeRate,
+
1581 j))
+
1582 {
+
1583 return doPayment(
+
1584 *fullPaymentComponents,
+
1585 totalValueOutstandingProxy,
+
1586 principalOutstandingProxy,
+
1587 managementFeeOutstandingProxy,
+
1588 paymentRemainingProxy,
+
1589 prevPaymentDateProxy,
+
1590 nextDueDateProxy,
+
1591 paymentInterval);
+
1592 }
+
1593 else if (fullPaymentComponents.error())
+
1594 // error() will be the TER returned if a payment is not made. It
+
1595 // will only evaluate to true if it's unsuccessful. Otherwise,
+
1596 // tesSUCCESS means nothing was done, so continue.
+
1597 return Unexpected(fullPaymentComponents.error());
+
1598
+
1599 // LCOV_EXCL_START
+
1600 UNREACHABLE("xrpl::loanMakePayment : invalid full payment result");
+
1601 JLOG(j.error()) << "Full payment computation failed unexpectedly.";
+
1602 return Unexpected(tecINTERNAL);
+
1603 // LCOV_EXCL_STOP
+
1604 }
+
1605
+
1606 // -------------------------------------------------------------
+
1607 // compute the periodic payment info that will be needed whether the
+
1608 // payment is late or regular
+ + +
1611 asset,
+
1612 loanScale,
+
1613 totalValueOutstandingProxy,
+
1614 principalOutstandingProxy,
+
1615 managementFeeOutstandingProxy,
+
1616 periodicPayment,
+
1617 periodicRate,
+
1618 paymentRemainingProxy,
+
1619 managementFeeRate),
+
1620 serviceFee};
+
1621 XRPL_ASSERT_PARTS(periodic.trackedPrincipalDelta >= 0, "xrpl::loanMakePayment", "regular payment valid principal");
+
1622
+
1623 // -------------------------------------------------------------
+
1624 // late payment handling
+
1625 if (paymentType == LoanPaymentType::late)
+
1626 {
+
1627 TenthBips32 const lateInterestRate{loan->at(sfLateInterestRate)};
+
1628 Number const latePaymentFee = loan->at(sfLatePaymentFee);
+
1629
+
1630 if (auto const latePaymentComponents = detail::computeLatePayment(
+
1631 asset,
+
1632 view,
+
1633 principalOutstandingProxy,
+
1634 nextDueDateProxy,
+
1635 periodic,
+
1636 lateInterestRate,
+
1637 loanScale,
+
1638 latePaymentFee,
+
1639 amount,
+
1640 managementFeeRate,
+
1641 j))
+
1642 {
+
1643 return doPayment(
+
1644 *latePaymentComponents,
+
1645 totalValueOutstandingProxy,
+
1646 principalOutstandingProxy,
+
1647 managementFeeOutstandingProxy,
+
1648 paymentRemainingProxy,
+
1649 prevPaymentDateProxy,
+
1650 nextDueDateProxy,
+
1651 paymentInterval);
+
1652 }
+
1653 else if (latePaymentComponents.error())
+
1654 {
+
1655 // error() will be the TER returned if a payment is not made. It
+
1656 // will only evaluate to true if it's unsuccessful.
+
1657 return Unexpected(latePaymentComponents.error());
+
1658 }
+
1659
+
1660 // LCOV_EXCL_START
+
1661 UNREACHABLE("xrpl::loanMakePayment : invalid late payment result");
+
1662 JLOG(j.error()) << "Late payment computation failed unexpectedly.";
+
1663 return Unexpected(tecINTERNAL);
+
1664 // LCOV_EXCL_STOP
+
1665 }
+
1666
+
1667 // -------------------------------------------------------------
+
1668 // regular periodic payment handling
+
1669
+
1670 XRPL_ASSERT_PARTS(
+
1671 paymentType == LoanPaymentType::regular || paymentType == LoanPaymentType::overpayment,
+
1672 "xrpl::loanMakePayment",
+
1673 "regular payment type");
+
1674
+
1675 // Keep a running total of the actual parts paid
+
1676 LoanPaymentParts totalParts;
+
1677 Number totalPaid;
+
1678 std::size_t numPayments = 0;
+
1679
+
1680 while ((amount >= (totalPaid + periodic.totalDue)) && paymentRemainingProxy > 0 &&
+
1681 numPayments < loanMaximumPaymentsPerTransaction)
+
1682 {
+
1683 // Try to make more payments
+
1684 XRPL_ASSERT_PARTS(
+
1685 periodic.trackedPrincipalDelta >= 0, "xrpl::loanMakePayment", "payment pays non-negative principal");
+
1686
+
1687 totalPaid += periodic.totalDue;
+
1688 totalParts += detail::doPayment(
+
1689 periodic,
+
1690 totalValueOutstandingProxy,
+
1691 principalOutstandingProxy,
+
1692 managementFeeOutstandingProxy,
+
1693 paymentRemainingProxy,
+
1694 prevPaymentDateProxy,
+
1695 nextDueDateProxy,
+
1696 paymentInterval);
+
1697 ++numPayments;
+
1698
+
1699 XRPL_ASSERT_PARTS(
+
1700 (periodic.specialCase == detail::PaymentSpecialCase::final) == (paymentRemainingProxy == 0),
+
1701 "xrpl::loanMakePayment",
+
1702 "final payment is the final payment");
+
1703
+
1704 // Don't compute the next payment if this was the last payment
+
1705 if (periodic.specialCase == detail::PaymentSpecialCase::final)
+
1706 break;
+
1707
+ + +
1710 asset,
+
1711 loanScale,
+
1712 totalValueOutstandingProxy,
+
1713 principalOutstandingProxy,
+
1714 managementFeeOutstandingProxy,
+
1715 periodicPayment,
+
1716 periodicRate,
+
1717 paymentRemainingProxy,
+
1718 managementFeeRate),
+
1719 serviceFee};
+
1720 }
+
1721
+
1722 if (numPayments == 0)
+
1723 {
+
1724 JLOG(j.warn()) << "Regular loan payment amount is insufficient. Due: " << periodic.totalDue
+
1725 << ", paid: " << amount;
+ +
1727 }
+
1728
+
1729 XRPL_ASSERT_PARTS(
+
1730 totalParts.principalPaid + totalParts.interestPaid + totalParts.feePaid == totalPaid,
+
1731 "xrpl::loanMakePayment",
+
1732 "payment parts add up");
+
1733 XRPL_ASSERT_PARTS(totalParts.valueChange == 0, "xrpl::loanMakePayment", "no value change");
+
1734
+
1735 // -------------------------------------------------------------
+
1736 // overpayment handling
+
1737 if (paymentType == LoanPaymentType::overpayment && loan->isFlag(lsfLoanOverpayment) && paymentRemainingProxy > 0 &&
+
1738 totalPaid < amount && numPayments < loanMaximumPaymentsPerTransaction)
+
1739 {
+
1740 TenthBips32 const overpaymentInterestRate{loan->at(sfOverpaymentInterestRate)};
+
1741 TenthBips32 const overpaymentFeeRate{loan->at(sfOverpaymentFee)};
+
1742
+
1743 // It shouldn't be possible for the overpayment to be greater than
+
1744 // totalValueOutstanding, because that would have been processed as
+
1745 // another normal payment. But cap it just in case.
+
1746 Number const overpayment = std::min(amount - totalPaid, *totalValueOutstandingProxy);
+
1747
+ +
1749 asset, loanScale, overpayment, overpaymentInterestRate, overpaymentFeeRate, managementFeeRate);
+
1750
+
1751 // Don't process an overpayment if the whole amount (or more!)
+
1752 // gets eaten by fees and interest.
+
1753 if (overpaymentComponents.trackedPrincipalDelta > 0)
+
1754 {
+
1755 XRPL_ASSERT_PARTS(
+
1756 overpaymentComponents.untrackedInterest >= beast::zero,
+
1757 "xrpl::loanMakePayment",
+
1758 "overpayment penalty did not reduce value of loan");
+
1759 // Can't just use `periodicPayment` here, because it might
+
1760 // change
+
1761 auto periodicPaymentProxy = loan->at(sfPeriodicPayment);
+
1762 if (auto const overResult = detail::doOverpayment(
+
1763 asset,
+
1764 loanScale,
+
1765 overpaymentComponents,
+
1766 totalValueOutstandingProxy,
+
1767 principalOutstandingProxy,
+
1768 managementFeeOutstandingProxy,
+
1769 periodicPaymentProxy,
+
1770 periodicRate,
+
1771 paymentRemainingProxy,
+
1772 managementFeeRate,
+
1773 j))
+
1774 totalParts += *overResult;
+
1775 else if (overResult.error())
+
1776 // error() will be the TER returned if a payment is not
+
1777 // made. It will only evaluate to true if it's unsuccessful.
+
1778 // Otherwise, tesSUCCESS means nothing was done, so
+
1779 // continue.
+
1780 return Unexpected(overResult.error());
+
1781 }
+
1782 }
+
1783
+
1784 // Check the final results are rounded, to double-check that the
+
1785 // intermediate steps were rounded.
+
1786 XRPL_ASSERT(
+
1787 isRounded(asset, totalParts.principalPaid, loanScale) && totalParts.principalPaid >= beast::zero,
+
1788 "xrpl::loanMakePayment : total principal paid is valid");
+
1789 XRPL_ASSERT(
+
1790 isRounded(asset, totalParts.interestPaid, loanScale) && totalParts.interestPaid >= beast::zero,
+
1791 "xrpl::loanMakePayment : total interest paid is valid");
+
1792 XRPL_ASSERT(
+
1793 isRounded(asset, totalParts.valueChange, loanScale), "xrpl::loanMakePayment : loan value change is valid");
+
1794 XRPL_ASSERT(
+
1795 isRounded(asset, totalParts.feePaid, loanScale) && totalParts.feePaid >= beast::zero,
+
1796 "xrpl::loanMakePayment : fee paid is valid");
+
1797 return totalParts;
+
1798}
-
1798} // namespace xrpl
+
1799} // namespace xrpl
T clamp(T... args)
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:318
@@ -1972,21 +1973,21 @@ $(document).ready(function() { init_codefold(0); });
T make_pair(T... args)
T max(T... args)
T min(T... args)
-
PaymentComponents computePaymentComponents(Asset const &asset, std::int32_t scale, Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 managementFeeRate)
-
Expected< ExtendedPaymentComponents, TER > computeFullPayment(Asset const &asset, ApplyView &view, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, std::uint32_t paymentRemaining, std::uint32_t prevPaymentDate, std::uint32_t const startDate, std::uint32_t const paymentInterval, TenthBips32 const closeInterestRate, std::int32_t loanScale, Number const &totalInterestOutstanding, Number const &periodicRate, Number const &closePaymentFee, STAmount const &amount, TenthBips16 managementFeeRate, beast::Journal j)
+
PaymentComponents computePaymentComponents(Asset const &asset, std::int32_t scale, Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 managementFeeRate)
+
Expected< ExtendedPaymentComponents, TER > computeFullPayment(Asset const &asset, ApplyView &view, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, std::uint32_t paymentRemaining, std::uint32_t prevPaymentDate, std::uint32_t const startDate, std::uint32_t const paymentInterval, TenthBips32 const closeInterestRate, std::int32_t loanScale, Number const &totalInterestOutstanding, Number const &periodicRate, Number const &closePaymentFee, STAmount const &amount, TenthBips16 managementFeeRate, beast::Journal j)
Number computeRaisedRate(Number const &periodicRate, std::uint32_t paymentsRemaining)
Number loanPeriodicPayment(Number const &principalOutstanding, Number const &periodicRate, std::uint32_t paymentsRemaining)
-
ExtendedPaymentComponents computeOverpaymentComponents(Asset const &asset, int32_t const loanScale, Number const &overpayment, TenthBips32 const overpaymentInterestRate, TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate)
-
Expected< LoanPaymentParts, TER > doOverpayment(Asset const &asset, std::int32_t loanScale, ExtendedPaymentComponents const &overpaymentComponents, NumberProxy &totalValueOutstandingProxy, NumberProxy &principalOutstandingProxy, NumberProxy &managementFeeOutstandingProxy, NumberProxy &periodicPaymentProxy, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips16 const managementFeeRate, beast::Journal j)
-
Expected< ExtendedPaymentComponents, TER > computeLatePayment(Asset const &asset, ApplyView const &view, Number const &principalOutstanding, std::int32_t nextDueDate, ExtendedPaymentComponents const &periodic, TenthBips32 lateInterestRate, std::int32_t loanScale, Number const &latePaymentFee, STAmount const &amount, TenthBips16 managementFeeRate, beast::Journal j)
+
ExtendedPaymentComponents computeOverpaymentComponents(Asset const &asset, int32_t const loanScale, Number const &overpayment, TenthBips32 const overpaymentInterestRate, TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate)
+
Expected< LoanPaymentParts, TER > doOverpayment(Asset const &asset, std::int32_t loanScale, ExtendedPaymentComponents const &overpaymentComponents, NumberProxy &totalValueOutstandingProxy, NumberProxy &principalOutstandingProxy, NumberProxy &managementFeeOutstandingProxy, NumberProxy &periodicPaymentProxy, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips16 const managementFeeRate, beast::Journal j)
+
Expected< ExtendedPaymentComponents, TER > computeLatePayment(Asset const &asset, ApplyView const &view, Number const &principalOutstanding, std::int32_t nextDueDate, ExtendedPaymentComponents const &periodic, TenthBips32 lateInterestRate, std::int32_t loanScale, Number const &latePaymentFee, STAmount const &amount, TenthBips16 managementFeeRate, beast::Journal j)
Number loanAccruedInterest(Number const &principalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t startDate, std::uint32_t prevPaymentDate, std::uint32_t paymentInterval)
std::pair< Number, Number > computeInterestAndFeeParts(Asset const &asset, Number const &interest, TenthBips16 managementFeeRate, std::int32_t loanScale)
Number loanLatePaymentInterest(Number const &principalOutstanding, TenthBips32 lateInterestRate, NetClock::time_point parentCloseTime, std::uint32_t nextPaymentDueDate)
Number computePaymentFactor(Number const &periodicRate, std::uint32_t paymentsRemaining)
Number loanPrincipalFromPeriodicPayment(Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentsRemaining)
-
Expected< std::pair< LoanPaymentParts, LoanProperties >, TER > tryOverpayment(Asset const &asset, std::int32_t loanScale, ExtendedPaymentComponents const &overpaymentComponents, LoanState const &roundedOldState, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 const managementFeeRate, beast::Journal j)
+
Expected< std::pair< LoanPaymentParts, LoanProperties >, TER > tryOverpayment(Asset const &asset, std::int32_t loanScale, ExtendedPaymentComponents const &overpaymentComponents, LoanState const &roundedOldState, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 const managementFeeRate, beast::Journal j)
LoanPaymentParts doPayment(ExtendedPaymentComponents const &payment, NumberProxy &totalValueOutstandingProxy, NumberProxy &principalOutstandingProxy, NumberProxy &managementFeeOutstandingProxy, UInt32Proxy &paymentRemainingProxy, UInt32Proxy &prevPaymentDateProxy, UInt32OptionalProxy &nextDueDateProxy, std::uint32_t paymentInterval)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Number loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval)
@@ -1995,7 +1996,7 @@ $(document).ready(function() { init_codefold(0); }); -
Number computeManagementFee(Asset const &asset, Number const &value, TenthBips32 managementFeeRate, std::int32_t scale)
+
Number computeManagementFee(Asset const &asset, Number const &value, TenthBips32 managementFeeRate, std::int32_t scale)
constexpr base_uint< Bits, Tag > operator+(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
Definition base_uint.h:589
Number operator-(Number const &x, Number const &y)
Definition Number.h:638
static constexpr Number numZero
Definition Number.h:515
@@ -2003,14 +2004,14 @@ $(document).ready(function() { init_codefold(0); });
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:129
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
static constexpr std::uint32_t secondsInYear
-
LoanState computeTheoreticalLoanState(Number const &periodicPayment, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips32 const managementFeeRate)
-
Number power(Number const &f, unsigned n)
Definition Number.cpp:915
-
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
-
Expected< LoanPaymentParts, TER > loanMakePayment(Asset const &asset, ApplyView &view, SLE::ref loan, SLE::const_ref brokerSle, STAmount const &amount, LoanPaymentType const paymentType, beast::Journal j)
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
LoanState computeTheoreticalLoanState(Number const &periodicPayment, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips32 const managementFeeRate)
+
Number power(Number const &f, unsigned n)
Definition Number.cpp:916
+
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
+
Expected< LoanPaymentParts, TER > loanMakePayment(Asset const &asset, ApplyView &view, SLE::ref loan, SLE::const_ref brokerSle, STAmount const &amount, LoanPaymentType const paymentType, beast::Journal j)
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
Number roundPeriodicPayment(Asset const &asset, Number const &periodicPayment, std::int32_t scale)
Ensure the periodic payment is always rounded consistently.
-
LoanState constructRoundedLoanState(SLE::const_ref loan)
+
LoanState constructRoundedLoanState(SLE::const_ref loan)
@ tecINTERNAL
Definition TER.h:291
@ tecTOO_SOON
Definition TER.h:299
@ tecEXPIRED
Definition TER.h:295
@@ -2018,10 +2019,10 @@ $(document).ready(function() { init_codefold(0); });
@ tecKILLED
Definition TER.h:297
@ tecINSUFFICIENT_PAYMENT
Definition TER.h:308
@ lsfLoanOverpayment
-
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
-
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
+
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
+
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
@ tesSUCCESS
Definition TER.h:225
-
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
+
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
bool isRounded(Asset const &asset, Number const &value, std::int32_t scale)
@@ -2059,7 +2060,7 @@ $(document).ready(function() { init_codefold(0); }); - +
T time_since_epoch(T... args)
diff --git a/LendingHelpers_8h_source.html b/LendingHelpers_8h_source.html index 10090046c4..6bcb204f36 100644 --- a/LendingHelpers_8h_source.html +++ b/LendingHelpers_8h_source.html @@ -573,19 +573,19 @@ $(document).ready(function() { init_codefold(0); }); -
PaymentComponents computePaymentComponents(Asset const &asset, std::int32_t scale, Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 managementFeeRate)
+
PaymentComponents computePaymentComponents(Asset const &asset, std::int32_t scale, Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 managementFeeRate)
Number computeRaisedRate(Number const &periodicRate, std::uint32_t paymentsRemaining)
Number loanPeriodicPayment(Number const &principalOutstanding, Number const &periodicRate, std::uint32_t paymentsRemaining)
-
ExtendedPaymentComponents computeOverpaymentComponents(Asset const &asset, int32_t const loanScale, Number const &overpayment, TenthBips32 const overpaymentInterestRate, TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate)
+
ExtendedPaymentComponents computeOverpaymentComponents(Asset const &asset, int32_t const loanScale, Number const &overpayment, TenthBips32 const overpaymentInterestRate, TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate)
Number loanAccruedInterest(Number const &principalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t startDate, std::uint32_t prevPaymentDate, std::uint32_t paymentInterval)
std::pair< Number, Number > computeInterestAndFeeParts(Asset const &asset, Number const &interest, TenthBips16 managementFeeRate, std::int32_t loanScale)
Number loanLatePaymentInterest(Number const &principalOutstanding, TenthBips32 lateInterestRate, NetClock::time_point parentCloseTime, std::uint32_t nextPaymentDueDate)
Number computePaymentFactor(Number const &periodicRate, std::uint32_t paymentsRemaining)
Number loanPrincipalFromPeriodicPayment(Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentsRemaining)
-
Expected< std::pair< LoanPaymentParts, LoanProperties >, TER > tryOverpayment(Asset const &asset, std::int32_t loanScale, ExtendedPaymentComponents const &overpaymentComponents, LoanState const &roundedOldState, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 const managementFeeRate, beast::Journal j)
+
Expected< std::pair< LoanPaymentParts, LoanProperties >, TER > tryOverpayment(Asset const &asset, std::int32_t loanScale, ExtendedPaymentComponents const &overpaymentComponents, LoanState const &roundedOldState, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 const managementFeeRate, beast::Journal j)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Number loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval)
@@ -593,7 +593,7 @@ $(document).ready(function() { init_codefold(0); }); -
Number computeManagementFee(Asset const &asset, Number const &value, TenthBips32 managementFeeRate, std::int32_t scale)
+
Number computeManagementFee(Asset const &asset, Number const &value, TenthBips32 managementFeeRate, std::int32_t scale)
constexpr base_uint< Bits, Tag > operator+(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
Definition base_uint.h:589
Number operator-(Number const &x, Number const &y)
Definition Number.h:638
static constexpr Number numZero
Definition Number.h:515
@@ -601,17 +601,17 @@ $(document).ready(function() { init_codefold(0); });
void adjustImpreciseNumber(NumberProxy value, Number const &adjustment, Asset const &asset, int vaultScale)
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
static constexpr std::uint32_t secondsInYear
-
LoanState computeTheoreticalLoanState(Number const &periodicPayment, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips32 const managementFeeRate)
+
LoanState computeTheoreticalLoanState(Number const &periodicPayment, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips32 const managementFeeRate)
TenthBips< std::uint32_t > TenthBips32
Definition Units.h:429
-
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
-
Expected< LoanPaymentParts, TER > loanMakePayment(Asset const &asset, ApplyView &view, SLE::ref loan, SLE::const_ref brokerSle, STAmount const &amount, LoanPaymentType const paymentType, beast::Journal j)
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
+
Expected< LoanPaymentParts, TER > loanMakePayment(Asset const &asset, ApplyView &view, SLE::ref loan, SLE::const_ref brokerSle, STAmount const &amount, LoanPaymentType const paymentType, beast::Journal j)
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
Number roundPeriodicPayment(Asset const &asset, Number const &periodicPayment, std::int32_t scale)
Ensure the periodic payment is always rounded consistently.
-
LoanState constructRoundedLoanState(SLE::const_ref loan)
-
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
-
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
-
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
+
LoanState constructRoundedLoanState(SLE::const_ref loan)
+
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
+
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
+
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
bool isRounded(Asset const &asset, Number const &value, std::int32_t scale)
@@ -648,7 +648,7 @@ $(document).ready(function() { init_codefold(0); }); - +
diff --git a/LendingHelpers__test_8cpp_source.html b/LendingHelpers__test_8cpp_source.html index 0c1dc83288..3a26915934 100644 --- a/LendingHelpers__test_8cpp_source.html +++ b/LendingHelpers__test_8cpp_source.html @@ -1281,14 +1281,14 @@ $(document).ready(function() { init_codefold(0); }); -
ExtendedPaymentComponents computeOverpaymentComponents(Asset const &asset, int32_t const loanScale, Number const &overpayment, TenthBips32 const overpaymentInterestRate, TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate)
+
ExtendedPaymentComponents computeOverpaymentComponents(Asset const &asset, int32_t const loanScale, Number const &overpayment, TenthBips32 const overpaymentInterestRate, TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Number loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
TenthBips< std::uint32_t > TenthBips32
Definition Units.h:429
-
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
-
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
+
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
+
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
diff --git a/LoanBrokerCoverClawback_8cpp_source.html b/LoanBrokerCoverClawback_8cpp_source.html index 4f2774523a..bf96194cb4 100644 --- a/LoanBrokerCoverClawback_8cpp_source.html +++ b/LoanBrokerCoverClawback_8cpp_source.html @@ -463,14 +463,14 @@ $(document).ready(function() { init_codefold(0); });
@ fhIGNORE_FREEZE
Definition View.h:58
constexpr T tenthBipsOfValue(T value, TenthBips< TBips > bips)
Definition Protocol.h:107
@ tefBAD_LEDGER
Definition TER.h:150
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Definition View.cpp:392
TenthBips< std::uint32_t > TenthBips32
Definition Units.h:429
static TER preclaimHelper(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2446
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
TER preclaimHelper< Issue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition Clawback.cpp:86
@ ahIGNORE_AUTH
Definition View.h:61
Expected< uint256, TER > determineBrokerID(ReadView const &view, STTx const &tx)
diff --git a/LoanBrokerCoverDeposit_8cpp_source.html b/LoanBrokerCoverDeposit_8cpp_source.html index 3e51c045d0..3503da3180 100644 --- a/LoanBrokerCoverDeposit_8cpp_source.html +++ b/LoanBrokerCoverDeposit_8cpp_source.html @@ -238,7 +238,7 @@ $(document).ready(function() { init_codefold(0); });
@ shFULL_BALANCE
Definition View.h:64
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
Definition View.h:121
@ tefBAD_LEDGER
Definition TER.h:150
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Definition View.cpp:392
diff --git a/LoanBrokerCoverWithdraw_8cpp_source.html b/LoanBrokerCoverWithdraw_8cpp_source.html index 311bd0f650..8e6c4c03c1 100644 --- a/LoanBrokerCoverWithdraw_8cpp_source.html +++ b/LoanBrokerCoverWithdraw_8cpp_source.html @@ -292,7 +292,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr T tenthBipsOfValue(T value, TenthBips< TBips > bips)
Definition Protocol.h:107
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
Definition View.h:121
@ tefBAD_LEDGER
Definition TER.h:150
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Definition View.cpp:392
TenthBips< std::uint32_t > TenthBips32
Definition Units.h:429
@@ -303,7 +303,7 @@ $(document).ready(function() { init_codefold(0); });
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2710
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2914
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
@ temINVALID
Definition TER.h:90
@ temMALFORMED
Definition TER.h:67
@ temBAD_AMOUNT
Definition TER.h:69
diff --git a/LoanBrokerDelete_8cpp_source.html b/LoanBrokerDelete_8cpp_source.html index 41cb4506b2..a7430959ed 100644 --- a/LoanBrokerDelete_8cpp_source.html +++ b/LoanBrokerDelete_8cpp_source.html @@ -296,7 +296,7 @@ $(document).ready(function() { init_codefold(0); });
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2446
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
@ temINVALID
Definition TER.h:90
@ tecNO_ENTRY
Definition TER.h:287
@ tecNO_PERMISSION
Definition TER.h:286
diff --git a/LoanBroker__test_8cpp_source.html b/LoanBroker__test_8cpp_source.html index 456f03468a..54cfa43819 100644 --- a/LoanBroker__test_8cpp_source.html +++ b/LoanBroker__test_8cpp_source.html @@ -1927,7 +1927,7 @@ $(document).ready(function() { init_codefold(0); });
Converts to IOU Issue or STAmount.
-
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
Definition mpt.cpp:228
+
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
Definition mpt.cpp:229
Converts to MPT Issue or STAmount.
A balance matches.
Definition balance.h:19
Set the fee on a JTx.
Definition fee.h:17
@@ -1958,7 +1958,7 @@ $(document).ready(function() { init_codefold(0); });
static MPTInit const mptInitNoFund
Definition mpt.h:103
auto const data
General field definitions, or fields used in multiple transaction namespaces.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
static none_t const none
Definition tags.h:14
@@ -1984,7 +1984,7 @@ $(document).ready(function() { init_codefold(0); });
static bool authorized(Port const &port, std::map< std::string, std::string > const &h)
std::size_t constexpr maxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:237
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:65
-
Number power(Number const &f, unsigned n)
Definition Number.cpp:915
+
Number power(Number const &f, unsigned n)
Definition Number.cpp:916
TenthBips< std::uint32_t > TenthBips32
Definition Units.h:429
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:128
base_uint< 256 > uint256
Definition base_uint.h:526
@@ -1997,7 +1997,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t const tfMPTUnauthorize
Definition TxFlags.h:152
constexpr std::uint32_t tfUniversal
Definition TxFlags.h:42
constexpr std::uint32_t tfSetfAuth
Definition TxFlags.h:95
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
@ tapNONE
Definition ApplyView.h:11
TenthBips< std::uint16_t > TenthBips16
Definition Units.h:428
constexpr std::uint32_t asfRequireAuth
Definition TxFlags.h:58
diff --git a/LoanDelete_8cpp_source.html b/LoanDelete_8cpp_source.html index 42b2f18207..60ecb40a6b 100644 --- a/LoanDelete_8cpp_source.html +++ b/LoanDelete_8cpp_source.html @@ -244,7 +244,7 @@ $(document).ready(function() { init_codefold(0); });
@ tefBAD_LEDGER
Definition TER.h:150
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
@ temINVALID
Definition TER.h:90
@ tecNO_ENTRY
Definition TER.h:287
@ tecINTERNAL
Definition TER.h:291
diff --git a/LoanManage_8cpp_source.html b/LoanManage_8cpp_source.html index 8cb31753c6..ad66d365c5 100644 --- a/LoanManage_8cpp_source.html +++ b/LoanManage_8cpp_source.html @@ -544,10 +544,10 @@ $(document).ready(function() { init_codefold(0); });
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2446
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
static Number owedToVault(SLE::ref loanSle)
constexpr std::uint32_t const tfLoanDefault
Definition TxFlags.h:289
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
@ temINVALID
Definition TER.h:90
@ temINVALID_FLAG
Definition TER.h:91
constexpr std::uint32_t const tfLoanUnimpair
Definition TxFlags.h:291
diff --git a/LoanPay_8cpp_source.html b/LoanPay_8cpp_source.html index e52fcbc892..41c3333f58 100644 --- a/LoanPay_8cpp_source.html +++ b/LoanPay_8cpp_source.html @@ -713,9 +713,9 @@ $(document).ready(function() { init_codefold(0); });
@ ahZERO_IF_UNAUTHORIZED
Definition View.h:61
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2710
-
Expected< LoanPaymentParts, TER > loanMakePayment(Asset const &asset, ApplyView &view, SLE::ref loan, SLE::const_ref brokerSle, STAmount const &amount, LoanPaymentType const paymentType, beast::Journal j)
+
Expected< LoanPaymentParts, TER > loanMakePayment(Asset const &asset, ApplyView &view, SLE::ref loan, SLE::const_ref brokerSle, STAmount const &amount, LoanPaymentType const paymentType, beast::Journal j)
constexpr std::uint32_t tfUniversal
Definition TxFlags.h:42
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
Number roundPeriodicPayment(Asset const &asset, Number const &periodicPayment, std::int32_t scale)
Ensure the periodic payment is always rounded consistently.
constexpr std::uint32_t const tfLoanLatePayment
Definition TxFlags.h:282
@ temINVALID
Definition TER.h:90
diff --git a/LoanSet_8cpp_source.html b/LoanSet_8cpp_source.html index 3104242df4..f1c4a17307 100644 --- a/LoanSet_8cpp_source.html +++ b/LoanSet_8cpp_source.html @@ -714,7 +714,7 @@ $(document).ready(function() { init_codefold(0); });
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:118
-
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1041
+
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1047
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
STObject getFieldObject(SField const &field) const
Definition STObject.cpp:653
@@ -758,8 +758,8 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t const tfLoanOverpayment
Definition TxFlags.h:272
std::size_t constexpr maxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:237
-
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2710
@@ -781,8 +781,8 @@ $(document).ready(function() { init_codefold(0); });
@ lsfLoanOverpayment
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
TER accountSendMulti(ApplyView &view, AccountID const &senderID, Asset const &asset, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Like accountSend, except one account is sending multiple payments (with the same asset!...
Definition View.cpp:2465
-
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
-
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
+
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
+
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
@ tesSUCCESS
Definition TER.h:225
bool isRounded(Asset const &asset, Number const &value, std::int32_t scale)
diff --git a/Loan__test_8cpp_source.html b/Loan__test_8cpp_source.html index b3c4a6da48..d380efdf60 100644 --- a/Loan__test_8cpp_source.html +++ b/Loan__test_8cpp_source.html @@ -7086,7 +7086,7 @@ $(document).ready(function() { init_codefold(0); });
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
-
Value removeMember(char const *key)
Remove and return the named member.
+
Value removeMember(char const *key)
Remove and return the named member.
A testsuite class.
Definition suite.h:51
log_os< char > log
Logging output stream.
Definition suite.h:144
@@ -7111,16 +7111,16 @@ $(document).ready(function() { init_codefold(0); });
std::chrono::duration< rep, period > duration
Definition chrono.h:44
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
-
Number truncate() const noexcept
Definition Number.cpp:805
+
Number truncate() const noexcept
Definition Number.cpp:806
constexpr int exponent() const noexcept
Returns the exponent of the external view of the Number.
Definition Number.h:563
Slice slice() const noexcept
Definition PublicKey.h:103
Asset const & asset() const
Definition STAmount.h:441
-
STAmount const & value() const noexcept
Definition STAmount.h:560
+
STAmount const & value() const noexcept
Definition STAmount.h:562
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:624
-
Serializer getSerializer() const
Definition STObject.h:966
+
Serializer getSerializer() const
Definition STObject.h:972
void setFieldObject(SField const &field, STObject const &v)
Definition STObject.cpp:808
STObject getFieldObject(SField const &field) const
Definition STObject.cpp:653
@@ -7223,8 +7223,8 @@ $(document).ready(function() { init_codefold(0); });
NetClock::time_point now()
Returns the current network time.
Definition Env.h:274
Converts to IOU Issue or STAmount.
-
void set(MPTSet const &set={})
Definition mpt.cpp:327
-
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
Definition mpt.cpp:228
+
void set(MPTSet const &set={})
Definition mpt.cpp:328
+
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
Definition mpt.cpp:229
MPTID const & issuanceID() const
Definition mpt.h:249
Converts to MPT Issue or STAmount.
@@ -7256,7 +7256,7 @@ $(document).ready(function() { init_codefold(0); });
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
beast::abstract_clock< std::chrono::steady_clock > clock_type
Definition Entry.h:13
-
PaymentComponents computePaymentComponents(Asset const &asset, std::int32_t scale, Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 managementFeeRate)
+
PaymentComponents computePaymentComponents(Asset const &asset, std::int32_t scale, Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 managementFeeRate)
Keylet loanbroker(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:498
@@ -7294,7 +7294,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
std::uint32_t ownerCount(Env const &env, Account const &account)
XRPAmount txfee(Env const &env, std::uint16_t n)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
std::unique_ptr< Config > makeConfig(std::map< std::string, std::string > extraTxQ={}, std::map< std::string, std::string > extraVoting={})
static Number number(STAmount const &a)
Definition AMM.cpp:17
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
@@ -7320,7 +7320,7 @@ $(document).ready(function() { init_codefold(0); });
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
Number loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval)
constexpr std::uint32_t const tmfMPTClearCanTransfer
Definition TxFlags.h:172
-
Number computeManagementFee(Asset const &asset, Number const &value, TenthBips32 managementFeeRate, std::int32_t scale)
+
Number computeManagementFee(Asset const &asset, Number const &value, TenthBips32 managementFeeRate, std::int32_t scale)
int run(int argc, char **argv)
Definition Main.cpp:322
constexpr std::uint32_t const tfMPTCanTransfer
Definition TxFlags.h:132
constexpr std::uint32_t const tmfMPTCanMutateCanTransfer
Definition TxFlags.h:143
@@ -7338,7 +7338,7 @@ $(document).ready(function() { init_codefold(0); });
@ tefNOT_MULTI_SIGNING
Definition TER.h:161
constexpr std::uint32_t const tfLoanFullPayment
Definition TxFlags.h:277
static constexpr std::uint32_t secondsInYear
-
LoanState computeTheoreticalLoanState(Number const &periodicPayment, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips32 const managementFeeRate)
+
LoanState computeTheoreticalLoanState(Number const &periodicPayment, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips32 const managementFeeRate)
constexpr std::uint32_t const tfLoanOverpayment
Definition TxFlags.h:272
std::size_t constexpr maxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:237
TenthBips< std::uint32_t > TenthBips32
Definition Units.h:429
@@ -7348,7 +7348,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:128
@ current
This was a new validation and was added.
base_uint< 256 > uint256
Definition base_uint.h:526
-
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
+
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
constexpr std::uint32_t asfDefaultRipple
Definition TxFlags.h:64
constexpr std::uint32_t const tfMPTCanClawback
Definition TxFlags.h:133
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:99
@@ -7367,13 +7367,13 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t const tfLoanDefault
Definition TxFlags.h:289
constexpr Number abs(Number x) noexcept
Definition Number.h:706
constexpr std::uint32_t tfSetfAuth
Definition TxFlags.h:95
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
TenthBips< std::uint16_t > TenthBips16
Definition Units.h:428
constexpr std::uint32_t asfRequireAuth
Definition TxFlags.h:58
Number roundPeriodicPayment(Asset const &asset, Number const &periodicPayment, std::int32_t scale)
Ensure the periodic payment is always rounded consistently.
constexpr std::uint32_t const tfLoanLatePayment
Definition TxFlags.h:282
@ txSign
inner transaction to sign
-
LoanState constructRoundedLoanState(SLE::const_ref loan)
+
LoanState constructRoundedLoanState(SLE::const_ref loan)
@ temINVALID
Definition TER.h:90
@ temINVALID_FLAG
Definition TER.h:91
@ temDISABLED
Definition TER.h:94
@@ -7404,10 +7404,10 @@ $(document).ready(function() { init_codefold(0); });
@ lsfLoanDefault
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:98
-
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
-
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
+
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
+
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
@ tesSUCCESS
Definition TER.h:225
-
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
+
Number computeFullPaymentInterest(Number const &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
constexpr std::uint32_t const tmfMPTSetCanTransfer
Definition TxFlags.h:171
constexpr std::uint32_t const tfLoanManageMask
Definition TxFlags.h:292
bool isRounded(Asset const &asset, Number const &value, std::int32_t scale)
diff --git a/LogLevel_8cpp_source.html b/LogLevel_8cpp_source.html index 27f752019c..fae798d956 100644 --- a/LogLevel_8cpp_source.html +++ b/LogLevel_8cpp_source.html @@ -150,7 +150,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Severity threshold() const
Returns the minimum severity level this sink will report.
beast::severities::Severity threshold() const
Definition Log.cpp:140
beast::Journal::Sink & get(std::string const &name)
Definition Log.cpp:120
diff --git a/MPTAmount_8cpp_source.html b/MPTAmount_8cpp_source.html index 6d35504bb3..8b2b4361a8 100644 --- a/MPTAmount_8cpp_source.html +++ b/MPTAmount_8cpp_source.html @@ -149,7 +149,7 @@ $(document).ready(function() { init_codefold(0); });
static MPTAmount minPositiveAmount()
Definition MPTAmount.cpp:44
MPTAmount & operator-=(MPTAmount const &other)
Definition MPTAmount.cpp:13
MPTAmount operator-() const
Definition MPTAmount.cpp:20
-
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:113
+
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:114
bool operator<(MPTAmount const &other) const
Definition MPTAmount.cpp:38
MPTAmount & operator+=(MPTAmount const &other)
Definition MPTAmount.cpp:6
value_type value_
Definition MPTAmount.h:25
diff --git a/MPTAmount_8h_source.html b/MPTAmount_8h_source.html index 66d00c5d49..3d1d18fea9 100644 --- a/MPTAmount_8h_source.html +++ b/MPTAmount_8h_source.html @@ -180,63 +180,64 @@ $(document).ready(function() { init_codefold(0); });
94
-
96constexpr MPTAmount::operator bool() const noexcept
-
97{
-
98 return value_ != 0;
-
99}
+
96constexpr MPTAmount::
+
97operator bool() const noexcept
+
98{
+
99 return value_ != 0;
+
100}
-
100
-
102constexpr int
-
-
103MPTAmount::signum() const noexcept
-
104{
-
105 return (value_ < 0) ? -1 : (value_ ? 1 : 0);
-
106}
+
101
+
103constexpr int
+
+
104MPTAmount::signum() const noexcept
+
105{
+
106 return (value_ < 0) ? -1 : (value_ ? 1 : 0);
+
107}
-
107
-
112constexpr MPTAmount::value_type
-
- -
114{
-
115 return value_;
-
116}
+
108
+
113constexpr MPTAmount::value_type
+
+ +
115{
+
116 return value_;
+
117}
-
117
-
118inline std::string
-
-
119to_string(MPTAmount const& amount)
-
120{
-
121 return std::to_string(amount.value());
-
122}
+
118
+
119inline std::string
+
+
120to_string(MPTAmount const& amount)
+
121{
+
122 return std::to_string(amount.value());
+
123}
-
123
-
124inline MPTAmount
-
-
125mulRatio(MPTAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp)
-
126{
-
127 using namespace boost::multiprecision;
-
128
-
129 if (!den)
-
130 Throw<std::runtime_error>("division by zero");
-
131
-
132 int128_t const amt128(amt.value());
-
133 auto const neg = amt.value() < 0;
-
134 auto const m = amt128 * num;
-
135 auto r = m / den;
-
136 if (m % den)
-
137 {
-
138 if (!neg && roundUp)
-
139 r += 1;
-
140 if (neg && !roundUp)
-
141 r -= 1;
-
142 }
- -
144 Throw<std::overflow_error>("MPT mulRatio overflow");
-
145 return MPTAmount(r.convert_to<MPTAmount::value_type>());
-
146}
+
124
+
125inline MPTAmount
+
+
126mulRatio(MPTAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp)
+
127{
+
128 using namespace boost::multiprecision;
+
129
+
130 if (!den)
+
131 Throw<std::runtime_error>("division by zero");
+
132
+
133 int128_t const amt128(amt.value());
+
134 auto const neg = amt.value() < 0;
+
135 auto const m = amt128 * num;
+
136 auto r = m / den;
+
137 if (m % den)
+
138 {
+
139 if (!neg && roundUp)
+
140 r += 1;
+
141 if (neg && !roundUp)
+
142 r -= 1;
+
143 }
+ +
145 Throw<std::overflow_error>("MPT mulRatio overflow");
+
146 return MPTAmount(r.convert_to<MPTAmount::value_type>());
+
147}
-
147
-
148} // namespace xrpl
+
148
+
149} // namespace xrpl
std::int64_t value_type
Definition MPTAmount.h:22
@@ -244,8 +245,8 @@ $(document).ready(function() { init_codefold(0); });
static MPTAmount minPositiveAmount()
Definition MPTAmount.cpp:44
MPTAmount & operator-=(MPTAmount const &other)
Definition MPTAmount.cpp:13
MPTAmount operator-() const
Definition MPTAmount.cpp:20
-
constexpr int signum() const noexcept
Return the sign of the amount.
Definition MPTAmount.h:103
-
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:113
+
constexpr int signum() const noexcept
Return the sign of the amount.
Definition MPTAmount.h:104
+
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:114
MPTAmount(Number const &x)
Definition MPTAmount.h:34
bool operator<(MPTAmount const &other) const
Definition MPTAmount.cpp:38
MPTAmount & operator+=(MPTAmount const &other)
Definition MPTAmount.cpp:6
diff --git a/MPTIssue_8cpp_source.html b/MPTIssue_8cpp_source.html index 6b76bce5b4..978a0c6809 100644 --- a/MPTIssue_8cpp_source.html +++ b/MPTIssue_8cpp_source.html @@ -185,10 +185,10 @@ $(document).ready(function() { init_codefold(0); });
88} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isString() const
-
bool isObject() const
+
bool isString() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:26
diff --git a/MPTokenIssuanceSet_8cpp_source.html b/MPTokenIssuanceSet_8cpp_source.html index 79796dea3c..87b067cb7d 100644 --- a/MPTokenIssuanceSet_8cpp_source.html +++ b/MPTokenIssuanceSet_8cpp_source.html @@ -480,7 +480,7 @@ $(document).ready(function() { init_codefold(0); });
@ lsmfMPTCanMutateRequireAuth
@ lsmfMPTCanMutateCanClawback
constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask
Definition TxFlags.h:159
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
constexpr std::uint32_t const tfMPTokenIssuanceSetMask
Definition TxFlags.h:158
@ tesSUCCESS
Definition TER.h:225
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:43
diff --git a/MPToken__test_8cpp_source.html b/MPToken__test_8cpp_source.html index 3d0bad1625..c2f5d50290 100644 --- a/MPToken__test_8cpp_source.html +++ b/MPToken__test_8cpp_source.html @@ -3312,7 +3312,7 @@ $(document).ready(function() { init_codefold(0); });
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:516
@@ -3368,16 +3368,16 @@ $(document).ready(function() { init_codefold(0); });
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:792
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:533
-
void set(MPTSet const &set={})
Definition mpt.cpp:327
-
void pay(Account const &src, Account const &dest, std::int64_t amount, std::optional< TER > err=std::nullopt, std::optional< std::vector< std::string > > credentials=std::nullopt)
Definition mpt.cpp:468
-
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:126
-
bool isTransferFeePresent() const
Definition mpt.cpp:462
-
bool checkMetadata(std::string const &metadata) const
Definition mpt.cpp:436
-
bool isMetadataPresent() const
Definition mpt.cpp:446
-
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
Definition mpt.cpp:228
+
void set(MPTSet const &set={})
Definition mpt.cpp:328
+
void pay(Account const &src, Account const &dest, std::int64_t amount, std::optional< TER > err=std::nullopt, std::optional< std::vector< std::string > > credentials=std::nullopt)
Definition mpt.cpp:469
+
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:127
+
bool isTransferFeePresent() const
Definition mpt.cpp:463
+
bool checkMetadata(std::string const &metadata) const
Definition mpt.cpp:437
+
bool isMetadataPresent() const
Definition mpt.cpp:447
+
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
Definition mpt.cpp:229
MPTID const & issuanceID() const
Definition mpt.h:249
-
void destroy(MPTDestroy const &arg=MPTDestroy{})
Definition mpt.cpp:195
-
bool checkTransferFee(std::uint16_t transferFee) const
Definition mpt.cpp:452
+
void destroy(MPTDestroy const &arg=MPTDestroy{})
Definition mpt.cpp:196
+
bool checkTransferFee(std::uint16_t transferFee) const
Definition mpt.cpp:453
Converts to MPT Issue or STAmount.
Sets the DeliverMin on a JTx.
Definition delivermin.h:13
Set the fee on a JTx.
Definition fee.h:17
@@ -3409,7 +3409,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value sidechain_xchain_account_create(Account const &acc, Json::Value const &bridge, Account const &dst, AnyAmount const &amt, AnyAmount const &reward)
Json::Value claw(Account const &account, STAmount const &amount, std::optional< Account > const &mptHolder)
Definition trust.cpp:46
Json::Value xchain_claim(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, Account const &dst)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
FeatureBitset testable_amendments()
Definition Env.h:76
diff --git a/Main_8cpp_source.html b/Main_8cpp_source.html index 2383211b4e..09670d9a3a 100644 --- a/Main_8cpp_source.html +++ b/Main_8cpp_source.html @@ -940,7 +940,7 @@ $(document).ready(function() { init_codefold(0); });
bool doVacuumDB(DatabaseCon::Setup const &setup, beast::Journal j)
doVacuumDB Creates, initialises, and performs cleanup on a database.
Definition Vacuum.cpp:10
std::unique_ptr< beast::Journal::Sink > setDebugLogSink(std::unique_ptr< beast::Journal::Sink > sink)
Set the sink for the debug journal.
Definition Log.cpp:439
bool adjustDescriptorLimit(int needed, beast::Journal j)
Definition Main.cpp:59
-
std::unique_ptr< Application > make_Application(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
+
std::unique_ptr< Application > make_Application(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
DatabaseCon::Setup setup_DatabaseCon(Config const &c, std::optional< beast::Journal > j=std::nullopt)
Definition Config.cpp:1043
static std::string const & systemName()
diff --git a/Manifest_8cpp_source.html b/Manifest_8cpp_source.html index 5cd32f6a1e..a748eb70e7 100644 --- a/Manifest_8cpp_source.html +++ b/Manifest_8cpp_source.html @@ -645,42 +645,43 @@ $(document).ready(function() { init_codefold(0); });
523 if (!configRevocation.empty())
524 {
525 std::string revocationStr;
-
526 revocationStr.reserve(std::accumulate(
-
527 configRevocation.cbegin(),
-
528 configRevocation.cend(),
-
529 std::size_t(0),
-
530 [](std::size_t init, std::string const& s) { return init + s.size(); }));
-
531
-
532 for (auto const& line : configRevocation)
-
533 revocationStr += boost::algorithm::trim_copy(line);
-
534
-
535 auto mo = deserializeManifest(base64_decode(revocationStr));
-
536
-
537 if (!mo || !mo->revoked() || applyManifest(std::move(*mo)) == ManifestDisposition::invalid)
-
538 {
-
539 JLOG(j_.error()) << "Invalid validator key revocation in config";
-
540 return false;
-
541 }
-
542 }
-
543
-
544 return true;
-
545}
+
526 revocationStr.reserve(
+ +
528 configRevocation.cbegin(),
+
529 configRevocation.cend(),
+
530 std::size_t(0),
+
531 [](std::size_t init, std::string const& s) { return init + s.size(); }));
+
532
+
533 for (auto const& line : configRevocation)
+
534 revocationStr += boost::algorithm::trim_copy(line);
+
535
+
536 auto mo = deserializeManifest(base64_decode(revocationStr));
+
537
+
538 if (!mo || !mo->revoked() || applyManifest(std::move(*mo)) == ManifestDisposition::invalid)
+
539 {
+
540 JLOG(j_.error()) << "Invalid validator key revocation in config";
+
541 return false;
+
542 }
+
543 }
+
544
+
545 return true;
+
546}
-
546
-
547void
-
- -
549 DatabaseCon& dbCon,
-
550 std::string const& dbTable,
-
551 std::function<bool(PublicKey const&)> const& isTrusted)
-
552{
- -
554 auto db = dbCon.checkoutDb();
-
555
-
556 saveManifests(*db, dbTable, isTrusted, map_, j_);
-
557}
+
547
+
548void
+
+ +
550 DatabaseCon& dbCon,
+
551 std::string const& dbTable,
+
552 std::function<bool(PublicKey const&)> const& isTrusted)
+
553{
+ +
555 auto db = dbCon.checkoutDb();
+
556
+
557 saveManifests(*db, dbTable, isTrusted, map_, j_);
+
558}
-
558} // namespace xrpl
+
559} // namespace xrpl
T accumulate(T... args)
T assign(T... args)
@@ -688,7 +689,7 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:318
Stream debug() const
Definition Journal.h:300
@@ -707,7 +708,7 @@ $(document).ready(function() { init_codefold(0); });
std::optional< std::string > getManifest(PublicKey const &pk) const
Returns manifest corresponding to a given public key.
Definition Manifest.cpp:319
hash_map< PublicKey, Manifest > map_
Active manifests stored by master public key.
Definition Manifest.h:230
std::optional< std::uint32_t > getSequence(PublicKey const &pk) const
Returns master key's current manifest sequence.
Definition Manifest.cpp:295
-
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
Definition Manifest.cpp:548
+
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
Definition Manifest.cpp:549
beast::Journal j_
Definition Manifest.h:226
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
Definition Manifest.cpp:331
A public key.
Definition PublicKey.h:42
diff --git a/Manifest_8h_source.html b/Manifest_8h_source.html index f23d7ef783..95af5774ea 100644 --- a/Manifest_8h_source.html +++ b/Manifest_8h_source.html @@ -406,7 +406,7 @@ $(document).ready(function() { init_codefold(0); });
std::optional< std::string > getManifest(PublicKey const &pk) const
Returns manifest corresponding to a given public key.
Definition Manifest.cpp:319
hash_map< PublicKey, Manifest > map_
Active manifests stored by master public key.
Definition Manifest.h:230
std::optional< std::uint32_t > getSequence(PublicKey const &pk) const
Returns master key's current manifest sequence.
Definition Manifest.cpp:295
-
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
Definition Manifest.cpp:548
+
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
Definition Manifest.cpp:549
beast::Journal j_
Definition Manifest.h:226
A public key.
Definition PublicKey.h:42
A secret key.
Definition SecretKey.h:18
diff --git a/Manifest__test_8cpp_source.html b/Manifest__test_8cpp_source.html index da958050ba..8e24b448d7 100644 --- a/Manifest__test_8cpp_source.html +++ b/Manifest__test_8cpp_source.html @@ -359,648 +359,649 @@ $(document).ready(function() { init_codefold(0); });
250
251 if (inManifests.size() == loadedManifests.size())
252 {
-
253 BEAST_EXPECT(std::equal(
-
254 inManifests.begin(),
-
255 inManifests.end(),
-
256 loadedManifests.begin(),
-
257 [](Manifest const* lhs, Manifest const* rhs) { return *lhs == *rhs; }));
-
258 }
-
259 else
-
260 {
-
261 fail();
-
262 }
-
263 }
-
264 {
-
265 // load config manifest
-
266 ManifestCache loaded;
-
267 std::vector<std::string> const emptyRevocation;
-
268
-
269 std::string const badManifest = "bad manifest";
-
270 BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", badManifest, emptyRevocation));
-
271
-
272 auto const sk = randomSecretKey();
-
273 auto const pk = derivePublicKey(KeyType::ed25519, sk);
-
274 auto const kp = randomKeyPair(KeyType::secp256k1);
-
275
-
276 std::string const cfgManifest = makeManifestString(pk, sk, kp.first, kp.second, 0);
-
277
-
278 BEAST_EXPECT(loaded.load(*dbCon, "ValidatorManifests", cfgManifest, emptyRevocation));
-
279 }
-
280 {
-
281 // load config revocation
-
282 ManifestCache loaded;
-
283 std::string const emptyManifest;
-
284
-
285 std::vector<std::string> const badRevocation = {"bad revocation"};
-
286 BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badRevocation));
-
287
-
288 auto const sk = randomSecretKey();
-
289 auto const keyType = KeyType::ed25519;
-
290 auto const pk = derivePublicKey(keyType, sk);
-
291 auto const kp = randomKeyPair(KeyType::secp256k1);
-
292 std::vector<std::string> const nonRevocation = {makeManifestString(pk, sk, kp.first, kp.second, 0)};
-
293
-
294 BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", emptyManifest, nonRevocation));
-
295 BEAST_EXPECT(!loaded.revoked(pk));
-
296
-
297 std::vector<std::string> const badSigRevocation = {makeRevocationString(sk, keyType, true)};
-
298 BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badSigRevocation));
-
299 BEAST_EXPECT(!loaded.revoked(pk));
-
300
-
301 std::vector<std::string> const cfgRevocation = {makeRevocationString(sk, keyType)};
-
302 BEAST_EXPECT(loaded.load(*dbCon, "ValidatorManifests", emptyManifest, cfgRevocation));
-
303
-
304 BEAST_EXPECT(loaded.revoked(pk));
-
305 }
-
306 }
-
307 boost::filesystem::remove(getDatabasePath() / boost::filesystem::path(dbName));
-
308 }
+
253 BEAST_EXPECT(
+ +
255 inManifests.begin(),
+
256 inManifests.end(),
+
257 loadedManifests.begin(),
+
258 [](Manifest const* lhs, Manifest const* rhs) { return *lhs == *rhs; }));
+
259 }
+
260 else
+
261 {
+
262 fail();
+
263 }
+
264 }
+
265 {
+
266 // load config manifest
+
267 ManifestCache loaded;
+
268 std::vector<std::string> const emptyRevocation;
+
269
+
270 std::string const badManifest = "bad manifest";
+
271 BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", badManifest, emptyRevocation));
+
272
+
273 auto const sk = randomSecretKey();
+
274 auto const pk = derivePublicKey(KeyType::ed25519, sk);
+
275 auto const kp = randomKeyPair(KeyType::secp256k1);
+
276
+
277 std::string const cfgManifest = makeManifestString(pk, sk, kp.first, kp.second, 0);
+
278
+
279 BEAST_EXPECT(loaded.load(*dbCon, "ValidatorManifests", cfgManifest, emptyRevocation));
+
280 }
+
281 {
+
282 // load config revocation
+
283 ManifestCache loaded;
+
284 std::string const emptyManifest;
+
285
+
286 std::vector<std::string> const badRevocation = {"bad revocation"};
+
287 BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badRevocation));
+
288
+
289 auto const sk = randomSecretKey();
+
290 auto const keyType = KeyType::ed25519;
+
291 auto const pk = derivePublicKey(keyType, sk);
+
292 auto const kp = randomKeyPair(KeyType::secp256k1);
+
293 std::vector<std::string> const nonRevocation = {makeManifestString(pk, sk, kp.first, kp.second, 0)};
+
294
+
295 BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", emptyManifest, nonRevocation));
+
296 BEAST_EXPECT(!loaded.revoked(pk));
+
297
+
298 std::vector<std::string> const badSigRevocation = {makeRevocationString(sk, keyType, true)};
+
299 BEAST_EXPECT(!loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badSigRevocation));
+
300 BEAST_EXPECT(!loaded.revoked(pk));
+
301
+
302 std::vector<std::string> const cfgRevocation = {makeRevocationString(sk, keyType)};
+
303 BEAST_EXPECT(loaded.load(*dbCon, "ValidatorManifests", emptyManifest, cfgRevocation));
+
304
+
305 BEAST_EXPECT(loaded.revoked(pk));
+
306 }
+
307 }
+
308 boost::filesystem::remove(getDatabasePath() / boost::filesystem::path(dbName));
+
309 }
-
309
-
310 void
-
- -
312 {
-
313 testcase("getSignature");
-
314 auto const sk = randomSecretKey();
-
315 auto const pk = derivePublicKey(KeyType::ed25519, sk);
-
316 auto const kp = randomKeyPair(KeyType::secp256k1);
-
317 auto const m = makeManifest(sk, KeyType::ed25519, kp.second, KeyType::secp256k1, 0);
-
318
- -
320 st[sfSequence] = 0;
-
321 st[sfPublicKey] = pk;
-
322 st[sfSigningPubKey] = kp.first;
-
323 Serializer ss;
- - -
326 auto const sig = sign(KeyType::secp256k1, kp.second, ss.slice());
-
327 BEAST_EXPECT(strHex(sig) == strHex(*m.getSignature()));
-
328
-
329 auto const masterSig = sign(KeyType::ed25519, sk, ss.slice());
-
330 BEAST_EXPECT(strHex(masterSig) == strHex(m.getMasterSignature()));
-
331 }
+
310
+
311 void
+
+ +
313 {
+
314 testcase("getSignature");
+
315 auto const sk = randomSecretKey();
+
316 auto const pk = derivePublicKey(KeyType::ed25519, sk);
+
317 auto const kp = randomKeyPair(KeyType::secp256k1);
+
318 auto const m = makeManifest(sk, KeyType::ed25519, kp.second, KeyType::secp256k1, 0);
+
319
+ +
321 st[sfSequence] = 0;
+
322 st[sfPublicKey] = pk;
+
323 st[sfSigningPubKey] = kp.first;
+
324 Serializer ss;
+ + +
327 auto const sig = sign(KeyType::secp256k1, kp.second, ss.slice());
+
328 BEAST_EXPECT(strHex(sig) == strHex(*m.getSignature()));
+
329
+
330 auto const masterSig = sign(KeyType::ed25519, sk, ss.slice());
+
331 BEAST_EXPECT(strHex(masterSig) == strHex(m.getMasterSignature()));
+
332 }
-
332
-
333 void
-
- -
335 {
-
336 testcase("getKeys");
-
337
-
338 ManifestCache cache;
-
339 auto const sk = randomSecretKey();
-
340 auto const pk = derivePublicKey(KeyType::ed25519, sk);
-
341
-
342 // getSigningKey should return same key if there is no manifest
-
343 BEAST_EXPECT(cache.getSigningKey(pk) == pk);
-
344
-
345 // getSigningKey should return the ephemeral public key
-
346 // for the listed validator master public key
-
347 // getMasterKey should return the listed validator master key
-
348 // for that ephemeral public key
-
349 auto const kp0 = randomKeyPair(KeyType::secp256k1);
-
350 BEAST_EXPECT(
- - -
353 BEAST_EXPECT(cache.getSigningKey(pk) == kp0.first);
-
354 BEAST_EXPECT(cache.getMasterKey(kp0.first) == pk);
-
355
-
356 // getSigningKey should return the latest ephemeral public key
-
357 // for the listed validator master public key
-
358 // getMasterKey should only return a master key for the latest
-
359 // ephemeral public key
-
360 auto const kp1 = randomKeyPair(KeyType::secp256k1);
-
361 BEAST_EXPECT(
- - -
364 BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
-
365 BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
-
366 BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
-
367
-
368 // getSigningKey and getMasterKey should fail if a new manifest is
-
369 // applied with the same signing key but a higher sequence
-
370 BEAST_EXPECT(
- - -
373 BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
-
374 BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
-
375 BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
-
376
-
377 // getSigningKey should return std::nullopt for a revoked master public
-
378 // key getMasterKey should return std::nullopt for an ephemeral public
-
379 // key from a revoked master public key
- -
381 BEAST_EXPECT(cache.revoked(pk));
-
382 BEAST_EXPECT(cache.getSigningKey(pk) == pk);
-
383 BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
-
384 BEAST_EXPECT(cache.getMasterKey(kp1.first) == kp1.first);
-
385 }
+
333
+
334 void
+
+ +
336 {
+
337 testcase("getKeys");
+
338
+
339 ManifestCache cache;
+
340 auto const sk = randomSecretKey();
+
341 auto const pk = derivePublicKey(KeyType::ed25519, sk);
+
342
+
343 // getSigningKey should return same key if there is no manifest
+
344 BEAST_EXPECT(cache.getSigningKey(pk) == pk);
+
345
+
346 // getSigningKey should return the ephemeral public key
+
347 // for the listed validator master public key
+
348 // getMasterKey should return the listed validator master key
+
349 // for that ephemeral public key
+
350 auto const kp0 = randomKeyPair(KeyType::secp256k1);
+
351 BEAST_EXPECT(
+ + +
354 BEAST_EXPECT(cache.getSigningKey(pk) == kp0.first);
+
355 BEAST_EXPECT(cache.getMasterKey(kp0.first) == pk);
+
356
+
357 // getSigningKey should return the latest ephemeral public key
+
358 // for the listed validator master public key
+
359 // getMasterKey should only return a master key for the latest
+
360 // ephemeral public key
+
361 auto const kp1 = randomKeyPair(KeyType::secp256k1);
+
362 BEAST_EXPECT(
+ + +
365 BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
+
366 BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
+
367 BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
+
368
+
369 // getSigningKey and getMasterKey should fail if a new manifest is
+
370 // applied with the same signing key but a higher sequence
+
371 BEAST_EXPECT(
+ + +
374 BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
+
375 BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
+
376 BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
+
377
+
378 // getSigningKey should return std::nullopt for a revoked master public
+
379 // key getMasterKey should return std::nullopt for an ephemeral public
+
380 // key from a revoked master public key
+ +
382 BEAST_EXPECT(cache.revoked(pk));
+
383 BEAST_EXPECT(cache.getSigningKey(pk) == pk);
+
384 BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
+
385 BEAST_EXPECT(cache.getMasterKey(kp1.first) == kp1.first);
+
386 }
-
386
-
387 void
-
- -
389 {
-
390 testcase("validator token");
-
391
-
392 {
-
393 auto const valSecret =
-
394 parseBase58<SecretKey>(TokenType::NodePrivate, "paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi");
-
395
-
396 // Format token string to test trim()
-
397 std::vector<std::string> const tokenBlob = {
-
398 " "
-
399 "eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3ND"
-
400 "diNT\n",
-
401 " \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2Iiwib"
-
402 "WFuaWZl \n",
-
403 "\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncx"
-
404 "L3ZDeE\n",
-
405 "\t "
-
406 "hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4"
-
407 "U0tG\t \t\n",
-
408 "bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUz"
-
409 "ZQU2\n",
-
410 "hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZ"
-
411 "eXd1\n",
-
412 "NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZW"
-
413 "RGdj\n",
-
414 "VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0i"
-
415 "fQ==\n"};
-
416
-
417 auto const manifest =
-
418 "JAAAAAFxIe1FtwmimvGtH2iCcMJqC9gVFKilGfw1/"
-
419 "vCxHXXLplc2GnMhAkE1agqXxBwD"
-
420 "wDbID6OMSYuM0FDAlpAgNk8SKFn7MO2fdkcwRQIhAOngu9sAKqXYouJ+l2V0W+"
-
421 "sAOkVB"
-
422 "+ZRS6PShlJAfUsXfAiBsVJGesaadOJc/"
-
423 "aAZokS1vymGmVrlHPKWX3Yywu6in8HASQKPu"
-
424 "gBD67kMaRFGvmpATHlGKJdvDFlWPYy5AqDedFv5TJa2w0i21eq3MYywLVJZnFO"
-
425 "r7C0kw"
-
426 "2AiTzSCjIzditQ8=";
-
427
-
428 auto const token = loadValidatorToken(tokenBlob);
-
429 BEAST_EXPECT(token);
-
430 BEAST_EXPECT(token->validationSecret == *valSecret);
-
431 BEAST_EXPECT(token->manifest == manifest);
-
432 }
-
433 {
-
434 std::vector<std::string> const badToken = {"bad token"};
-
435 BEAST_EXPECT(!loadValidatorToken(badToken));
-
436 }
-
437 }
+
387
+
388 void
+
+ +
390 {
+
391 testcase("validator token");
+
392
+
393 {
+
394 auto const valSecret =
+
395 parseBase58<SecretKey>(TokenType::NodePrivate, "paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi");
+
396
+
397 // Format token string to test trim()
+
398 std::vector<std::string> const tokenBlob = {
+
399 " "
+
400 "eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3ND"
+
401 "diNT\n",
+
402 " \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2Iiwib"
+
403 "WFuaWZl \n",
+
404 "\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncx"
+
405 "L3ZDeE\n",
+
406 "\t "
+
407 "hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4"
+
408 "U0tG\t \t\n",
+
409 "bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUz"
+
410 "ZQU2\n",
+
411 "hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZ"
+
412 "eXd1\n",
+
413 "NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZW"
+
414 "RGdj\n",
+
415 "VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0i"
+
416 "fQ==\n"};
+
417
+
418 auto const manifest =
+
419 "JAAAAAFxIe1FtwmimvGtH2iCcMJqC9gVFKilGfw1/"
+
420 "vCxHXXLplc2GnMhAkE1agqXxBwD"
+
421 "wDbID6OMSYuM0FDAlpAgNk8SKFn7MO2fdkcwRQIhAOngu9sAKqXYouJ+l2V0W+"
+
422 "sAOkVB"
+
423 "+ZRS6PShlJAfUsXfAiBsVJGesaadOJc/"
+
424 "aAZokS1vymGmVrlHPKWX3Yywu6in8HASQKPu"
+
425 "gBD67kMaRFGvmpATHlGKJdvDFlWPYy5AqDedFv5TJa2w0i21eq3MYywLVJZnFO"
+
426 "r7C0kw"
+
427 "2AiTzSCjIzditQ8=";
+
428
+
429 auto const token = loadValidatorToken(tokenBlob);
+
430 BEAST_EXPECT(token);
+
431 BEAST_EXPECT(token->validationSecret == *valSecret);
+
432 BEAST_EXPECT(token->manifest == manifest);
+
433 }
+
434 {
+
435 std::vector<std::string> const badToken = {"bad token"};
+
436 BEAST_EXPECT(!loadValidatorToken(badToken));
+
437 }
+
438 }
-
438
-
439 void
-
- -
441 {
-
442 testcase("Versioning");
-
443
- -
445 auto const pk = derivePublicKey(KeyType::ed25519, sk);
-
446
- -
448 auto const spk = derivePublicKey(KeyType::secp256k1, ssk);
-
449
-
450 auto buildManifestObject = [&](std::uint16_t version) {
- -
452 st[sfSequence] = 3;
-
453 st[sfPublicKey] = pk;
-
454 st[sfSigningPubKey] = spk;
-
455
-
456 if (version != 0)
-
457 st[sfVersion] = version;
-
458
-
459 sign(st, HashPrefix::manifest, KeyType::ed25519, sk, sfMasterSignature);
- -
461
-
462 Serializer s;
-
463 st.add(s);
-
464
-
465 return std::string(static_cast<char const*>(s.data()), s.size());
-
466 };
-
467
-
468 // We understand version 0 manifests:
-
469 BEAST_EXPECT(deserializeManifest(buildManifestObject(0)));
-
470
-
471 // We don't understand any other versions:
-
472 BEAST_EXPECT(!deserializeManifest(buildManifestObject(1)));
-
473 BEAST_EXPECT(!deserializeManifest(buildManifestObject(2001)));
-
474 }
+
439
+
440 void
+
+ +
442 {
+
443 testcase("Versioning");
+
444
+ +
446 auto const pk = derivePublicKey(KeyType::ed25519, sk);
+
447
+ +
449 auto const spk = derivePublicKey(KeyType::secp256k1, ssk);
+
450
+
451 auto buildManifestObject = [&](std::uint16_t version) {
+ +
453 st[sfSequence] = 3;
+
454 st[sfPublicKey] = pk;
+
455 st[sfSigningPubKey] = spk;
+
456
+
457 if (version != 0)
+
458 st[sfVersion] = version;
+
459
+
460 sign(st, HashPrefix::manifest, KeyType::ed25519, sk, sfMasterSignature);
+ +
462
+
463 Serializer s;
+
464 st.add(s);
+
465
+
466 return std::string(static_cast<char const*>(s.data()), s.size());
+
467 };
+
468
+
469 // We understand version 0 manifests:
+
470 BEAST_EXPECT(deserializeManifest(buildManifestObject(0)));
+
471
+
472 // We don't understand any other versions:
+
473 BEAST_EXPECT(!deserializeManifest(buildManifestObject(1)));
+
474 BEAST_EXPECT(!deserializeManifest(buildManifestObject(2001)));
+
475 }
-
475
-
476 void
-
- -
478 {
- -
480
-
481 std::uint32_t sequence = 0;
-
482
-
483 // public key with invalid type
-
484 std::array<std::uint8_t, 33> const badKey{0x99, 0x30, 0xE7, 0xFC, 0x9D, 0x56, 0xBB, 0x25, 0xD6, 0x89, 0x3B,
-
485 0xA3, 0xF3, 0x17, 0xAE, 0x5B, 0xCF, 0x33, 0xB3, 0x29, 0x1B, 0xD6,
-
486 0x3D, 0xB3, 0x26, 0x54, 0xA3, 0x13, 0x22, 0x2F, 0x7F, 0xD0, 0x20};
-
487
-
488 // Short public key:
-
489 std::array<std::uint8_t, 16> const shortKey{
-
490 0x03, 0x30, 0xE7, 0xFC, 0x9D, 0x56, 0xBB, 0x25, 0xD6, 0x89, 0x3B, 0xA3, 0xF3, 0x17, 0xAE, 0x5B};
-
491
-
492 auto toString = [](STObject const& st) {
-
493 Serializer s;
-
494 st.add(s);
-
495
-
496 return std::string(static_cast<char const*>(s.data()), s.size());
-
497 };
-
498
-
499 for (auto const keyType : keyTypes)
-
500 {
-
501 auto const sk = generateSecretKey(keyType, randomSeed());
-
502 auto const pk = derivePublicKey(keyType, sk);
-
503
-
504 for (auto const sKeyType : keyTypes)
-
505 {
-
506 auto const ssk = generateSecretKey(sKeyType, randomSeed());
-
507 auto const spk = derivePublicKey(sKeyType, ssk);
-
508
-
509 auto buildManifestObject = [&](std::uint32_t seq,
- -
511 bool noSigningPublic = false,
-
512 bool noSignature = false) {
- -
514 st[sfSequence] = seq;
-
515 st[sfPublicKey] = pk;
-
516
-
517 if (domain)
-
518 st[sfDomain] = makeSlice(*domain);
-
519
-
520 if (!noSigningPublic)
-
521 st[sfSigningPubKey] = spk;
-
522
-
523 sign(st, HashPrefix::manifest, keyType, sk, sfMasterSignature);
-
524
-
525 if (!noSignature)
-
526 sign(st, HashPrefix::manifest, sKeyType, ssk);
-
527
-
528 return st;
-
529 };
-
530
-
531 {
-
532 testcase << "deserializeManifest: normal manifest (" << to_string(keyType) << " + "
-
533 << to_string(sKeyType) << ")";
-
534
-
535 { // valid manifest without domain
-
536 auto const st = buildManifestObject(++sequence, std::nullopt);
-
537
-
538 auto const m = toString(st);
-
539 auto const manifest = deserializeManifest(m);
-
540
-
541 BEAST_EXPECT(manifest);
-
542 BEAST_EXPECT(manifest->masterKey == pk);
-
543 BEAST_EXPECT(manifest->signingKey == spk);
-
544 BEAST_EXPECT(manifest->sequence == sequence);
-
545 BEAST_EXPECT(manifest->serialized == m);
-
546 BEAST_EXPECT(manifest->domain.empty());
-
547 BEAST_EXPECT(manifest->verify());
-
548 }
-
549
-
550 { // invalid manifest (empty domain)
-
551 auto const st = buildManifestObject(++sequence, std::string{});
-
552
-
553 BEAST_EXPECT(!deserializeManifest(toString(st)));
-
554 }
-
555
-
556 { // invalid manifest (domain too short)
-
557 auto const st = buildManifestObject(++sequence, std::string{"a.b"});
-
558 BEAST_EXPECT(!deserializeManifest(toString(st)));
-
559 }
-
560 { // invalid manifest (domain too long)
-
561 std::string s(254, 'a');
-
562 auto const st = buildManifestObject(++sequence, s + ".example.com");
-
563 BEAST_EXPECT(!deserializeManifest(toString(st)));
-
564 }
-
565 { // invalid manifest (domain component too long)
-
566 std::string s(72, 'a');
-
567 auto const st = buildManifestObject(++sequence, s + ".example.com");
-
568 BEAST_EXPECT(!deserializeManifest(toString(st)));
-
569 }
-
570
-
571 auto const st = buildManifestObject(++sequence, std::string{"example.com"});
-
572
-
573 {
-
574 // valid manifest with domain
-
575 auto const m = toString(st);
-
576 auto const manifest = deserializeManifest(m);
-
577
-
578 BEAST_EXPECT(manifest);
-
579 BEAST_EXPECT(manifest->masterKey == pk);
-
580 BEAST_EXPECT(manifest->signingKey == spk);
-
581 BEAST_EXPECT(manifest->sequence == sequence);
-
582 BEAST_EXPECT(manifest->serialized == m);
-
583 BEAST_EXPECT(manifest->domain == "example.com");
-
584 BEAST_EXPECT(manifest->verify());
-
585 }
-
586 {
-
587 // valid manifest with invalid signature
-
588 auto badSigSt = st;
-
589 badSigSt[sfSequence] = sequence + 1;
-
590
-
591 auto const m = toString(badSigSt);
-
592 auto const manifest = deserializeManifest(m);
-
593
-
594 BEAST_EXPECT(manifest);
-
595 BEAST_EXPECT(manifest->masterKey == pk);
-
596 BEAST_EXPECT(manifest->signingKey == spk);
-
597 BEAST_EXPECT(manifest->sequence == sequence + 1);
-
598 BEAST_EXPECT(manifest->serialized == m);
-
599 BEAST_EXPECT(manifest->domain == "example.com");
-
600 BEAST_EXPECT(!manifest->verify());
-
601 }
-
602 {
-
603 // reject missing sequence
-
604 auto badSt = st;
-
605 BEAST_EXPECT(badSt.delField(sfSequence));
-
606 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
-
607 }
-
608 {
-
609 // reject missing public key
-
610 auto badSt = st;
-
611 BEAST_EXPECT(badSt.delField(sfPublicKey));
-
612 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
-
613 }
-
614 {
-
615 // reject invalid public key type
-
616 auto badSt = st;
-
617 badSt[sfPublicKey] = makeSlice(badKey);
-
618 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
-
619 }
-
620 {
-
621 // reject short public key
-
622 auto badSt = st;
-
623 badSt[sfPublicKey] = makeSlice(shortKey);
-
624 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
-
625 }
-
626 {
-
627 // reject missing signing public key
-
628 auto badSt = st;
-
629 BEAST_EXPECT(badSt.delField(sfSigningPubKey));
-
630 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
-
631 }
-
632 {
-
633 // reject invalid signing public key type
-
634 auto badSt = st;
-
635 badSt[sfSigningPubKey] = makeSlice(badKey);
-
636 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
-
637 }
-
638 {
-
639 // reject short signing public key
-
640 auto badSt = st;
-
641 badSt[sfSigningPubKey] = makeSlice(shortKey);
-
642 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
-
643 }
-
644 {
-
645 // reject missing signature
-
646 auto badSt = st;
-
647 BEAST_EXPECT(badSt.delField(sfMasterSignature));
-
648 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
-
649 }
-
650 {
-
651 // reject missing signing key signature
-
652 auto badSt = st;
-
653 BEAST_EXPECT(badSt.delField(sfSignature));
-
654 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
-
655 }
-
656 {
-
657 // reject matching master & ephemeral keys
- -
659 st[sfSequence] = 314159;
-
660 st[sfPublicKey] = pk;
-
661 st[sfSigningPubKey] = pk;
-
662
-
663 sign(st, HashPrefix::manifest, keyType, sk, sfMasterSignature);
-
664
-
665 sign(st, HashPrefix::manifest, sKeyType, sk);
-
666
-
667 BEAST_EXPECT(!deserializeManifest(toString(st)));
-
668 }
-
669 }
-
670
-
671 {
-
672 testcase << "deserializeManifest: revocation manifest (" << to_string(keyType) << " + "
-
673 << to_string(sKeyType) << ")";
-
674
-
675 // valid revocation
-
676 {
-
677 auto const st =
-
678 buildManifestObject(std::numeric_limits<std::uint32_t>::max(), std::nullopt, true, true);
-
679
-
680 auto const m = toString(st);
-
681 auto const manifest = deserializeManifest(m);
-
682
-
683 BEAST_EXPECT(manifest);
-
684 BEAST_EXPECT(manifest->masterKey == pk);
-
685
-
686 // Since this manifest is revoked, it should not have
-
687 // a signingKey
-
688 BEAST_EXPECT(!manifest->signingKey);
-
689 BEAST_EXPECT(manifest->revoked());
-
690 BEAST_EXPECT(manifest->domain.empty());
-
691 BEAST_EXPECT(manifest->serialized == m);
-
692 BEAST_EXPECT(manifest->verify());
-
693 }
-
694
-
695 { // can't specify an ephemeral signing key
-
696 auto const st =
-
697 buildManifestObject(std::numeric_limits<std::uint32_t>::max(), std::nullopt, true, false);
-
698
-
699 BEAST_EXPECT(!deserializeManifest(toString(st)));
-
700 }
-
701 { // can't specify an ephemeral signature
-
702 auto const st =
-
703 buildManifestObject(std::numeric_limits<std::uint32_t>::max(), std::nullopt, false, true);
-
704
-
705 BEAST_EXPECT(!deserializeManifest(toString(st)));
-
706 }
-
707 { // can't specify an ephemeral key & signature
-
708 auto const st =
-
709 buildManifestObject(std::numeric_limits<std::uint32_t>::max(), std::nullopt, false, false);
-
710
-
711 BEAST_EXPECT(!deserializeManifest(toString(st)));
-
712 }
-
713 }
-
714 }
-
715 }
-
716 }
+
476
+
477 void
+
+ +
479 {
+ +
481
+
482 std::uint32_t sequence = 0;
+
483
+
484 // public key with invalid type
+
485 std::array<std::uint8_t, 33> const badKey{0x99, 0x30, 0xE7, 0xFC, 0x9D, 0x56, 0xBB, 0x25, 0xD6, 0x89, 0x3B,
+
486 0xA3, 0xF3, 0x17, 0xAE, 0x5B, 0xCF, 0x33, 0xB3, 0x29, 0x1B, 0xD6,
+
487 0x3D, 0xB3, 0x26, 0x54, 0xA3, 0x13, 0x22, 0x2F, 0x7F, 0xD0, 0x20};
+
488
+
489 // Short public key:
+
490 std::array<std::uint8_t, 16> const shortKey{
+
491 0x03, 0x30, 0xE7, 0xFC, 0x9D, 0x56, 0xBB, 0x25, 0xD6, 0x89, 0x3B, 0xA3, 0xF3, 0x17, 0xAE, 0x5B};
+
492
+
493 auto toString = [](STObject const& st) {
+
494 Serializer s;
+
495 st.add(s);
+
496
+
497 return std::string(static_cast<char const*>(s.data()), s.size());
+
498 };
+
499
+
500 for (auto const keyType : keyTypes)
+
501 {
+
502 auto const sk = generateSecretKey(keyType, randomSeed());
+
503 auto const pk = derivePublicKey(keyType, sk);
+
504
+
505 for (auto const sKeyType : keyTypes)
+
506 {
+
507 auto const ssk = generateSecretKey(sKeyType, randomSeed());
+
508 auto const spk = derivePublicKey(sKeyType, ssk);
+
509
+
510 auto buildManifestObject = [&](std::uint32_t seq,
+ +
512 bool noSigningPublic = false,
+
513 bool noSignature = false) {
+ +
515 st[sfSequence] = seq;
+
516 st[sfPublicKey] = pk;
+
517
+
518 if (domain)
+
519 st[sfDomain] = makeSlice(*domain);
+
520
+
521 if (!noSigningPublic)
+
522 st[sfSigningPubKey] = spk;
+
523
+
524 sign(st, HashPrefix::manifest, keyType, sk, sfMasterSignature);
+
525
+
526 if (!noSignature)
+
527 sign(st, HashPrefix::manifest, sKeyType, ssk);
+
528
+
529 return st;
+
530 };
+
531
+
532 {
+
533 testcase << "deserializeManifest: normal manifest (" << to_string(keyType) << " + "
+
534 << to_string(sKeyType) << ")";
+
535
+
536 { // valid manifest without domain
+
537 auto const st = buildManifestObject(++sequence, std::nullopt);
+
538
+
539 auto const m = toString(st);
+
540 auto const manifest = deserializeManifest(m);
+
541
+
542 BEAST_EXPECT(manifest);
+
543 BEAST_EXPECT(manifest->masterKey == pk);
+
544 BEAST_EXPECT(manifest->signingKey == spk);
+
545 BEAST_EXPECT(manifest->sequence == sequence);
+
546 BEAST_EXPECT(manifest->serialized == m);
+
547 BEAST_EXPECT(manifest->domain.empty());
+
548 BEAST_EXPECT(manifest->verify());
+
549 }
+
550
+
551 { // invalid manifest (empty domain)
+
552 auto const st = buildManifestObject(++sequence, std::string{});
+
553
+
554 BEAST_EXPECT(!deserializeManifest(toString(st)));
+
555 }
+
556
+
557 { // invalid manifest (domain too short)
+
558 auto const st = buildManifestObject(++sequence, std::string{"a.b"});
+
559 BEAST_EXPECT(!deserializeManifest(toString(st)));
+
560 }
+
561 { // invalid manifest (domain too long)
+
562 std::string s(254, 'a');
+
563 auto const st = buildManifestObject(++sequence, s + ".example.com");
+
564 BEAST_EXPECT(!deserializeManifest(toString(st)));
+
565 }
+
566 { // invalid manifest (domain component too long)
+
567 std::string s(72, 'a');
+
568 auto const st = buildManifestObject(++sequence, s + ".example.com");
+
569 BEAST_EXPECT(!deserializeManifest(toString(st)));
+
570 }
+
571
+
572 auto const st = buildManifestObject(++sequence, std::string{"example.com"});
+
573
+
574 {
+
575 // valid manifest with domain
+
576 auto const m = toString(st);
+
577 auto const manifest = deserializeManifest(m);
+
578
+
579 BEAST_EXPECT(manifest);
+
580 BEAST_EXPECT(manifest->masterKey == pk);
+
581 BEAST_EXPECT(manifest->signingKey == spk);
+
582 BEAST_EXPECT(manifest->sequence == sequence);
+
583 BEAST_EXPECT(manifest->serialized == m);
+
584 BEAST_EXPECT(manifest->domain == "example.com");
+
585 BEAST_EXPECT(manifest->verify());
+
586 }
+
587 {
+
588 // valid manifest with invalid signature
+
589 auto badSigSt = st;
+
590 badSigSt[sfSequence] = sequence + 1;
+
591
+
592 auto const m = toString(badSigSt);
+
593 auto const manifest = deserializeManifest(m);
+
594
+
595 BEAST_EXPECT(manifest);
+
596 BEAST_EXPECT(manifest->masterKey == pk);
+
597 BEAST_EXPECT(manifest->signingKey == spk);
+
598 BEAST_EXPECT(manifest->sequence == sequence + 1);
+
599 BEAST_EXPECT(manifest->serialized == m);
+
600 BEAST_EXPECT(manifest->domain == "example.com");
+
601 BEAST_EXPECT(!manifest->verify());
+
602 }
+
603 {
+
604 // reject missing sequence
+
605 auto badSt = st;
+
606 BEAST_EXPECT(badSt.delField(sfSequence));
+
607 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
+
608 }
+
609 {
+
610 // reject missing public key
+
611 auto badSt = st;
+
612 BEAST_EXPECT(badSt.delField(sfPublicKey));
+
613 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
+
614 }
+
615 {
+
616 // reject invalid public key type
+
617 auto badSt = st;
+
618 badSt[sfPublicKey] = makeSlice(badKey);
+
619 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
+
620 }
+
621 {
+
622 // reject short public key
+
623 auto badSt = st;
+
624 badSt[sfPublicKey] = makeSlice(shortKey);
+
625 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
+
626 }
+
627 {
+
628 // reject missing signing public key
+
629 auto badSt = st;
+
630 BEAST_EXPECT(badSt.delField(sfSigningPubKey));
+
631 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
+
632 }
+
633 {
+
634 // reject invalid signing public key type
+
635 auto badSt = st;
+
636 badSt[sfSigningPubKey] = makeSlice(badKey);
+
637 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
+
638 }
+
639 {
+
640 // reject short signing public key
+
641 auto badSt = st;
+
642 badSt[sfSigningPubKey] = makeSlice(shortKey);
+
643 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
+
644 }
+
645 {
+
646 // reject missing signature
+
647 auto badSt = st;
+
648 BEAST_EXPECT(badSt.delField(sfMasterSignature));
+
649 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
+
650 }
+
651 {
+
652 // reject missing signing key signature
+
653 auto badSt = st;
+
654 BEAST_EXPECT(badSt.delField(sfSignature));
+
655 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
+
656 }
+
657 {
+
658 // reject matching master & ephemeral keys
+ +
660 st[sfSequence] = 314159;
+
661 st[sfPublicKey] = pk;
+
662 st[sfSigningPubKey] = pk;
+
663
+
664 sign(st, HashPrefix::manifest, keyType, sk, sfMasterSignature);
+
665
+
666 sign(st, HashPrefix::manifest, sKeyType, sk);
+
667
+
668 BEAST_EXPECT(!deserializeManifest(toString(st)));
+
669 }
+
670 }
+
671
+
672 {
+
673 testcase << "deserializeManifest: revocation manifest (" << to_string(keyType) << " + "
+
674 << to_string(sKeyType) << ")";
+
675
+
676 // valid revocation
+
677 {
+
678 auto const st =
+
679 buildManifestObject(std::numeric_limits<std::uint32_t>::max(), std::nullopt, true, true);
+
680
+
681 auto const m = toString(st);
+
682 auto const manifest = deserializeManifest(m);
+
683
+
684 BEAST_EXPECT(manifest);
+
685 BEAST_EXPECT(manifest->masterKey == pk);
+
686
+
687 // Since this manifest is revoked, it should not have
+
688 // a signingKey
+
689 BEAST_EXPECT(!manifest->signingKey);
+
690 BEAST_EXPECT(manifest->revoked());
+
691 BEAST_EXPECT(manifest->domain.empty());
+
692 BEAST_EXPECT(manifest->serialized == m);
+
693 BEAST_EXPECT(manifest->verify());
+
694 }
+
695
+
696 { // can't specify an ephemeral signing key
+
697 auto const st =
+
698 buildManifestObject(std::numeric_limits<std::uint32_t>::max(), std::nullopt, true, false);
+
699
+
700 BEAST_EXPECT(!deserializeManifest(toString(st)));
+
701 }
+
702 { // can't specify an ephemeral signature
+
703 auto const st =
+
704 buildManifestObject(std::numeric_limits<std::uint32_t>::max(), std::nullopt, false, true);
+
705
+
706 BEAST_EXPECT(!deserializeManifest(toString(st)));
+
707 }
+
708 { // can't specify an ephemeral key & signature
+
709 auto const st =
+
710 buildManifestObject(std::numeric_limits<std::uint32_t>::max(), std::nullopt, false, false);
+
711
+
712 BEAST_EXPECT(!deserializeManifest(toString(st)));
+
713 }
+
714 }
+
715 }
+
716 }
+
717 }
-
717
-
718 void
-
- -
720 {
-
721 testcase("Manifest Domain Names");
-
722
- -
724 auto const pk1 = derivePublicKey(KeyType::secp256k1, sk1);
-
725
- -
727 auto const pk2 = derivePublicKey(KeyType::secp256k1, sk2);
-
728
-
729 auto test = [&](std::string domain) {
- -
731 st[sfSequence] = 7;
-
732 st[sfPublicKey] = pk1;
-
733 st[sfDomain] = makeSlice(domain);
-
734 st[sfSigningPubKey] = pk2;
-
735
-
736 sign(st, HashPrefix::manifest, KeyType::secp256k1, sk1, sfMasterSignature);
- -
738
-
739 Serializer s;
-
740 st.add(s);
-
741
-
742 return deserializeManifest(std::string(static_cast<char const*>(s.data()), s.size()));
-
743 };
-
744
-
745 BEAST_EXPECT(test("example.com"));
-
746 BEAST_EXPECT(test("test.example.com"));
-
747 BEAST_EXPECT(test("example-domain.com"));
-
748 BEAST_EXPECT(test("xn--mxavchb.gr"));
-
749 BEAST_EXPECT(test("test.xn--mxavchb.gr"));
-
750 BEAST_EXPECT(test("123.gr"));
-
751 BEAST_EXPECT(test("x.yz"));
-
752 BEAST_EXPECT(test(std::string(63, 'a') + ".example.com"));
-
753 BEAST_EXPECT(test(std::string(63, 'a') + "." + std::string(63, 'b')));
-
754
-
755 // No period
-
756 BEAST_EXPECT(!test("example"));
-
757
-
758 // Leading period:
-
759 BEAST_EXPECT(!test(".com"));
-
760 BEAST_EXPECT(!test(".example.com"));
-
761
-
762 // A trailing period is technically valid but we don't allow it
-
763 BEAST_EXPECT(!test("example.com."));
-
764
-
765 // A component can't start or end with a dash
-
766 BEAST_EXPECT(!test("-example.com"));
-
767 BEAST_EXPECT(!test("example-.com"));
-
768
-
769 // Empty component:
-
770 BEAST_EXPECT(!test("double..periods.example.com"));
-
771
-
772 // TLD too short or too long:
-
773 BEAST_EXPECT(!test("example.x"));
-
774 BEAST_EXPECT(!test("example." + std::string(64, 'a')));
-
775
-
776 // Invalid characters:
-
777 BEAST_EXPECT(!test("example.com-org"));
-
778 BEAST_EXPECT(!test("bang!.com"));
-
779 BEAST_EXPECT(!test("bang!.example.com"));
-
780
-
781 // Too short
-
782 BEAST_EXPECT(!test("a.b"));
-
783
-
784 // Single component too long:
-
785 BEAST_EXPECT(!test(std::string(64, 'a') + ".com"));
-
786 BEAST_EXPECT(!test(std::string(64, 'a') + ".example.com"));
-
787
-
788 // Multiple components too long:
-
789 BEAST_EXPECT(!test(std::string(64, 'a') + "." + std::string(64, 'b')));
+
718
+
719 void
+
+ +
721 {
+
722 testcase("Manifest Domain Names");
+
723
+ +
725 auto const pk1 = derivePublicKey(KeyType::secp256k1, sk1);
+
726
+ +
728 auto const pk2 = derivePublicKey(KeyType::secp256k1, sk2);
+
729
+
730 auto test = [&](std::string domain) {
+ +
732 st[sfSequence] = 7;
+
733 st[sfPublicKey] = pk1;
+
734 st[sfDomain] = makeSlice(domain);
+
735 st[sfSigningPubKey] = pk2;
+
736
+
737 sign(st, HashPrefix::manifest, KeyType::secp256k1, sk1, sfMasterSignature);
+ +
739
+
740 Serializer s;
+
741 st.add(s);
+
742
+
743 return deserializeManifest(std::string(static_cast<char const*>(s.data()), s.size()));
+
744 };
+
745
+
746 BEAST_EXPECT(test("example.com"));
+
747 BEAST_EXPECT(test("test.example.com"));
+
748 BEAST_EXPECT(test("example-domain.com"));
+
749 BEAST_EXPECT(test("xn--mxavchb.gr"));
+
750 BEAST_EXPECT(test("test.xn--mxavchb.gr"));
+
751 BEAST_EXPECT(test("123.gr"));
+
752 BEAST_EXPECT(test("x.yz"));
+
753 BEAST_EXPECT(test(std::string(63, 'a') + ".example.com"));
+
754 BEAST_EXPECT(test(std::string(63, 'a') + "." + std::string(63, 'b')));
+
755
+
756 // No period
+
757 BEAST_EXPECT(!test("example"));
+
758
+
759 // Leading period:
+
760 BEAST_EXPECT(!test(".com"));
+
761 BEAST_EXPECT(!test(".example.com"));
+
762
+
763 // A trailing period is technically valid but we don't allow it
+
764 BEAST_EXPECT(!test("example.com."));
+
765
+
766 // A component can't start or end with a dash
+
767 BEAST_EXPECT(!test("-example.com"));
+
768 BEAST_EXPECT(!test("example-.com"));
+
769
+
770 // Empty component:
+
771 BEAST_EXPECT(!test("double..periods.example.com"));
+
772
+
773 // TLD too short or too long:
+
774 BEAST_EXPECT(!test("example.x"));
+
775 BEAST_EXPECT(!test("example." + std::string(64, 'a')));
+
776
+
777 // Invalid characters:
+
778 BEAST_EXPECT(!test("example.com-org"));
+
779 BEAST_EXPECT(!test("bang!.com"));
+
780 BEAST_EXPECT(!test("bang!.example.com"));
+
781
+
782 // Too short
+
783 BEAST_EXPECT(!test("a.b"));
+
784
+
785 // Single component too long:
+
786 BEAST_EXPECT(!test(std::string(64, 'a') + ".com"));
+
787 BEAST_EXPECT(!test(std::string(64, 'a') + ".example.com"));
+
788
+
789 // Multiple components too long:
790 BEAST_EXPECT(!test(std::string(64, 'a') + "." + std::string(64, 'b')));
-
791
-
792 // Overall too long:
-
793 BEAST_EXPECT(!test(std::string(63, 'a') + "." + std::string(63, 'b') + ".example.com"));
-
794 }
+
791 BEAST_EXPECT(!test(std::string(64, 'a') + "." + std::string(64, 'b')));
+
792
+
793 // Overall too long:
+
794 BEAST_EXPECT(!test(std::string(63, 'a') + "." + std::string(63, 'b') + ".example.com"));
+
795 }
-
795
-
796 void
-
-
797 run() override
-
798 {
-
799 ManifestCache cache;
-
800 {
-
801 testcase("apply");
-
802
-
803 auto const sk_a = randomSecretKey();
-
804 auto const pk_a = derivePublicKey(KeyType::ed25519, sk_a);
-
805 auto const kp_a0 = randomKeyPair(KeyType::secp256k1);
-
806 auto const kp_a1 = randomKeyPair(KeyType::secp256k1);
-
807 auto const s_a0 = makeManifest(sk_a, KeyType::ed25519, kp_a0.second, KeyType::secp256k1, 0);
-
808 auto const s_a1 = makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 1);
-
809 auto const s_a2 = makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 2);
-
810 auto const s_aMax = makeRevocation(sk_a, KeyType::ed25519);
-
811
-
812 auto const sk_b = randomSecretKey();
-
813 auto const kp_b0 = randomKeyPair(KeyType::secp256k1);
-
814 auto const kp_b1 = randomKeyPair(KeyType::secp256k1);
-
815 auto const kp_b2 = randomKeyPair(KeyType::secp256k1);
-
816 auto const s_b0 = makeManifest(sk_b, KeyType::ed25519, kp_b0.second, KeyType::secp256k1, 0);
-
817 auto const s_b1 = makeManifest(
-
818 sk_b,
- -
820 kp_b1.second,
- -
822 1,
-
823 true); // invalidSig
-
824 auto const s_b2 = makeManifest(sk_b, KeyType::ed25519, kp_b2.second, KeyType::ed25519, 2);
-
825
-
826 auto const fake = s_b2.serialized + '\0';
-
827
-
828 // applyManifest should accept new manifests with
-
829 // higher sequence numbers
-
830 auto const seq0 = cache.sequence();
-
831 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::accepted);
-
832 BEAST_EXPECT(cache.sequence() > seq0);
-
833
-
834 auto const seq1 = cache.sequence();
-
835 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
-
836 BEAST_EXPECT(cache.sequence() == seq1);
-
837
-
838 BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::accepted);
-
839 BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::stale);
-
840 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
-
841
-
842 BEAST_EXPECT(cache.applyManifest(clone(s_a2)) == ManifestDisposition::badEphemeralKey);
-
843
-
844 // applyManifest should accept manifests with max sequence numbers
-
845 // that revoke the master public key
-
846 BEAST_EXPECT(!cache.revoked(pk_a));
-
847 BEAST_EXPECT(s_aMax.revoked());
-
848 BEAST_EXPECT(cache.applyManifest(clone(s_aMax)) == ManifestDisposition::accepted);
-
849 BEAST_EXPECT(cache.applyManifest(clone(s_aMax)) == ManifestDisposition::stale);
-
850 BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::stale);
-
851 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
-
852 BEAST_EXPECT(cache.revoked(pk_a));
-
853
-
854 // applyManifest should reject manifests with invalid signatures
-
855 BEAST_EXPECT(cache.applyManifest(clone(s_b0)) == ManifestDisposition::accepted);
-
856 BEAST_EXPECT(cache.applyManifest(clone(s_b0)) == ManifestDisposition::stale);
-
857 BEAST_EXPECT(!deserializeManifest(fake));
-
858 BEAST_EXPECT(cache.applyManifest(clone(s_b1)) == ManifestDisposition::invalid);
-
859 BEAST_EXPECT(cache.applyManifest(clone(s_b2)) == ManifestDisposition::accepted);
-
860
-
861 auto const s_c0 = makeManifest(kp_b2.second, KeyType::ed25519, randomSecretKey(), KeyType::ed25519, 47);
-
862 BEAST_EXPECT(cache.applyManifest(clone(s_c0)) == ManifestDisposition::badMasterKey);
-
863 }
-
864
-
865 testLoadStore(cache);
- -
867 testGetKeys();
- - - - -
872 }
+
796
+
797 void
+
+
798 run() override
+
799 {
+
800 ManifestCache cache;
+
801 {
+
802 testcase("apply");
+
803
+
804 auto const sk_a = randomSecretKey();
+
805 auto const pk_a = derivePublicKey(KeyType::ed25519, sk_a);
+
806 auto const kp_a0 = randomKeyPair(KeyType::secp256k1);
+
807 auto const kp_a1 = randomKeyPair(KeyType::secp256k1);
+
808 auto const s_a0 = makeManifest(sk_a, KeyType::ed25519, kp_a0.second, KeyType::secp256k1, 0);
+
809 auto const s_a1 = makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 1);
+
810 auto const s_a2 = makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 2);
+
811 auto const s_aMax = makeRevocation(sk_a, KeyType::ed25519);
+
812
+
813 auto const sk_b = randomSecretKey();
+
814 auto const kp_b0 = randomKeyPair(KeyType::secp256k1);
+
815 auto const kp_b1 = randomKeyPair(KeyType::secp256k1);
+
816 auto const kp_b2 = randomKeyPair(KeyType::secp256k1);
+
817 auto const s_b0 = makeManifest(sk_b, KeyType::ed25519, kp_b0.second, KeyType::secp256k1, 0);
+
818 auto const s_b1 = makeManifest(
+
819 sk_b,
+ +
821 kp_b1.second,
+ +
823 1,
+
824 true); // invalidSig
+
825 auto const s_b2 = makeManifest(sk_b, KeyType::ed25519, kp_b2.second, KeyType::ed25519, 2);
+
826
+
827 auto const fake = s_b2.serialized + '\0';
+
828
+
829 // applyManifest should accept new manifests with
+
830 // higher sequence numbers
+
831 auto const seq0 = cache.sequence();
+
832 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::accepted);
+
833 BEAST_EXPECT(cache.sequence() > seq0);
+
834
+
835 auto const seq1 = cache.sequence();
+
836 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
+
837 BEAST_EXPECT(cache.sequence() == seq1);
+
838
+
839 BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::accepted);
+
840 BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::stale);
+
841 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
+
842
+
843 BEAST_EXPECT(cache.applyManifest(clone(s_a2)) == ManifestDisposition::badEphemeralKey);
+
844
+
845 // applyManifest should accept manifests with max sequence numbers
+
846 // that revoke the master public key
+
847 BEAST_EXPECT(!cache.revoked(pk_a));
+
848 BEAST_EXPECT(s_aMax.revoked());
+
849 BEAST_EXPECT(cache.applyManifest(clone(s_aMax)) == ManifestDisposition::accepted);
+
850 BEAST_EXPECT(cache.applyManifest(clone(s_aMax)) == ManifestDisposition::stale);
+
851 BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::stale);
+
852 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
+
853 BEAST_EXPECT(cache.revoked(pk_a));
+
854
+
855 // applyManifest should reject manifests with invalid signatures
+
856 BEAST_EXPECT(cache.applyManifest(clone(s_b0)) == ManifestDisposition::accepted);
+
857 BEAST_EXPECT(cache.applyManifest(clone(s_b0)) == ManifestDisposition::stale);
+
858 BEAST_EXPECT(!deserializeManifest(fake));
+
859 BEAST_EXPECT(cache.applyManifest(clone(s_b1)) == ManifestDisposition::invalid);
+
860 BEAST_EXPECT(cache.applyManifest(clone(s_b2)) == ManifestDisposition::accepted);
+
861
+
862 auto const s_c0 = makeManifest(kp_b2.second, KeyType::ed25519, randomSecretKey(), KeyType::ed25519, 47);
+
863 BEAST_EXPECT(cache.applyManifest(clone(s_c0)) == ManifestDisposition::badMasterKey);
+
864 }
+
865
+
866 testLoadStore(cache);
+ +
868 testGetKeys();
+ + + + +
873 }
-
873};
+
874};
-
874
-
875BEAST_DEFINE_TESTSUITE(Manifest, app, xrpl);
-
876
-
877} // namespace test
-
878} // namespace xrpl
+
875
+
876BEAST_DEFINE_TESTSUITE(Manifest, app, xrpl);
+
877
+
878} // namespace test
+
879} // namespace xrpl
T begin(T... args)
@@ -1013,11 +1014,11 @@ $(document).ready(function() { init_codefold(0); });
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
Definition Manifest.cpp:343
PublicKey getMasterKey(PublicKey const &pk) const
Returns ephemeral signing key's master public key.
Definition Manifest.cpp:284
std::uint32_t sequence() const
A monotonically increasing number used to detect new manifests.
Definition Manifest.h:244
-
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
Definition Manifest.cpp:548
+
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
Definition Manifest.cpp:549
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
Definition Manifest.cpp:331
A public key.
Definition PublicKey.h:42
-
void addWithoutSigningFields(Serializer &s) const
Definition STObject.h:957
+
void addWithoutSigningFields(Serializer &s) const
Definition STObject.h:963
void add(Serializer &s) const override
Definition STObject.cpp:117
A secret key.
Definition SecretKey.h:18
@@ -1027,23 +1028,23 @@ $(document).ready(function() { init_codefold(0); });
void const * data() const noexcept
Definition Serializer.h:56
std::string makeManifestString(PublicKey const &pk, SecretKey const &sk, PublicKey const &spk, SecretKey const &ssk, int seq)
- +
void testLoadStore(ManifestCache &m)
static PublicKey randomMasterKey()
- -
void run() override
Runs the suite.
- + +
void run() override
Runs the suite.
+ - +
std::string makeRevocationString(SecretKey const &sk, KeyType type, bool invalidSig=false)
static void setupDatabaseDir(boost::filesystem::path const &dbPath)
static void cleanupDatabaseDir(boost::filesystem::path const &dbPath)
Manifest makeManifest(SecretKey const &sk, KeyType type, SecretKey const &ssk, KeyType stype, int seq, bool invalidSig=false)
static PublicKey randomNode()
- +
static boost::filesystem::path getDatabasePath()
- +
Manifest clone(Manifest const &m)
Manifest makeRevocation(SecretKey const &sk, KeyType type, bool invalidSig=false)
A transaction testing environment.
Definition Env.h:119
diff --git a/MultiApiJson_8h_source.html b/MultiApiJson_8h_source.html index ad608066e0..da48596717 100644 --- a/MultiApiJson_8h_source.html +++ b/MultiApiJson_8h_source.html @@ -206,7 +206,7 @@ $(document).ready(function() { init_codefold(0); });
107 // unsigned int version, extra arguments
108 template <typename Json, typename Version, typename... Args, typename Fn>
- +
111 auto
112 operator()(Json& json, Version version, Fn fn, Args&&... args) const
@@ -223,7 +223,7 @@ $(document).ready(function() { init_codefold(0); });
122 // unsigned int version, Json only
123 template <typename Json, typename Version, typename Fn>
- +
126 auto
127 operator()(Json& json, Version version, Fn fn) const -> std::invoke_result_t<Fn, decltype(json.val[0])>
diff --git a/MultiSign__test_8cpp_source.html b/MultiSign__test_8cpp_source.html index 3b7b129bb9..c000eab3d9 100644 --- a/MultiSign__test_8cpp_source.html +++ b/MultiSign__test_8cpp_source.html @@ -1596,8 +1596,8 @@ $(document).ready(function() { init_codefold(0); });
1467} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
const_iterator begin() const
-
const_iterator end() const
+
const_iterator begin() const
+
const_iterator end() const
std::string asString() const
Returns the unquoted string value.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -1694,7 +1694,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:23
@@ -1720,7 +1720,7 @@ $(document).ready(function() { init_codefold(0); });
@ tefMASTER_DISABLED
Definition TER.h:157
@ tefBAD_SIGNATURE
Definition TER.h:159
@ tefNOT_MULTI_SIGNING
Definition TER.h:161
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:10
@ temINVALID_FLAG
Definition TER.h:91
diff --git a/NFTOffers_8cpp_source.html b/NFTOffers_8cpp_source.html index 749ddce935..6eba57e60d 100644 --- a/NFTOffers_8cpp_source.html +++ b/NFTOffers_8cpp_source.html @@ -239,9 +239,9 @@ $(document).ready(function() { init_codefold(0); });
148
149} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isString() const
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:471
diff --git a/NFTokenAcceptOffer_8cpp_source.html b/NFTokenAcceptOffer_8cpp_source.html index bdb6895ebd..5f7cdd5f2b 100644 --- a/NFTokenAcceptOffer_8cpp_source.html +++ b/NFTokenAcceptOffer_8cpp_source.html @@ -682,7 +682,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t const tfNFTokenAcceptOfferMask
Definition TxFlags.h:218
@ temMALFORMED
Definition TER.h:67
@ temBAD_OFFER
Definition TER.h:75
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecCANT_ACCEPT_OWN_NFTOKEN_OFFER
Definition TER.h:305
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecINTERNAL
Definition TER.h:291
diff --git a/NFTokenBurn_8cpp_source.html b/NFTokenBurn_8cpp_source.html index fcab93b8bf..ee8082cb63 100644 --- a/NFTokenBurn_8cpp_source.html +++ b/NFTokenBurn_8cpp_source.html @@ -194,10 +194,10 @@ $(document).ready(function() { init_codefold(0); });
std::uint16_t getFlags(uint256 const &id)
Definition nft.h:40
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::size_t constexpr maxDeletableTokenOfferEntries
The maximum number of offers in an offer directory for NFT to be burnable.
Definition Protocol.h:55
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecNO_ENTRY
Definition TER.h:287
@ tecNO_PERMISSION
Definition TER.h:286
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
diff --git a/NFTokenBurn__test_8cpp_source.html b/NFTokenBurn__test_8cpp_source.html index a6e86b8d80..a10ba1a4ff 100644 --- a/NFTokenBurn__test_8cpp_source.html +++ b/NFTokenBurn__test_8cpp_source.html @@ -1232,11 +1232,11 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
+
bool isArray() const
UInt size() const
Number of values in array or object.
-
std::string toStyledString() const
+
std::string toStyledString() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -1294,7 +1294,7 @@ $(document).ready(function() { init_codefold(0); });
@ tefINVARIANT_FAILED
Definition TER.h:163
std::size_t constexpr maxDeletableTokenOfferEntries
The maximum number of offers in an offer directory for NFT to be burnable.
Definition Protocol.h:55
std::size_t constexpr maxTokenURILength
The maximum length of a URI inside an NFT.
Definition Protocol.h:202
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
@ tapNONE
Definition ApplyView.h:11
@ tecINVARIANT_FAILED
Definition TER.h:294
@ tecHAS_OBLIGATIONS
Definition TER.h:298
diff --git a/NFTokenCancelOffer_8cpp_source.html b/NFTokenCancelOffer_8cpp_source.html index eeb7d52f30..91357e2784 100644 --- a/NFTokenCancelOffer_8cpp_source.html +++ b/NFTokenCancelOffer_8cpp_source.html @@ -192,8 +192,8 @@ $(document).ready(function() { init_codefold(0); });
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:97
STVector256 const & getFieldV256(SField const &field) const
Definition STObject.cpp:646
-
std::vector< uint256 >::iterator begin()
-
std::vector< uint256 >::iterator end()
+
std::vector< uint256 >::iterator begin()
+
std::vector< uint256 >::iterator end()
beast::Journal const j_
Definition Transactor.h:110
ApplyView & view()
Definition Transactor.h:128
diff --git a/NFTokenCreateOffer_8cpp_source.html b/NFTokenCreateOffer_8cpp_source.html index a99d48e3b1..71d3053d4d 100644 --- a/NFTokenCreateOffer_8cpp_source.html +++ b/NFTokenCreateOffer_8cpp_source.html @@ -195,7 +195,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:129
constexpr std::uint32_t const tfNFTokenCreateOfferMask
Definition TxFlags.h:211
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecNO_ENTRY
Definition TER.h:287
@ tecEXPIRED
Definition TER.h:295
constexpr std::uint32_t const tfSellNFToken
Definition TxFlags.h:210
diff --git a/NFTokenDir__test_8cpp_source.html b/NFTokenDir__test_8cpp_source.html index 71ce135bb0..1ca99e58cc 100644 --- a/NFTokenDir__test_8cpp_source.html +++ b/NFTokenDir__test_8cpp_source.html @@ -1612,12 +1612,12 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
Value & append(Value const &value)
Append value to array at the end.
+
bool isArray() const
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
std::string toStyledString() const
+
std::string toStyledString() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:221
diff --git a/NFTokenMint_8cpp_source.html b/NFTokenMint_8cpp_source.html index f879517abd..5df89fcd31 100644 --- a/NFTokenMint_8cpp_source.html +++ b/NFTokenMint_8cpp_source.html @@ -463,13 +463,13 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t const tfNFTokenMintMaskWithMutable
Definition TxFlags.h:206
constexpr std::uint32_t const tfNFTokenMintMask
Definition TxFlags.h:196
static bool hasOfferFields(PreflightContext const &ctx)
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
std::size_t constexpr maxTokenURILength
The maximum length of a URI inside an NFT.
Definition Protocol.h:202
constexpr std::uint32_t const tfNFTokenMintOldMaskWithMutable
Definition TxFlags.h:203
constexpr std::uint32_t const tfNFTokenMintOldMask
Definition TxFlags.h:199
@ temMALFORMED
Definition TER.h:67
@ temBAD_NFTOKEN_TRANSFER_FEE
Definition TER.h:107
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecINTERNAL
Definition TER.h:291
@ tecEXPIRED
Definition TER.h:295
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
diff --git a/NFTokenModify_8cpp_source.html b/NFTokenModify_8cpp_source.html index a2a5680d0b..1e5969e4ac 100644 --- a/NFTokenModify_8cpp_source.html +++ b/NFTokenModify_8cpp_source.html @@ -168,7 +168,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecNO_ENTRY
Definition TER.h:287
@ tecINTERNAL
Definition TER.h:291
@ tecNO_PERMISSION
Definition TER.h:286
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
ReadView const & view
Definition Transactor.h:56
diff --git a/NFTokenUtils_8cpp_source.html b/NFTokenUtils_8cpp_source.html index 39b8e275ce..f770f87f3a 100644 --- a/NFTokenUtils_8cpp_source.html +++ b/NFTokenUtils_8cpp_source.html @@ -1231,7 +1231,7 @@ $(document).ready(function() { init_codefold(0); });
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
@ tefTOO_BIG
Definition TER.h:164
@ tefNFTOKEN_IS_NOT_TRANSFERABLE
Definition TER.h:166
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
std::size_t constexpr maxDeletableTokenOfferEntries
The maximum number of offers in an offer directory for NFT to be burnable.
Definition Protocol.h:55
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition View.cpp:515
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
diff --git a/NFTokenUtils_8h_source.html b/NFTokenUtils_8h_source.html index 4c0a837d69..432924f6bd 100644 --- a/NFTokenUtils_8h_source.html +++ b/NFTokenUtils_8h_source.html @@ -223,7 +223,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
base_uint< 256 > uint256
Definition base_uint.h:526
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
@ lsfSellNFToken
diff --git a/NFToken__test_8cpp_source.html b/NFToken__test_8cpp_source.html index fab8f4c06a..729309c97b 100644 --- a/NFToken__test_8cpp_source.html +++ b/NFToken__test_8cpp_source.html @@ -7131,11 +7131,11 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
Value removeMember(char const *key)
Remove and return the named member.
+
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
A testsuite class.
Definition suite.h:51
void pass()
Record a successful test condition.
Definition suite.h:494
@@ -7246,7 +7246,7 @@ $(document).ready(function() { init_codefold(0); });
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
std::size_t constexpr maxTokenURILength
The maximum length of a URI inside an NFT.
Definition Protocol.h:202
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:99
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
constexpr std::uint32_t tfFullyCanonicalSig
Transaction flags.
Definition TxFlags.h:40
constexpr std::uint32_t const tfOnlyXRP
Definition TxFlags.h:120
@ temBAD_EXPIRATION
Definition TER.h:71
diff --git a/NetworkID__test_8cpp_source.html b/NetworkID__test_8cpp_source.html index 5eb55e282e..25002e157d 100644 --- a/NetworkID__test_8cpp_source.html +++ b/NetworkID__test_8cpp_source.html @@ -259,7 +259,7 @@ $(document).ready(function() { init_codefold(0); });
Set the fee on a JTx.
Definition fee.h:17
Set the regular signature on a JTx.
Definition sig.h:15
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ telWRONG_NETWORK
Definition TER.h:45
diff --git a/NetworkOPs_8cpp_source.html b/NetworkOPs_8cpp_source.html index b9dfe0ff98..9afc8b6b20 100644 --- a/NetworkOPs_8cpp_source.html +++ b/NetworkOPs_8cpp_source.html @@ -4729,9 +4729,9 @@ $(document).ready(function() { init_codefold(0); });
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
Json::UInt UInt
Definition json_value.h:137
-
Value & append(Value const &value)
Append value to array at the end.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
Value & append(Value const &value)
Append value to array at the end.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:318
Stream debug() const
Definition Journal.h:300
@@ -5005,7 +5005,7 @@ $(document).ready(function() { init_codefold(0); });
Issue const & issue() const
Definition STAmount.h:454
std::string getText() const override
Definition STAmount.cpp:639
void setJson(Json::Value &) const
Definition STAmount.cpp:599
- +
std::optional< T > get(std::string const &name) const
@@ -5106,7 +5106,7 @@ $(document).ready(function() { init_codefold(0); });
@ terQUEUED
Definition TER.h:205
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
-
bool isTerRetry(TER x) noexcept
Definition TER.h:643
+
bool isTerRetry(TER x) noexcept
Definition TER.h:637
@ fhZERO_IF_FROZEN
Definition View.h:58
@ fhIGNORE_FREEZE
Definition View.h:58
csprng_engine & crypto_prng()
The default cryptographically secure PRNG.
@@ -5125,7 +5125,7 @@ $(document).ready(function() { init_codefold(0); });
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
FeeSetup setup_FeeVote(Section const &section)
Definition Config.cpp:1024
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Definition View.cpp:392
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
bool transResultInfo(TER code, std::string &token, std::string &text)
Definition TER.cpp:228
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
Definition View.cpp:112
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
@@ -5149,7 +5149,7 @@ $(document).ready(function() { init_codefold(0); });
@ jtTRANSACTION
Definition Job.h:41
@ jtCLIENT_FEE_CHANGE
Definition Job.h:26
@ jtBATCH
Definition Job.h:44
-
bool isTefFailure(TER x) noexcept
Definition TER.h:637
+
bool isTefFailure(TER x) noexcept
Definition TER.h:631
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:699
auto constexpr muldiv_max
Definition mulDiv.h:8
uint256 getQualityNext(uint256 const &uBase)
Definition Indexes.cpp:119
@@ -5168,12 +5168,12 @@ $(document).ready(function() { init_codefold(0); });
@ tapNONE
Definition ApplyView.h:11
@ tapFAIL_HARD
Definition ApplyView.h:15
@ tapUNLIMITED
Definition ApplyView.h:22
-
bool isTelLocal(TER x) noexcept
Definition TER.h:625
+
bool isTelLocal(TER x) noexcept
Definition TER.h:619
@ ledgerMaster
ledger master data for signing
@ proposal
proposal for signing
@ temINVALID_FLAG
Definition TER.h:91
@ temBAD_SIGNATURE
Definition TER.h:85
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
static std::uint32_t trunc32(std::uint64_t v)
static std::array< char const *, 5 > const stateNames
void handleNewValidation(Application &app, std::shared_ptr< STValidation > const &val, std::string const &source, BypassAccept const bypassAccept, std::optional< beast::Journal > j)
Handle a new validation.
@@ -5184,7 +5184,7 @@ $(document).ready(function() { init_codefold(0); });
@ FULL
we have the ledger and can even validate
@ SYNCING
fallen slightly behind
std::shared_ptr< STTx const > sterilize(STTx const &stx)
Sterilize a transaction.
Definition STTx.cpp:767
-
bool isTemMalformed(TER x) noexcept
Definition TER.h:631
+
bool isTemMalformed(TER x) noexcept
Definition TER.h:625
@ tesSUCCESS
Definition TER.h:225
error_code_i
Definition ErrorCodes.h:20
@ rpcINTERNAL
Definition ErrorCodes.h:110
diff --git a/NoRippleCheck_8cpp_source.html b/NoRippleCheck_8cpp_source.html index 39c7e4e627..1d835ad908 100644 --- a/NoRippleCheck_8cpp_source.html +++ b/NoRippleCheck_8cpp_source.html @@ -257,12 +257,12 @@ $(document).ready(function() { init_codefold(0); });
170} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A view into a ledger.
Definition ReadView.h:31
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:721
-
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:554
+
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:556
Currency const & getCurrency() const
Definition STAmount.h:460
AccountID const & getIssuer() const
Definition STAmount.h:466
virtual LoadFeeTrack & getFeeTrack()=0
diff --git a/NoRipple__test_8cpp_source.html b/NoRipple__test_8cpp_source.html index 544d9e5ccd..3ed2412042 100644 --- a/NoRipple__test_8cpp_source.html +++ b/NoRipple__test_8cpp_source.html @@ -380,7 +380,7 @@ $(document).ready(function() { init_codefold(0); });
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
diff --git a/NodeFamily_8cpp_source.html b/NodeFamily_8cpp_source.html index 8adaaaef32..1af45830aa 100644 --- a/NodeFamily_8cpp_source.html +++ b/NodeFamily_8cpp_source.html @@ -94,108 +94,110 @@ $(document).ready(function() { init_codefold(0); });
10 : app_(app)
11 , db_(app.getNodeStore())
12 , j_(app.journal("NodeFamily"))
-
13 , fbCache_(std::make_shared<FullBelowCache>(
-
14 "Node family full below cache",
-
15 stopwatch(),
-
16 app.journal("NodeFamilyFulLBelowCache"),
-
17 cm.collector(),
- - -
20 , tnCache_(std::make_shared<TreeNodeCache>(
-
21 "Node family tree node cache",
-
22 app.config().getValueFor(SizedItem::treeCacheSize),
-
23 std::chrono::seconds(app.config().getValueFor(SizedItem::treeCacheAge)),
-
24 stopwatch(),
-
25 j_))
-
26{
-
27}
+
13 , fbCache_(
+
14 std::make_shared<FullBelowCache>(
+
15 "Node family full below cache",
+
16 stopwatch(),
+
17 app.journal("NodeFamilyFulLBelowCache"),
+
18 cm.collector(),
+ + +
21 , tnCache_(
+
22 std::make_shared<TreeNodeCache>(
+
23 "Node family tree node cache",
+
24 app.config().getValueFor(SizedItem::treeCacheSize),
+
25 std::chrono::seconds(app.config().getValueFor(SizedItem::treeCacheAge)),
+
26 stopwatch(),
+
27 j_))
+
28{
+
29}
-
28
-
29void
-
- -
31{
-
32 fbCache_->sweep();
-
33 tnCache_->sweep();
-
34}
+
30
+
31void
+
+ +
33{
+
34 fbCache_->sweep();
+
35 tnCache_->sweep();
+
36}
-
35
-
36void
-
- -
38{
-
39 {
- -
41 maxSeq_ = 0;
-
42 }
-
43
-
44 fbCache_->reset();
-
45 tnCache_->reset();
-
46}
+
37
+
38void
+
+ +
40{
+
41 {
+ +
43 maxSeq_ = 0;
+
44 }
+
45
+
46 fbCache_->reset();
+
47 tnCache_->reset();
+
48}
-
47
-
48void
-
- -
50{
-
51 JLOG(j_.error()) << "Missing node in " << seq;
- -
53 if (maxSeq_ == 0)
-
54 {
-
55 maxSeq_ = seq;
-
56
-
57 do
-
58 {
-
59 // Try to acquire the most recent missing ledger
-
60 seq = maxSeq_;
-
61
-
62 lock.unlock();
+
49
+
50void
+
+ +
52{
+
53 JLOG(j_.error()) << "Missing node in " << seq;
+ +
55 if (maxSeq_ == 0)
+
56 {
+
57 maxSeq_ = seq;
+
58
+
59 do
+
60 {
+
61 // Try to acquire the most recent missing ledger
+
62 seq = maxSeq_;
63
-
64 // This can invoke the missing node handler
- -
66
-
67 lock.lock();
-
68 } while (maxSeq_ != seq);
-
69 }
-
70 else if (maxSeq_ < seq)
-
71 {
-
72 // We found a more recent ledger with a missing node
-
73 maxSeq_ = seq;
-
74 }
-
75}
+
64 lock.unlock();
+
65
+
66 // This can invoke the missing node handler
+ +
68
+
69 lock.lock();
+
70 } while (maxSeq_ != seq);
+
71 }
+
72 else if (maxSeq_ < seq)
+
73 {
+
74 // We found a more recent ledger with a missing node
+
75 maxSeq_ = seq;
+
76 }
+
77}
-
76
-
77void
-
- -
79{
-
80 if (hash.isNonZero())
-
81 {
-
82 JLOG(j_.error()) << "Missing node in " << to_string(hash);
-
83
- -
85 }
-
86}
+
78
+
79void
+
+ +
81{
+
82 if (hash.isNonZero())
+
83 {
+
84 JLOG(j_.error()) << "Missing node in " << to_string(hash);
+
85
+ +
87 }
+
88}
-
87
-
88} // namespace xrpl
+
89
+
90} // namespace xrpl
Stream error() const
Definition Journal.h:318
Provides the beast::insight::Collector service.
virtual std::shared_ptr< Ledger const > acquire(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason)=0
uint256 getHashBySeq(std::uint32_t index)
Get a ledger's hash by sequence number using the cache.
-
void reset() override
+
void reset() override
std::shared_ptr< TreeNodeCache > tnCache_
Definition NodeFamily.h:75
Application & app_
Definition NodeFamily.h:70
-
void acquire(uint256 const &hash, std::uint32_t seq)
-
void sweep() override
+
void acquire(uint256 const &hash, std::uint32_t seq)
+
void sweep() override
std::mutex maxSeqMutex_
Definition NodeFamily.h:79
NodeFamily()=delete
std::shared_ptr< FullBelowCache > fbCache_
Definition NodeFamily.h:74
beast::Journal const j_
Definition NodeFamily.h:72
LedgerIndex maxSeq_
Definition NodeFamily.h:78
-
void missingNodeAcquireBySeq(std::uint32_t seq, uint256 const &hash) override
Acquire ledger that has a missing node by ledger sequence.
+
void missingNodeAcquireBySeq(std::uint32_t seq, uint256 const &hash) override
Acquire ledger that has a missing node by ledger sequence.
virtual InboundLedgers & getInboundLedgers()=0
virtual LedgerMaster & getLedgerMaster()=0
Map/cache combination.
Definition TaggedCache.h:42
diff --git a/NodeFamily_8h_source.html b/NodeFamily_8h_source.html index 0c395cf176..c136f039b6 100644 --- a/NodeFamily_8h_source.html +++ b/NodeFamily_8h_source.html @@ -188,16 +188,16 @@ $(document).ready(function() { init_codefold(0); });
beast::Journal const & journal() override
Definition NodeFamily.h:37
std::shared_ptr< FullBelowCache > getFullBelowCache() override
Return a pointer to the Family Full Below Cache.
Definition NodeFamily.h:43
NodeFamily & operator=(NodeFamily &&)=delete
-
void reset() override
+
void reset() override
std::shared_ptr< TreeNodeCache > tnCache_
Definition NodeFamily.h:75
Application & app_
Definition NodeFamily.h:70
NodeFamily(NodeFamily const &)=delete
NodeFamily(NodeFamily &&)=delete
std::shared_ptr< TreeNodeCache > getTreeNodeCache() override
Return a pointer to the Family Tree Node Cache.
Definition NodeFamily.h:49
NodeStore::Database & db_
Definition NodeFamily.h:71
-
void acquire(uint256 const &hash, std::uint32_t seq)
+
void acquire(uint256 const &hash, std::uint32_t seq)
NodeFamily & operator=(NodeFamily const &)=delete
-
void sweep() override
+
void sweep() override
void missingNodeAcquireByHash(uint256 const &hash, std::uint32_t seq) override
Acquire ledger that has a missing node by ledger hash.
Definition NodeFamily.h:64
NodeStore::Database & db() override
Definition NodeFamily.h:25
std::mutex maxSeqMutex_
Definition NodeFamily.h:79
@@ -206,7 +206,7 @@ $(document).ready(function() { init_codefold(0); });
NodeStore::Database const & db() const override
Definition NodeFamily.h:31
beast::Journal const j_
Definition NodeFamily.h:72
LedgerIndex maxSeq_
Definition NodeFamily.h:78
-
void missingNodeAcquireBySeq(std::uint32_t seq, uint256 const &hash) override
Acquire ledger that has a missing node by ledger sequence.
+
void missingNodeAcquireBySeq(std::uint32_t seq, uint256 const &hash) override
Acquire ledger that has a missing node by ledger sequence.
Persistency layer for NodeObject.
Definition Database.h:31
diff --git a/Node_8cpp_source.html b/Node_8cpp_source.html index b0d1e491c6..59c1c59e9c 100644 --- a/Node_8cpp_source.html +++ b/Node_8cpp_source.html @@ -660,609 +660,612 @@ $(document).ready(function() { init_codefold(0); });
555getTxHistory(soci::session& session, Application& app, LedgerIndex startIndex, int quantity)
556{
557 std::string sql = boost::str(
-
558 boost::format("SELECT LedgerSeq, Status, RawTxn "
-
559 "FROM Transactions ORDER BY LedgerSeq DESC LIMIT %u,%u;") %
-
560 startIndex % quantity);
-
561
- -
563 int total = 0;
-
564
-
565 {
-
566 // SOCI requires boost::optional (not std::optional) as parameters.
-
567 boost::optional<std::uint64_t> ledgerSeq;
-
568 boost::optional<std::string> status;
-
569 soci::blob sociRawTxnBlob(session);
-
570 soci::indicator rti;
-
571 Blob rawTxn;
-
572
-
573 soci::statement st =
-
574 (session.prepare << sql, soci::into(ledgerSeq), soci::into(status), soci::into(sociRawTxnBlob, rti));
-
575
-
576 st.execute();
-
577 while (st.fetch())
-
578 {
-
579 if (soci::i_ok == rti)
-
580 convert(sociRawTxnBlob, rawTxn);
-
581 else
-
582 rawTxn.clear();
-
583
-
584 if (auto trans = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app))
-
585 {
-
586 total++;
-
587 txs.push_back(trans);
-
588 }
+
558 boost::format(
+
559 "SELECT LedgerSeq, Status, RawTxn "
+
560 "FROM Transactions ORDER BY LedgerSeq DESC LIMIT %u,%u;") %
+
561 startIndex % quantity);
+
562
+ +
564 int total = 0;
+
565
+
566 {
+
567 // SOCI requires boost::optional (not std::optional) as parameters.
+
568 boost::optional<std::uint64_t> ledgerSeq;
+
569 boost::optional<std::string> status;
+
570 soci::blob sociRawTxnBlob(session);
+
571 soci::indicator rti;
+
572 Blob rawTxn;
+
573
+
574 soci::statement st =
+
575 (session.prepare << sql, soci::into(ledgerSeq), soci::into(status), soci::into(sociRawTxnBlob, rti));
+
576
+
577 st.execute();
+
578 while (st.fetch())
+
579 {
+
580 if (soci::i_ok == rti)
+
581 convert(sociRawTxnBlob, rawTxn);
+
582 else
+
583 rawTxn.clear();
+
584
+
585 if (auto trans = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app))
+
586 {
+
587 total++;
+
588 txs.push_back(trans);
+
589 }
-
589 }
-
590 }
-
591
-
592 return {txs, total};
-
593}
-
594
-
612static std::string
- -
614 Application& app,
-
615 std::string selection,
-
616 RelationalDatabase::AccountTxOptions const& options,
-
617 bool descending,
-
618 bool binary,
-
619 bool count,
- -
621{
-
622 constexpr std::uint32_t NONBINARY_PAGE_LENGTH = 200;
-
623 constexpr std::uint32_t BINARY_PAGE_LENGTH = 500;
-
624
-
625 std::uint32_t numberOfResults;
-
626
-
627 if (count)
-
628 {
-
629 numberOfResults = std::numeric_limits<std::uint32_t>::max();
-
630 }
-
631 else if (options.limit == UINT32_MAX)
-
632 {
-
633 numberOfResults = binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH;
-
634 }
-
635 else if (!options.bUnlimited)
-
636 {
-
637 numberOfResults = std::min(binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH, options.limit);
-
638 }
-
639 else
-
640 {
-
641 numberOfResults = options.limit;
-
642 }
-
643
-
644 std::string maxClause = "";
-
645 std::string minClause = "";
-
646
-
647 if (options.maxLedger)
-
648 {
-
649 maxClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq <= '%u'") % options.maxLedger);
-
650 }
-
651
-
652 if (options.minLedger)
-
653 {
-
654 minClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq >= '%u'") % options.minLedger);
-
655 }
-
656
-
657 std::string sql;
-
658
-
659 if (count)
-
660 sql = boost::str(
-
661 boost::format("SELECT %s FROM AccountTransactions "
-
662 "WHERE Account = '%s' %s %s LIMIT %u, %u;") %
-
663 selection % toBase58(options.account) % maxClause % minClause % options.offset % numberOfResults);
-
664 else
-
665 sql = boost::str(
-
666 boost::format("SELECT %s FROM "
-
667 "AccountTransactions INNER JOIN Transactions "
-
668 "ON Transactions.TransID = AccountTransactions.TransID "
-
669 "WHERE Account = '%s' %s %s "
-
670 "ORDER BY AccountTransactions.LedgerSeq %s, "
-
671 "AccountTransactions.TxnSeq %s, AccountTransactions.TransID %s "
-
672 "LIMIT %u, %u;") %
-
673 selection % toBase58(options.account) % maxClause % minClause % (descending ? "DESC" : "ASC") %
-
674 (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % options.offset % numberOfResults);
-
675 JLOG(j.trace()) << "txSQL query: " << sql;
-
676 return sql;
-
677}
-
678
- - -
702 soci::session& session,
-
703 Application& app,
-
704 LedgerMaster& ledgerMaster,
-
705 RelationalDatabase::AccountTxOptions const& options,
-
706 bool descending,
- -
708{
- -
710
- -
712 app, "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", options, descending, false, false, j);
-
713 if (sql == "")
-
714 return {ret, 0};
-
715
-
716 int total = 0;
-
717 {
-
718 // SOCI requires boost::optional (not std::optional) as parameters.
-
719 boost::optional<std::uint64_t> ledgerSeq;
-
720 boost::optional<std::string> status;
-
721 soci::blob sociTxnBlob(session), sociTxnMetaBlob(session);
-
722 soci::indicator rti, tmi;
-
723 Blob rawTxn, txnMeta;
-
724
-
725 soci::statement st =
-
726 (session.prepare << sql,
-
727 soci::into(ledgerSeq),
-
728 soci::into(status),
-
729 soci::into(sociTxnBlob, rti),
-
730 soci::into(sociTxnMetaBlob, tmi));
-
731
-
732 st.execute();
-
733 while (st.fetch())
-
734 {
-
735 if (soci::i_ok == rti)
-
736 convert(sociTxnBlob, rawTxn);
-
737 else
-
738 rawTxn.clear();
-
739
-
740 if (soci::i_ok == tmi)
-
741 convert(sociTxnMetaBlob, txnMeta);
-
742 else
-
743 txnMeta.clear();
-
744
-
745 auto txn = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app);
-
746
-
747 if (txnMeta.empty())
-
748 { // Work around a bug that could leave the metadata missing
-
749 auto const seq = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0));
-
750
-
751 JLOG(j.warn()) << "Recovering ledger " << seq << ", txn " << txn->getID();
-
752
-
753 if (auto l = ledgerMaster.getLedgerBySeq(seq))
-
754 pendSaveValidated(app, l, false, false);
-
755 }
-
756
-
757 if (txn)
-
758 {
-
759 ret.emplace_back(txn, std::make_shared<TxMeta>(txn->getID(), txn->getLedger(), txnMeta));
-
760 total++;
-
761 }
-
762 }
-
763 }
-
- -
765 return {ret, total};
-
766}
-
767
- - -
770 soci::session& session,
-
771 Application& app,
-
772 LedgerMaster& ledgerMaster,
+
590 }
+
591 }
+
592
+
593 return {txs, total};
+
594}
+
595
+
613static std::string
+ +
615 Application& app,
+
616 std::string selection,
+
617 RelationalDatabase::AccountTxOptions const& options,
+
618 bool descending,
+
619 bool binary,
+
620 bool count,
+ +
622{
+
623 constexpr std::uint32_t NONBINARY_PAGE_LENGTH = 200;
+
624 constexpr std::uint32_t BINARY_PAGE_LENGTH = 500;
+
625
+
626 std::uint32_t numberOfResults;
+
627
+
628 if (count)
+
629 {
+
630 numberOfResults = std::numeric_limits<std::uint32_t>::max();
+
631 }
+
632 else if (options.limit == UINT32_MAX)
+
633 {
+
634 numberOfResults = binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH;
+
635 }
+
636 else if (!options.bUnlimited)
+
637 {
+
638 numberOfResults = std::min(binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH, options.limit);
+
639 }
+
640 else
+
641 {
+
642 numberOfResults = options.limit;
+
643 }
+
644
+
645 std::string maxClause = "";
+
646 std::string minClause = "";
+
647
+
648 if (options.maxLedger)
+
649 {
+
650 maxClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq <= '%u'") % options.maxLedger);
+
651 }
+
652
+
653 if (options.minLedger)
+
654 {
+
655 minClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq >= '%u'") % options.minLedger);
+
656 }
+
657
+
658 std::string sql;
+
659
+
660 if (count)
+
661 sql = boost::str(
+
662 boost::format(
+
663 "SELECT %s FROM AccountTransactions "
+
664 "WHERE Account = '%s' %s %s LIMIT %u, %u;") %
+
665 selection % toBase58(options.account) % maxClause % minClause % options.offset % numberOfResults);
+
666 else
+
667 sql = boost::str(
+
668 boost::format(
+
669 "SELECT %s FROM "
+
670 "AccountTransactions INNER JOIN Transactions "
+
671 "ON Transactions.TransID = AccountTransactions.TransID "
+
672 "WHERE Account = '%s' %s %s "
+
673 "ORDER BY AccountTransactions.LedgerSeq %s, "
+
674 "AccountTransactions.TxnSeq %s, AccountTransactions.TransID %s "
+
675 "LIMIT %u, %u;") %
+
676 selection % toBase58(options.account) % maxClause % minClause % (descending ? "DESC" : "ASC") %
+
677 (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % options.offset % numberOfResults);
+
678 JLOG(j.trace()) << "txSQL query: " << sql;
+
679 return sql;
+
680}
+
681
+ + +
705 soci::session& session,
+
706 Application& app,
+
707 LedgerMaster& ledgerMaster,
+
708 RelationalDatabase::AccountTxOptions const& options,
+
709 bool descending,
+ +
711{
+ +
713
+ +
715 app, "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", options, descending, false, false, j);
+
716 if (sql == "")
+
717 return {ret, 0};
+
718
+
719 int total = 0;
+
720 {
+
721 // SOCI requires boost::optional (not std::optional) as parameters.
+
722 boost::optional<std::uint64_t> ledgerSeq;
+
723 boost::optional<std::string> status;
+
724 soci::blob sociTxnBlob(session), sociTxnMetaBlob(session);
+
725 soci::indicator rti, tmi;
+
726 Blob rawTxn, txnMeta;
+
727
+
728 soci::statement st =
+
729 (session.prepare << sql,
+
730 soci::into(ledgerSeq),
+
731 soci::into(status),
+
732 soci::into(sociTxnBlob, rti),
+
733 soci::into(sociTxnMetaBlob, tmi));
+
734
+
735 st.execute();
+
736 while (st.fetch())
+
737 {
+
738 if (soci::i_ok == rti)
+
739 convert(sociTxnBlob, rawTxn);
+
740 else
+
741 rawTxn.clear();
+
742
+
743 if (soci::i_ok == tmi)
+
744 convert(sociTxnMetaBlob, txnMeta);
+
745 else
+
746 txnMeta.clear();
+
747
+
748 auto txn = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app);
+
749
+
750 if (txnMeta.empty())
+
751 { // Work around a bug that could leave the metadata missing
+
752 auto const seq = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0));
+
753
+
754 JLOG(j.warn()) << "Recovering ledger " << seq << ", txn " << txn->getID();
+
755
+
756 if (auto l = ledgerMaster.getLedgerBySeq(seq))
+
757 pendSaveValidated(app, l, false, false);
+
758 }
+
759
+
760 if (txn)
+
761 {
+
762 ret.emplace_back(txn, std::make_shared<TxMeta>(txn->getID(), txn->getLedger(), txnMeta));
+
763 total++;
+
764 }
+
765 }
+
766 }
+
+ +
768 return {ret, total};
+
769}
+
770
+ + +
773 soci::session& session,
+
774 Application& app,
+
775 LedgerMaster& ledgerMaster,
-
773 RelationalDatabase::AccountTxOptions const& options,
- -
- -
776 return getAccountTxs(session, app, ledgerMaster, options, false, j);
-
777}
-
778
- - -
781 soci::session& session,
-
782 Application& app,
- +
776 RelationalDatabase::AccountTxOptions const& options,
+ +
+ +
779 return getAccountTxs(session, app, ledgerMaster, options, false, j);
+
780}
+
781
+ + +
784 soci::session& session,
+
785 Application& app,
+
- - -
786{
-
787 return getAccountTxs(session, app, ledgerMaster, options, true, j);
-
788}
-
789
- - -
812 soci::session& session,
-
813 Application& app,
-
814 RelationalDatabase::AccountTxOptions const& options,
-
815 bool descending,
- -
817{
- -
819
- -
821 app, "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", options, descending, true /*binary*/, false, j);
-
822 if (sql == "")
-
823 return {ret, 0};
-
824
-
825 int total = 0;
-
826
-
827 {
-
828 // SOCI requires boost::optional (not std::optional) as parameters.
-
829 boost::optional<std::uint64_t> ledgerSeq;
-
830 boost::optional<std::string> status;
-
831 soci::blob sociTxnBlob(session), sociTxnMetaBlob(session);
-
832 soci::indicator rti, tmi;
-
833
-
834 soci::statement st =
-
835 (session.prepare << sql,
-
836 soci::into(ledgerSeq),
-
837 soci::into(status),
-
838 soci::into(sociTxnBlob, rti),
-
839 soci::into(sociTxnMetaBlob, tmi));
-
840
-
841 st.execute();
-
842 while (st.fetch())
-
843 {
-
844 Blob rawTxn;
-
845 if (soci::i_ok == rti)
-
846 convert(sociTxnBlob, rawTxn);
-
847 Blob txnMeta;
-
848 if (soci::i_ok == tmi)
-
849 convert(sociTxnMetaBlob, txnMeta);
-
850
-
851 auto const seq = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0));
-
852
-
853 ret.emplace_back(std::move(rawTxn), std::move(txnMeta), seq);
-
854 total++;
-
855 }
-
856 }
-
- -
858 return {ret, total};
-
859}
-
860
- - -
863 soci::session& session,
-
864 Application& app,
+ + +
789{
+
790 return getAccountTxs(session, app, ledgerMaster, options, true, j);
+
791}
+
792
+ + +
815 soci::session& session,
+
816 Application& app,
+
817 RelationalDatabase::AccountTxOptions const& options,
+
818 bool descending,
+ +
820{
+ +
822
+ +
824 app, "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", options, descending, true /*binary*/, false, j);
+
825 if (sql == "")
+
826 return {ret, 0};
+
827
+
828 int total = 0;
+
829
+
830 {
+
831 // SOCI requires boost::optional (not std::optional) as parameters.
+
832 boost::optional<std::uint64_t> ledgerSeq;
+
833 boost::optional<std::string> status;
+
834 soci::blob sociTxnBlob(session), sociTxnMetaBlob(session);
+
835 soci::indicator rti, tmi;
+
836
+
837 soci::statement st =
+
838 (session.prepare << sql,
+
839 soci::into(ledgerSeq),
+
840 soci::into(status),
+
841 soci::into(sociTxnBlob, rti),
+
842 soci::into(sociTxnMetaBlob, tmi));
+
843
+
844 st.execute();
+
845 while (st.fetch())
+
846 {
+
847 Blob rawTxn;
+
848 if (soci::i_ok == rti)
+
849 convert(sociTxnBlob, rawTxn);
+
850 Blob txnMeta;
+
851 if (soci::i_ok == tmi)
+
852 convert(sociTxnMetaBlob, txnMeta);
+
853
+
854 auto const seq = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0));
+
855
+
856 ret.emplace_back(std::move(rawTxn), std::move(txnMeta), seq);
+
857 total++;
+
858 }
+
859 }
+
+ +
861 return {ret, total};
+
862}
+
863
+ + +
866 soci::session& session,
+
867 Application& app,
-
865 RelationalDatabase::AccountTxOptions const& options,
- -
- -
868 return getAccountTxsB(session, app, options, false, j);
-
869}
-
870
- - -
873 soci::session& session,
-
874 Application& app,
+
868 RelationalDatabase::AccountTxOptions const& options,
+ +
+ +
871 return getAccountTxsB(session, app, options, false, j);
+
872}
+
873
+ + +
876 soci::session& session,
+
877 Application& app,
- - -
877{
-
878 return getAccountTxsB(session, app, options, true, j);
-
879}
-
880
- - -
902 soci::session& session,
-
903 std::function<void(std::uint32_t)> const& onUnsavedLedger,
-
904 std::function<void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& onTransaction,
-
905 RelationalDatabase::AccountTxPageOptions const& options,
-
906 std::uint32_t page_length,
-
907 bool forward)
-
908{
-
909 int total = 0;
-
910
-
911 bool lookingForMarker = options.marker.has_value();
-
912
-
913 std::uint32_t numberOfResults;
-
914
-
915 if (options.limit == 0 || options.limit == UINT32_MAX || (options.limit > page_length && !options.bAdmin))
-
916 numberOfResults = page_length;
-
917 else
-
918 numberOfResults = options.limit;
-
919
-
920 // As an account can have many thousands of transactions, there is a limit
-
921 // placed on the amount of transactions returned. If the limit is reached
-
922 // before the result set has been exhausted (we always query for one more
-
923 // than the limit), then we return an opaque marker that can be supplied in
-
924 // a subsequent query.
-
925 std::uint32_t queryLimit = numberOfResults + 1;
-
926 std::uint32_t findLedger = 0, findSeq = 0;
-
927
-
928 if (lookingForMarker)
-
929 {
-
930 findLedger = options.marker->ledgerSeq;
-
931 findSeq = options.marker->txnSeq;
-
932 }
-
933
- -
935
-
936 static std::string const prefix(
-
937 R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
-
938 Status,RawTxn,TxnMeta
-
939 FROM AccountTransactions INNER JOIN Transactions
-
940 ON Transactions.TransID = AccountTransactions.TransID
-
941 AND AccountTransactions.Account = '%s' WHERE
-
942 )");
-
943
-
944 std::string sql;
-
945
-
946 // SQL's BETWEEN uses a closed interval ([a,b])
-
947
-
948 char const* const order = forward ? "ASC" : "DESC";
-
949
-
950 if (findLedger == 0)
-
951 {
-
952 sql = boost::str(
-
953 boost::format(prefix + (R"(AccountTransactions.LedgerSeq BETWEEN %u AND %u
-
954 ORDER BY AccountTransactions.LedgerSeq %s,
-
955 AccountTransactions.TxnSeq %s
-
956 LIMIT %u;)")) %
-
957 toBase58(options.account) % options.minLedger % options.maxLedger % order % order % queryLimit);
-
958 }
-
959 else
-
960 {
-
961 char const* const compare = forward ? ">=" : "<=";
-
962 std::uint32_t const minLedger = forward ? findLedger + 1 : options.minLedger;
-
963 std::uint32_t const maxLedger = forward ? options.maxLedger : findLedger - 1;
-
964
-
965 auto b58acct = toBase58(options.account);
-
966 sql = boost::str(
-
967 boost::format((
-
968 R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
-
969 Status,RawTxn,TxnMeta
-
970 FROM AccountTransactions, Transactions WHERE
-
971 (AccountTransactions.TransID = Transactions.TransID AND
-
972 AccountTransactions.Account = '%s' AND
-
973 AccountTransactions.LedgerSeq BETWEEN %u AND %u)
-
974 UNION
-
975 SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,Status,RawTxn,TxnMeta
-
976 FROM AccountTransactions, Transactions WHERE
-
977 (AccountTransactions.TransID = Transactions.TransID AND
-
978 AccountTransactions.Account = '%s' AND
-
979 AccountTransactions.LedgerSeq = %u AND
-
980 AccountTransactions.TxnSeq %s %u)
-
981 ORDER BY AccountTransactions.LedgerSeq %s,
-
982 AccountTransactions.TxnSeq %s
-
983 LIMIT %u;
-
984 )")) %
-
985 b58acct % minLedger % maxLedger % b58acct % findLedger % compare % findSeq % order % order % queryLimit);
-
986 }
-
987
-
988 {
-
989 Blob rawData;
-
990 Blob rawMeta;
-
991
-
992 // SOCI requires boost::optional (not std::optional) as parameters.
-
993 boost::optional<std::uint64_t> ledgerSeq;
-
994 boost::optional<std::uint32_t> txnSeq;
-
995 boost::optional<std::string> status;
-
996 soci::blob txnData(session);
-
997 soci::blob txnMeta(session);
-
998 soci::indicator dataPresent, metaPresent;
-
999
-
1000 soci::statement st =
-
1001 (session.prepare << sql,
-
1002 soci::into(ledgerSeq),
-
1003 soci::into(txnSeq),
-
1004 soci::into(status),
-
1005 soci::into(txnData, dataPresent),
-
1006 soci::into(txnMeta, metaPresent));
-
1007
-
1008 st.execute();
-
1009
-
1010 while (st.fetch())
-
1011 {
-
1012 if (lookingForMarker)
-
1013 {
-
1014 if (findLedger == ledgerSeq.value_or(0) && findSeq == txnSeq.value_or(0))
-
1015 {
-
1016 lookingForMarker = false;
-
1017 }
-
1018 else
-
1019 continue;
-
1020 }
-
1021 else if (numberOfResults == 0)
-
1022 {
-
1023 newmarker = {rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)), txnSeq.value_or(0)};
-
1024 break;
-
1025 }
-
1026
-
1027 if (dataPresent == soci::i_ok)
-
1028 convert(txnData, rawData);
-
1029 else
-
1030 rawData.clear();
-
1031
-
1032 if (metaPresent == soci::i_ok)
-
1033 convert(txnMeta, rawMeta);
-
1034 else
-
1035 rawMeta.clear();
-
1036
-
1037 // Work around a bug that could leave the metadata missing
-
1038 if (rawMeta.size() == 0)
-
-
1039 onUnsavedLedger(ledgerSeq.value_or(0));
-
1040
-
1041 // `rawData` and `rawMeta` will be used after they are moved.
-
1042 // That's OK.
-
1043 onTransaction(
-
1044 rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
-
1045 *status,
-
1046 std::move(rawData),
-
1047 std::move(rawMeta));
+ + +
880{
+
881 return getAccountTxsB(session, app, options, true, j);
+
882}
+
883
+ + +
905 soci::session& session,
+
906 std::function<void(std::uint32_t)> const& onUnsavedLedger,
+
907 std::function<void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& onTransaction,
+
908 RelationalDatabase::AccountTxPageOptions const& options,
+
909 std::uint32_t page_length,
+
910 bool forward)
+
911{
+
912 int total = 0;
+
913
+
914 bool lookingForMarker = options.marker.has_value();
+
915
+
916 std::uint32_t numberOfResults;
+
917
+
918 if (options.limit == 0 || options.limit == UINT32_MAX || (options.limit > page_length && !options.bAdmin))
+
919 numberOfResults = page_length;
+
920 else
+
921 numberOfResults = options.limit;
+
922
+
923 // As an account can have many thousands of transactions, there is a limit
+
924 // placed on the amount of transactions returned. If the limit is reached
+
925 // before the result set has been exhausted (we always query for one more
+
926 // than the limit), then we return an opaque marker that can be supplied in
+
927 // a subsequent query.
+
928 std::uint32_t queryLimit = numberOfResults + 1;
+
929 std::uint32_t findLedger = 0, findSeq = 0;
+
930
+
931 if (lookingForMarker)
+
932 {
+
933 findLedger = options.marker->ledgerSeq;
+
934 findSeq = options.marker->txnSeq;
+
935 }
+
936
+ +
938
+
939 static std::string const prefix(
+
940 R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
+
941 Status,RawTxn,TxnMeta
+
942 FROM AccountTransactions INNER JOIN Transactions
+
943 ON Transactions.TransID = AccountTransactions.TransID
+
944 AND AccountTransactions.Account = '%s' WHERE
+
945 )");
+
946
+
947 std::string sql;
+
948
+
949 // SQL's BETWEEN uses a closed interval ([a,b])
+
950
+
951 char const* const order = forward ? "ASC" : "DESC";
+
952
+
953 if (findLedger == 0)
+
954 {
+
955 sql = boost::str(
+
956 boost::format(prefix + (R"(AccountTransactions.LedgerSeq BETWEEN %u AND %u
+
957 ORDER BY AccountTransactions.LedgerSeq %s,
+
958 AccountTransactions.TxnSeq %s
+
959 LIMIT %u;)")) %
+
960 toBase58(options.account) % options.minLedger % options.maxLedger % order % order % queryLimit);
+
961 }
+
962 else
+
963 {
+
964 char const* const compare = forward ? ">=" : "<=";
+
965 std::uint32_t const minLedger = forward ? findLedger + 1 : options.minLedger;
+
966 std::uint32_t const maxLedger = forward ? options.maxLedger : findLedger - 1;
+
967
+
968 auto b58acct = toBase58(options.account);
+
969 sql = boost::str(
+
970 boost::format((
+
971 R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
+
972 Status,RawTxn,TxnMeta
+
973 FROM AccountTransactions, Transactions WHERE
+
974 (AccountTransactions.TransID = Transactions.TransID AND
+
975 AccountTransactions.Account = '%s' AND
+
976 AccountTransactions.LedgerSeq BETWEEN %u AND %u)
+
977 UNION
+
978 SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,Status,RawTxn,TxnMeta
+
979 FROM AccountTransactions, Transactions WHERE
+
980 (AccountTransactions.TransID = Transactions.TransID AND
+
981 AccountTransactions.Account = '%s' AND
+
982 AccountTransactions.LedgerSeq = %u AND
+
983 AccountTransactions.TxnSeq %s %u)
+
984 ORDER BY AccountTransactions.LedgerSeq %s,
+
985 AccountTransactions.TxnSeq %s
+
986 LIMIT %u;
+
987 )")) %
+
988 b58acct % minLedger % maxLedger % b58acct % findLedger % compare % findSeq % order % order % queryLimit);
+
989 }
+
990
+
991 {
+
992 Blob rawData;
+
993 Blob rawMeta;
+
994
+
995 // SOCI requires boost::optional (not std::optional) as parameters.
+
996 boost::optional<std::uint64_t> ledgerSeq;
+
997 boost::optional<std::uint32_t> txnSeq;
+
998 boost::optional<std::string> status;
+
999 soci::blob txnData(session);
+
1000 soci::blob txnMeta(session);
+
1001 soci::indicator dataPresent, metaPresent;
+
1002
+
1003 soci::statement st =
+
1004 (session.prepare << sql,
+
1005 soci::into(ledgerSeq),
+
1006 soci::into(txnSeq),
+
1007 soci::into(status),
+
1008 soci::into(txnData, dataPresent),
+
1009 soci::into(txnMeta, metaPresent));
+
1010
+
1011 st.execute();
+
1012
+
1013 while (st.fetch())
+
1014 {
+
1015 if (lookingForMarker)
+
1016 {
+
1017 if (findLedger == ledgerSeq.value_or(0) && findSeq == txnSeq.value_or(0))
+
1018 {
+
1019 lookingForMarker = false;
+
1020 }
+
1021 else
+
1022 continue;
+
1023 }
+
1024 else if (numberOfResults == 0)
+
1025 {
+
1026 newmarker = {rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)), txnSeq.value_or(0)};
+
1027 break;
+
1028 }
+
1029
+
1030 if (dataPresent == soci::i_ok)
+
1031 convert(txnData, rawData);
+
1032 else
+
1033 rawData.clear();
+
1034
+
1035 if (metaPresent == soci::i_ok)
+
1036 convert(txnMeta, rawMeta);
+
1037 else
+
1038 rawMeta.clear();
+
1039
+
1040 // Work around a bug that could leave the metadata missing
+
1041 if (rawMeta.size() == 0)
+
+
1042 onUnsavedLedger(ledgerSeq.value_or(0));
+
1043
+
1044 // `rawData` and `rawMeta` will be used after they are moved.
+
1045 // That's OK.
+
1046 onTransaction(
+
1047 rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
+
1048 *status,
+
1049 std::move(rawData),
+
1050 std::move(rawMeta));
-
1048 // Note some callbacks will move the data, some will not. Clear
-
1049 // them so code doesn't depend on if the data was actually moved
-
-
1050 // or not. The code will be more efficient if `rawData` and
-
1051 // `rawMeta` don't have to allocate in `convert`, so don't
-
1052 // refactor my moving these variables into loop scope.
-
1053 rawData.clear();
-
1054 rawMeta.clear();
-
1055
-
1056 --numberOfResults;
-
1057 total++;
-
1058 }
+
1051 // Note some callbacks will move the data, some will not. Clear
+
1052 // them so code doesn't depend on if the data was actually moved
+
+
1053 // or not. The code will be more efficient if `rawData` and
+
1054 // `rawMeta` don't have to allocate in `convert`, so don't
+
1055 // refactor my moving these variables into loop scope.
+
1056 rawData.clear();
+
1057 rawMeta.clear();
+
1058
+
1059 --numberOfResults;
+
1060 total++;
+
1061 }
-
1059 }
-
1060
-
-
1061 return {newmarker, total};
-
1062}
+
1062 }
1063
- - -
1066 soci::session& session,
-
1067 std::function<void(std::uint32_t)> const& onUnsavedLedger,
-
1068 std::function<void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& onTransaction,
-
1069 RelationalDatabase::AccountTxPageOptions const& options,
-
1070 std::uint32_t page_length)
-
1071{
-
1072 return accountTxPage(session, onUnsavedLedger, onTransaction, options, page_length, true);
-
1073}
-
1074
- - -
1077 soci::session& session,
-
1078 std::function<void(std::uint32_t)> const& onUnsavedLedger,
-
1079 std::function<void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& onTransaction,
-
1080 RelationalDatabase::AccountTxPageOptions const& options,
-
1081 std::uint32_t page_length)
-
1082{
-
1083 return accountTxPage(session, onUnsavedLedger, onTransaction, options, page_length, false);
-
1084}
-
1085
- - -
1088 soci::session& session,
-
1089 Application& app,
-
1090 uint256 const& id,
-
1091 std::optional<ClosedInterval<uint32_t>> const& range,
-
1092 error_code_i& ec)
-
1093{
-
1094 std::string sql =
-
1095 "SELECT LedgerSeq,Status,RawTxn,TxnMeta "
-
1096 "FROM Transactions WHERE TransID='";
-
1097
-
1098 sql.append(to_string(id));
-
1099 sql.append("';");
+
+
1064 return {newmarker, total};
+
1065}
+
1066
+ + +
1069 soci::session& session,
+
1070 std::function<void(std::uint32_t)> const& onUnsavedLedger,
+
1071 std::function<void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& onTransaction,
+
1072 RelationalDatabase::AccountTxPageOptions const& options,
+
1073 std::uint32_t page_length)
+
1074{
+
1075 return accountTxPage(session, onUnsavedLedger, onTransaction, options, page_length, true);
+
1076}
+
1077
+ + +
1080 soci::session& session,
+
1081 std::function<void(std::uint32_t)> const& onUnsavedLedger,
+
1082 std::function<void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& onTransaction,
+
1083 RelationalDatabase::AccountTxPageOptions const& options,
+
1084 std::uint32_t page_length)
+
1085{
+
1086 return accountTxPage(session, onUnsavedLedger, onTransaction, options, page_length, false);
+
1087}
+
1088
+ + +
1091 soci::session& session,
+
1092 Application& app,
+
1093 uint256 const& id,
+
1094 std::optional<ClosedInterval<uint32_t>> const& range,
+
1095 error_code_i& ec)
+
1096{
+
1097 std::string sql =
+
1098 "SELECT LedgerSeq,Status,RawTxn,TxnMeta "
+
1099 "FROM Transactions WHERE TransID='";
1100
-
1101 // SOCI requires boost::optional (not std::optional) as parameters.
-
1102 boost::optional<std::uint64_t> ledgerSeq;
-
1103 boost::optional<std::string> status;
-
1104 Blob rawTxn, rawMeta;
-
1105 {
-
1106 soci::blob sociRawTxnBlob(session), sociRawMetaBlob(session);
-
1107 soci::indicator txn, meta;
-
1108
-
1109 session << sql, soci::into(ledgerSeq), soci::into(status), soci::into(sociRawTxnBlob, txn),
-
1110 soci::into(sociRawMetaBlob, meta);
+
1101 sql.append(to_string(id));
+
1102 sql.append("';");
+
1103
+
1104 // SOCI requires boost::optional (not std::optional) as parameters.
+
1105 boost::optional<std::uint64_t> ledgerSeq;
+
1106 boost::optional<std::string> status;
+
1107 Blob rawTxn, rawMeta;
+
1108 {
+
1109 soci::blob sociRawTxnBlob(session), sociRawMetaBlob(session);
+
1110 soci::indicator txn, meta;
1111
-
1112 auto const got_data = session.got_data();
-
1113
-
1114 if ((!got_data || txn != soci::i_ok || meta != soci::i_ok) && !range)
-
1115 return TxSearched::unknown;
+
1112 session << sql, soci::into(ledgerSeq), soci::into(status), soci::into(sociRawTxnBlob, txn),
+
1113 soci::into(sociRawMetaBlob, meta);
+
1114
+
1115 auto const got_data = session.got_data();
1116
-
1117 if (!got_data)
-
1118 {
-
1119 uint64_t count = 0;
-
1120 soci::indicator rti;
-
1121
-
1122 session << "SELECT COUNT(DISTINCT LedgerSeq) FROM Transactions WHERE "
-
1123 "LedgerSeq BETWEEN "
-
1124 << range->first() << " AND " << range->last() << ";",
-
1125 soci::into(count, rti);
-
1126
-
1127 if (!session.got_data() || rti != soci::i_ok)
-
1128 return TxSearched::some;
+
1117 if ((!got_data || txn != soci::i_ok || meta != soci::i_ok) && !range)
+
1118 return TxSearched::unknown;
+
1119
+
1120 if (!got_data)
+
1121 {
+
1122 uint64_t count = 0;
+
1123 soci::indicator rti;
+
1124
+
1125 session << "SELECT COUNT(DISTINCT LedgerSeq) FROM Transactions WHERE "
+
1126 "LedgerSeq BETWEEN "
+
1127 << range->first() << " AND " << range->last() << ";",
+
1128 soci::into(count, rti);
1129
-
1130 return count == (range->last() - range->first() + 1) ? TxSearched::all : TxSearched::some;
-
1131 }
+
1130 if (!session.got_data() || rti != soci::i_ok)
+
1131 return TxSearched::some;
1132
-
1133 convert(sociRawTxnBlob, rawTxn);
+
1133 return count == (range->last() - range->first() + 1) ? TxSearched::all : TxSearched::some;
+
1134 }
+
1135
+
1136 convert(sociRawTxnBlob, rawTxn);
-
1134 convert(sociRawMetaBlob, rawMeta);
-
1135 }
-
- -
1137 try
-
1138 {
-
1139 auto txn = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app);
-
1140
-
1141 if (!ledgerSeq)
-
1142 return std::pair{std::move(txn), nullptr};
+
1137 convert(sociRawMetaBlob, rawMeta);
+
1138 }
+
+ +
1140 try
+
1141 {
+
1142 auto txn = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app);
1143
-
1144 std::uint32_t inLedger = rangeCheckedCast<std::uint32_t>(ledgerSeq.value());
-
1145
-
1146 auto txMeta = std::make_shared<TxMeta>(id, inLedger, rawMeta);
-
1147
-
1148 return std::pair{std::move(txn), std::move(txMeta)};
-
1149 }
-
1150 catch (std::exception& e)
-
1151 {
-
1152 JLOG(app.journal("Ledger").warn())
-
1153 << "Unable to deserialize transaction from raw SQL value. Error: " << e.what();
-
1154
- -
1156 }
+
1144 if (!ledgerSeq)
+
1145 return std::pair{std::move(txn), nullptr};
+
1146
+
1147 std::uint32_t inLedger = rangeCheckedCast<std::uint32_t>(ledgerSeq.value());
+
1148
+
1149 auto txMeta = std::make_shared<TxMeta>(id, inLedger, rawMeta);
+
1150
+
1151 return std::pair{std::move(txn), std::move(txMeta)};
+
1152 }
+
1153 catch (std::exception& e)
+
1154 {
+
1155 JLOG(app.journal("Ledger").warn())
+
1156 << "Unable to deserialize transaction from raw SQL value. Error: " << e.what();
1157
-
1158 return TxSearched::unknown;
-
1159}
+ +
1159 }
1160
-
1161bool
-
1162dbHasSpace(soci::session& session, Config const& config, beast::Journal j)
-
1163{
-
1164 boost::filesystem::space_info space = boost::filesystem::space(config.legacy("database_path"));
-
1165
-
1166 if (space.available < megabytes(512))
-
1167 {
-
1168 JLOG(j.fatal()) << "Remaining free disk space is less than 512MB";
-
1169 return false;
-
1170 }
-
1171
-
1172 if (config.useTxTables())
-
1173 {
-
1174 DatabaseCon::Setup dbSetup = setup_DatabaseCon(config);
-
1175 boost::filesystem::path dbPath = dbSetup.dataDir / TxDBName;
-
1176 boost::system::error_code ec;
-
1177 std::optional<std::uint64_t> dbSize = boost::filesystem::file_size(dbPath, ec);
-
1178 if (ec)
-
1179 {
-
1180 JLOG(j.error()) << "Error checking transaction db file size: " << ec.message();
-
1181 dbSize.reset();
-
1182 }
-
1183
-
1184 static auto const pageSize = [&] {
-
1185 std::uint32_t ps;
-
1186 session << "PRAGMA page_size;", soci::into(ps);
-
1187 return ps;
-
1188 }();
-
1189 static auto const maxPages = [&] {
-
1190 std::uint32_t mp;
-
1191 session << "PRAGMA max_page_count;", soci::into(mp);
+
1161 return TxSearched::unknown;
+
1162}
+
1163
+
1164bool
+
1165dbHasSpace(soci::session& session, Config const& config, beast::Journal j)
+
1166{
+
1167 boost::filesystem::space_info space = boost::filesystem::space(config.legacy("database_path"));
+
1168
+
1169 if (space.available < megabytes(512))
+
1170 {
+
1171 JLOG(j.fatal()) << "Remaining free disk space is less than 512MB";
+
1172 return false;
+
1173 }
+
1174
+
1175 if (config.useTxTables())
+
1176 {
+
1177 DatabaseCon::Setup dbSetup = setup_DatabaseCon(config);
+
1178 boost::filesystem::path dbPath = dbSetup.dataDir / TxDBName;
+
1179 boost::system::error_code ec;
+
1180 std::optional<std::uint64_t> dbSize = boost::filesystem::file_size(dbPath, ec);
+
1181 if (ec)
+
1182 {
+
1183 JLOG(j.error()) << "Error checking transaction db file size: " << ec.message();
+
1184 dbSize.reset();
+
1185 }
+
1186
+
1187 static auto const pageSize = [&] {
+
1188 std::uint32_t ps;
+
1189 session << "PRAGMA page_size;", soci::into(ps);
+
1190 return ps;
+
1191 }();
+
1192 static auto const maxPages = [&] {
+
1193 std::uint32_t mp;
+
1194 session << "PRAGMA max_page_count;", soci::into(mp);
-
1192 return mp;
-
1193 }();
-
1194 std::uint32_t pageCount;
-
1195 session << "PRAGMA page_count;", soci::into(pageCount);
-
1196 std::uint32_t freePages = maxPages - pageCount;
-
1197 std::uint64_t freeSpace = safe_cast<std::uint64_t>(freePages) * pageSize;
-
1198 JLOG(j.info()) << "Transaction DB pathname: " << dbPath.string() << "; file size: " << dbSize.value_or(-1)
-
1199 << " bytes"
-
1200 << "; SQLite page size: " << pageSize << " bytes"
-
1201 << "; Free pages: " << freePages << "; Free space: " << freeSpace << " bytes; "
-
1202 << "Note that this does not take into account available disk "
-
1203 "space.";
-
1204
-
1205 if (freeSpace < megabytes(512))
-
1206 {
-
1207 JLOG(j.fatal()) << "Free SQLite space for transaction db is less than "
-
1208 "512MB. To fix this, rippled must be executed with the "
-
1209 "vacuum parameter before restarting. "
-
1210 "Note that this activity can take multiple days, "
-
1211 "depending on database size.";
-
1212 return false;
-
1213 }
-
1214 }
-
1215
-
1216 return true;
-
1217}
+
1195 return mp;
+
1196 }();
+
1197 std::uint32_t pageCount;
+
1198 session << "PRAGMA page_count;", soci::into(pageCount);
+
1199 std::uint32_t freePages = maxPages - pageCount;
+
1200 std::uint64_t freeSpace = safe_cast<std::uint64_t>(freePages) * pageSize;
+
1201 JLOG(j.info()) << "Transaction DB pathname: " << dbPath.string() << "; file size: " << dbSize.value_or(-1)
+
1202 << " bytes"
+
1203 << "; SQLite page size: " << pageSize << " bytes"
+
1204 << "; Free pages: " << freePages << "; Free space: " << freeSpace << " bytes; "
+
1205 << "Note that this does not take into account available disk "
+
1206 "space.";
+
1207
+
1208 if (freeSpace < megabytes(512))
+
1209 {
+
1210 JLOG(j.fatal()) << "Free SQLite space for transaction db is less than "
+
1211 "512MB. To fix this, rippled must be executed with the "
+
1212 "vacuum parameter before restarting. "
+
1213 "Note that this activity can take multiple days, "
+
1214 "depending on database size.";
+
1215 return false;
+
1216 }
+
1217 }
1218
-
1219} // namespace detail
-
1220} // namespace xrpl
+
1219 return true;
+
1220}
+
1221
+
1222} // namespace detail
+
1223} // namespace xrpl
T append(T... args)
@@ -1318,35 +1321,35 @@ $(document).ready(function() { init_codefold(0); });
void deleteBeforeLedgerSeq(soci::session &session, TableType type, LedgerIndex ledgerSeq)
deleteBeforeLedgerSeq Deletes all entries in given table for the ledgers with given sequence and all ...
Definition Node.cpp:126
std::optional< LedgerIndex > getMinLedgerSeq(soci::session &session, TableType type)
getMinLedgerSeq Returns minimum ledger sequence in given table.
Definition Node.cpp:100
std::optional< LedgerIndex > getMaxLedgerSeq(soci::session &session, TableType type)
getMaxLedgerSeq Returns maximum ledger sequence in given table.
Definition Node.cpp:110
-
std::pair< RelationalDatabase::AccountTxs, int > getNewestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxs Returns newest transactions for given account which match given criteria starting...
Definition Node.cpp:775
+
std::pair< RelationalDatabase::AccountTxs, int > getNewestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxs Returns newest transactions for given account which match given criteria starting...
Definition Node.cpp:778
std::pair< std::vector< std::shared_ptr< Transaction > >, int > getTxHistory(soci::session &session, Application &app, LedgerIndex startIndex, int quantity)
getTxHistory Returns given number of most recent transactions starting from given number of entry.
Definition Node.cpp:550
-
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getOldestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxsB Returns oldest transactions in binary form for given account which match given c...
Definition Node.cpp:857
+
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getOldestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxsB Returns oldest transactions in binary form for given account which match given c...
Definition Node.cpp:860
std::optional< LedgerHeader > getNewestLedgerInfo(soci::session &session, beast::Journal j)
getNewestLedgerInfo Returns info of newest saved ledger.
Definition Node.cpp:425
std::optional< LedgerHeader > getLimitedOldestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedOldestLedgerInfo Returns info of oldest ledger from ledgers with sequences greater or equal...
Definition Node.cpp:433
-
std::variant< RelationalDatabase::AccountTx, TxSearched > getTransaction(soci::session &session, Application &app, uint256 const &id, std::optional< ClosedInterval< uint32_t > > const &range, error_code_i &ec)
getTransaction Returns transaction with given hash.
Definition Node.cpp:1061
+
std::variant< RelationalDatabase::AccountTx, TxSearched > getTransaction(soci::session &session, Application &app, uint256 const &id, std::optional< ClosedInterval< uint32_t > > const &range, error_code_i &ec)
getTransaction Returns transaction with given hash.
Definition Node.cpp:1064
DatabasePairValid makeLedgerDBs(Config const &config, DatabaseCon::Setup const &setup, DatabaseCon::CheckpointerSetup const &checkpointerSetup, beast::Journal j)
makeLedgerDBs Opens ledger and transactions databases.
Definition Node.cpp:49
RelationalDatabase::CountMinMax getRowsMinMax(soci::session &session, TableType type)
getRowsMinMax Returns minimum ledger sequence, maximum ledger sequence and total number of rows in gi...
Definition Node.cpp:144
-
static std::pair< RelationalDatabase::AccountTxs, int > getAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, bool descending, beast::Journal j)
getAccountTxs Returns the oldest or newest transactions for the account that matches the given criter...
Definition Node.cpp:696
+
static std::pair< RelationalDatabase::AccountTxs, int > getAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, bool descending, beast::Journal j)
getAccountTxs Returns the oldest or newest transactions for the account that matches the given criter...
Definition Node.cpp:699
static std::optional< LedgerHeader > getLedgerInfo(soci::session &session, std::string const &sqlSuffix, beast::Journal j)
getLedgerInfo Returns the info of the ledger retrieved from the database by using the provided SQL qu...
Definition Node.cpp:353
-
static std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > accountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length, bool forward)
accountTxPage Searches for the oldest or newest transactions for the account that matches the given c...
Definition Node.cpp:896
+
static std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > accountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length, bool forward)
accountTxPage Searches for the oldest or newest transactions for the account that matches the given c...
Definition Node.cpp:899
std::size_t getRows(soci::session &session, TableType type)
getRows Returns number of rows in given table.
Definition Node.cpp:132
std::optional< LedgerHeader > getLedgerInfoByIndex(soci::session &session, LedgerIndex ledgerSeq, beast::Journal j)
getLedgerInfoByIndex Returns ledger by its sequence.
Definition Node.cpp:417
-
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getNewestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxsB Returns newest transactions in binary form for given account which match given c...
Definition Node.cpp:867
+
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getNewestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxsB Returns newest transactions in binary form for given account which match given c...
Definition Node.cpp:870
uint256 getHashByIndex(soci::session &session, LedgerIndex ledgerIndex)
getHashByIndex Returns hash of ledger with given sequence.
Definition Node.cpp:457
void deleteByLedgerSeq(soci::session &session, TableType type, LedgerIndex ledgerSeq)
deleteByLedgerSeq Deletes all entries in given table for the ledger with given sequence.
Definition Node.cpp:120
-
std::pair< RelationalDatabase::AccountTxs, int > getOldestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxs Returns oldest transactions for given account which match given criteria starting...
Definition Node.cpp:764
-
bool dbHasSpace(soci::session &session, Config const &config, beast::Journal j)
dbHasSpace Checks if given database has available space.
Definition Node.cpp:1136
+
std::pair< RelationalDatabase::AccountTxs, int > getOldestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxs Returns oldest transactions for given account which match given criteria starting...
Definition Node.cpp:767
+
bool dbHasSpace(soci::session &session, Config const &config, beast::Journal j)
dbHasSpace Checks if given database has available space.
Definition Node.cpp:1139
std::optional< LedgerHeader > getLimitedNewestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedNewestLedgerInfo Returns info of newest ledger from ledgers with sequences greater or equal...
Definition Node.cpp:441
-
static std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, bool descending, beast::Journal j)
getAccountTxsB Returns the oldest or newest transactions in binary form for the account that matches ...
Definition Node.cpp:806
-
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > oldestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
oldestAccountTxPage Searches oldest transactions for given account which match given criteria startin...
Definition Node.cpp:1039
+
static std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, bool descending, beast::Journal j)
getAccountTxsB Returns the oldest or newest transactions in binary form for the account that matches ...
Definition Node.cpp:809
+
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > oldestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
oldestAccountTxPage Searches oldest transactions for given account which match given criteria startin...
Definition Node.cpp:1042
static std::string to_string(TableType type)
to_string Returns the name of a table according to its TableType.
Definition Node.cpp:28
constexpr int TableTypeCount
Definition Node.h:13
-
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > newestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
newestAccountTxPage Searches newest transactions for given account which match given criteria startin...
Definition Node.cpp:1050
-
static std::string transactionsSQL(Application &app, std::string selection, RelationalDatabase::AccountTxOptions const &options, bool descending, bool binary, bool count, beast::Journal j)
transactionsSQL Returns a SQL query for selecting the oldest or newest transactions in decoded or bin...
Definition Node.cpp:608
+
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > newestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
newestAccountTxPage Searches newest transactions for given account which match given criteria startin...
Definition Node.cpp:1053
+
static std::string transactionsSQL(Application &app, std::string selection, RelationalDatabase::AccountTxOptions const &options, bool descending, bool binary, bool count, beast::Journal j)
transactionsSQL Returns a SQL query for selecting the oldest or newest transactions in decoded or bin...
Definition Node.cpp:609
std::optional< LedgerHashPair > getHashesByIndex(soci::session &session, LedgerIndex ledgerIndex, beast::Journal j)
getHashesByIndex Returns hash of the ledger and hash of parent ledger for the ledger of given sequenc...
Definition Node.cpp:486
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/Node_8h_source.html b/Node_8h_source.html index df1d453ac1..54fea167e2 100644 --- a/Node_8h_source.html +++ b/Node_8h_source.html @@ -238,29 +238,29 @@ $(document).ready(function() { init_codefold(0); });
void deleteBeforeLedgerSeq(soci::session &session, TableType type, LedgerIndex ledgerSeq)
deleteBeforeLedgerSeq Deletes all entries in given table for the ledgers with given sequence and all ...
Definition Node.cpp:126
std::optional< LedgerIndex > getMinLedgerSeq(soci::session &session, TableType type)
getMinLedgerSeq Returns minimum ledger sequence in given table.
Definition Node.cpp:100
std::optional< LedgerIndex > getMaxLedgerSeq(soci::session &session, TableType type)
getMaxLedgerSeq Returns maximum ledger sequence in given table.
Definition Node.cpp:110
-
std::pair< RelationalDatabase::AccountTxs, int > getNewestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxs Returns newest transactions for given account which match given criteria starting...
Definition Node.cpp:775
+
std::pair< RelationalDatabase::AccountTxs, int > getNewestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxs Returns newest transactions for given account which match given criteria starting...
Definition Node.cpp:778
std::pair< std::vector< std::shared_ptr< Transaction > >, int > getTxHistory(soci::session &session, Application &app, LedgerIndex startIndex, int quantity)
getTxHistory Returns given number of most recent transactions starting from given number of entry.
Definition Node.cpp:550
-
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getOldestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxsB Returns oldest transactions in binary form for given account which match given c...
Definition Node.cpp:857
+
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getOldestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxsB Returns oldest transactions in binary form for given account which match given c...
Definition Node.cpp:860
std::optional< LedgerHeader > getNewestLedgerInfo(soci::session &session, beast::Journal j)
getNewestLedgerInfo Returns info of newest saved ledger.
Definition Node.cpp:425
std::optional< LedgerHeader > getLimitedOldestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedOldestLedgerInfo Returns info of oldest ledger from ledgers with sequences greater or equal...
Definition Node.cpp:433
-
std::variant< RelationalDatabase::AccountTx, TxSearched > getTransaction(soci::session &session, Application &app, uint256 const &id, std::optional< ClosedInterval< uint32_t > > const &range, error_code_i &ec)
getTransaction Returns transaction with given hash.
Definition Node.cpp:1061
+
std::variant< RelationalDatabase::AccountTx, TxSearched > getTransaction(soci::session &session, Application &app, uint256 const &id, std::optional< ClosedInterval< uint32_t > > const &range, error_code_i &ec)
getTransaction Returns transaction with given hash.
Definition Node.cpp:1064
DatabasePairValid makeLedgerDBs(Config const &config, DatabaseCon::Setup const &setup, DatabaseCon::CheckpointerSetup const &checkpointerSetup, beast::Journal j)
makeLedgerDBs Opens ledger and transactions databases.
Definition Node.cpp:49
RelationalDatabase::CountMinMax getRowsMinMax(soci::session &session, TableType type)
getRowsMinMax Returns minimum ledger sequence, maximum ledger sequence and total number of rows in gi...
Definition Node.cpp:144
std::size_t getRows(soci::session &session, TableType type)
getRows Returns number of rows in given table.
Definition Node.cpp:132
std::optional< LedgerHeader > getLedgerInfoByIndex(soci::session &session, LedgerIndex ledgerSeq, beast::Journal j)
getLedgerInfoByIndex Returns ledger by its sequence.
Definition Node.cpp:417
-
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getNewestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxsB Returns newest transactions in binary form for given account which match given c...
Definition Node.cpp:867
+
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getNewestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxsB Returns newest transactions in binary form for given account which match given c...
Definition Node.cpp:870
uint256 getHashByIndex(soci::session &session, LedgerIndex ledgerIndex)
getHashByIndex Returns hash of ledger with given sequence.
Definition Node.cpp:457
void deleteByLedgerSeq(soci::session &session, TableType type, LedgerIndex ledgerSeq)
deleteByLedgerSeq Deletes all entries in given table for the ledger with given sequence.
Definition Node.cpp:120
-
std::pair< RelationalDatabase::AccountTxs, int > getOldestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxs Returns oldest transactions for given account which match given criteria starting...
Definition Node.cpp:764
-
bool dbHasSpace(soci::session &session, Config const &config, beast::Journal j)
dbHasSpace Checks if given database has available space.
Definition Node.cpp:1136
+
std::pair< RelationalDatabase::AccountTxs, int > getOldestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxs Returns oldest transactions for given account which match given criteria starting...
Definition Node.cpp:767
+
bool dbHasSpace(soci::session &session, Config const &config, beast::Journal j)
dbHasSpace Checks if given database has available space.
Definition Node.cpp:1139
std::optional< LedgerHeader > getLimitedNewestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedNewestLedgerInfo Returns info of newest ledger from ledgers with sequences greater or equal...
Definition Node.cpp:441
-
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > oldestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
oldestAccountTxPage Searches oldest transactions for given account which match given criteria startin...
Definition Node.cpp:1039
+
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > oldestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
oldestAccountTxPage Searches oldest transactions for given account which match given criteria startin...
Definition Node.cpp:1042
constexpr int TableTypeCount
Definition Node.h:13
-
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > newestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
newestAccountTxPage Searches newest transactions for given account which match given criteria startin...
Definition Node.cpp:1050
+
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > newestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
newestAccountTxPage Searches newest transactions for given account which match given criteria startin...
Definition Node.cpp:1053
std::optional< LedgerHashPair > getHashesByIndex(soci::session &session, LedgerIndex ledgerIndex, beast::Journal j)
getHashesByIndex Returns hash of the ledger and hash of parent ledger for the ledger of given sequenc...
Definition Node.cpp:486
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition RangeSet.h:34
diff --git a/Number_8cpp_source.html b/Number_8cpp_source.html index a76017e780..d865956f36 100644 --- a/Number_8cpp_source.html +++ b/Number_8cpp_source.html @@ -919,329 +919,330 @@ $(document).ready(function() { init_codefold(0); });
775
-
776Number::operator rep() const
-
777{
-
778 rep drops = mantissa();
-
779 int offset = exponent();
-
780 Guard g;
-
781 if (drops != 0)
-
782 {
-
783 if (negative_)
-
784 {
-
785 g.set_negative();
-
786 drops = -drops;
-
787 }
-
788 for (; offset < 0; ++offset)
-
789 {
-
790 g.push(drops % 10);
-
791 drops /= 10;
-
792 }
-
793 for (; offset > 0; --offset)
-
794 {
-
795 if (drops > maxRep / 10)
-
796 throw std::overflow_error("Number::operator rep() overflow");
-
797 drops *= 10;
-
798 }
-
799 g.doRound(drops, "Number::operator rep() rounding overflow");
-
800 }
-
801 return drops;
-
802}
+
776Number::
+
777operator rep() const
+
778{
+
779 rep drops = mantissa();
+
780 int offset = exponent();
+
781 Guard g;
+
782 if (drops != 0)
+
783 {
+
784 if (negative_)
+
785 {
+
786 g.set_negative();
+
787 drops = -drops;
+
788 }
+
789 for (; offset < 0; ++offset)
+
790 {
+
791 g.push(drops % 10);
+
792 drops /= 10;
+
793 }
+
794 for (; offset > 0; --offset)
+
795 {
+
796 if (drops > maxRep / 10)
+
797 throw std::overflow_error("Number::operator rep() overflow");
+
798 drops *= 10;
+
799 }
+
800 g.doRound(drops, "Number::operator rep() rounding overflow");
+
801 }
+
802 return drops;
+
803}
-
803
-
804Number
-
-
805Number::truncate() const noexcept
-
806{
-
807 if (exponent_ >= 0 || mantissa_ == 0)
-
808 return *this;
-
809
-
810 Number ret = *this;
-
811 while (ret.exponent_ < 0 && ret.mantissa_ != 0)
-
812 {
-
813 ret.exponent_ += 1;
-
814 ret.mantissa_ /= rep(10);
-
815 }
-
816 // We are guaranteed that normalize() will never throw an exception
-
817 // because exponent is either negative or zero at this point.
-
818 ret.normalize();
-
819 return ret;
-
820}
+
804
+
805Number
+
+
806Number::truncate() const noexcept
+
807{
+
808 if (exponent_ >= 0 || mantissa_ == 0)
+
809 return *this;
+
810
+
811 Number ret = *this;
+
812 while (ret.exponent_ < 0 && ret.mantissa_ != 0)
+
813 {
+
814 ret.exponent_ += 1;
+
815 ret.mantissa_ /= rep(10);
+
816 }
+
817 // We are guaranteed that normalize() will never throw an exception
+
818 // because exponent is either negative or zero at this point.
+
819 ret.normalize();
+
820 return ret;
+
821}
-
821
- -
-
823to_string(Number const& amount)
-
824{
-
825 // keep full internal accuracy, but make more human friendly if possible
-
826 constexpr Number zero = Number{};
-
827 if (amount == zero)
-
828 return "0";
-
829
-
830 auto exponent = amount.exponent_;
-
831 auto mantissa = amount.mantissa_;
-
832 bool const negative = amount.negative_;
-
833
-
834 // Use scientific notation for exponents that are too small or too large
-
835 auto const rangeLog = Number::mantissaLog();
-
836 if (((exponent != 0) && ((exponent < -(rangeLog + 10)) || (exponent > -(rangeLog - 10)))))
-
837 {
-
838 while (mantissa != 0 && mantissa % 10 == 0 && exponent < Number::maxExponent)
-
839 {
-
840 mantissa /= 10;
-
841 ++exponent;
-
842 }
-
843 std::string ret = negative ? "-" : "";
- -
845 ret.append(1, 'e');
- -
847 return ret;
-
848 }
-
849
-
850 XRPL_ASSERT(exponent + 43 > 0, "xrpl::to_string(Number) : minimum exponent");
-
851
-
852 ptrdiff_t const pad_prefix = rangeLog + 12;
-
853 ptrdiff_t const pad_suffix = rangeLog + 8;
-
854
-
855 std::string const raw_value(std::to_string(mantissa));
-
856 std::string val;
-
857
-
858 val.reserve(raw_value.length() + pad_prefix + pad_suffix);
-
859 val.append(pad_prefix, '0');
-
860 val.append(raw_value);
-
861 val.append(pad_suffix, '0');
-
862
-
863 ptrdiff_t const offset(exponent + pad_prefix + rangeLog + 1);
-
864
-
865 auto pre_from(val.begin());
-
866 auto const pre_to(val.begin() + offset);
-
867
-
868 auto const post_from(val.begin() + offset);
-
869 auto post_to(val.end());
-
870
-
871 // Crop leading zeroes. Take advantage of the fact that there's always a
-
872 // fixed amount of leading zeroes and skip them.
-
873 if (std::distance(pre_from, pre_to) > pad_prefix)
-
874 pre_from += pad_prefix;
-
875
-
876 XRPL_ASSERT(post_to >= post_from, "xrpl::to_string(Number) : first distance check");
-
877
-
878 pre_from = std::find_if(pre_from, pre_to, [](char c) { return c != '0'; });
-
879
-
880 // Crop trailing zeroes. Take advantage of the fact that there's always a
-
881 // fixed amount of trailing zeroes and skip them.
-
882 if (std::distance(post_from, post_to) > pad_suffix)
-
883 post_to -= pad_suffix;
-
884
-
885 XRPL_ASSERT(post_to >= post_from, "xrpl::to_string(Number) : second distance check");
-
886
-
887 post_to = std::find_if(std::make_reverse_iterator(post_to), std::make_reverse_iterator(post_from), [](char c) {
-
888 return c != '0';
-
889 }).base();
-
890
-
891 std::string ret;
-
892
-
893 if (negative)
-
894 ret.append(1, '-');
-
895
-
896 // Assemble the output:
-
897 if (pre_from == pre_to)
-
898 ret.append(1, '0');
-
899 else
-
900 ret.append(pre_from, pre_to);
-
901
-
902 if (post_to != post_from)
-
903 {
-
904 ret.append(1, '.');
-
905 ret.append(post_from, post_to);
-
906 }
-
907
-
908 return ret;
-
909}
+
822
+ +
+
824to_string(Number const& amount)
+
825{
+
826 // keep full internal accuracy, but make more human friendly if possible
+
827 constexpr Number zero = Number{};
+
828 if (amount == zero)
+
829 return "0";
+
830
+
831 auto exponent = amount.exponent_;
+
832 auto mantissa = amount.mantissa_;
+
833 bool const negative = amount.negative_;
+
834
+
835 // Use scientific notation for exponents that are too small or too large
+
836 auto const rangeLog = Number::mantissaLog();
+
837 if (((exponent != 0) && ((exponent < -(rangeLog + 10)) || (exponent > -(rangeLog - 10)))))
+
838 {
+
839 while (mantissa != 0 && mantissa % 10 == 0 && exponent < Number::maxExponent)
+
840 {
+
841 mantissa /= 10;
+
842 ++exponent;
+
843 }
+
844 std::string ret = negative ? "-" : "";
+ +
846 ret.append(1, 'e');
+ +
848 return ret;
+
849 }
+
850
+
851 XRPL_ASSERT(exponent + 43 > 0, "xrpl::to_string(Number) : minimum exponent");
+
852
+
853 ptrdiff_t const pad_prefix = rangeLog + 12;
+
854 ptrdiff_t const pad_suffix = rangeLog + 8;
+
855
+
856 std::string const raw_value(std::to_string(mantissa));
+
857 std::string val;
+
858
+
859 val.reserve(raw_value.length() + pad_prefix + pad_suffix);
+
860 val.append(pad_prefix, '0');
+
861 val.append(raw_value);
+
862 val.append(pad_suffix, '0');
+
863
+
864 ptrdiff_t const offset(exponent + pad_prefix + rangeLog + 1);
+
865
+
866 auto pre_from(val.begin());
+
867 auto const pre_to(val.begin() + offset);
+
868
+
869 auto const post_from(val.begin() + offset);
+
870 auto post_to(val.end());
+
871
+
872 // Crop leading zeroes. Take advantage of the fact that there's always a
+
873 // fixed amount of leading zeroes and skip them.
+
874 if (std::distance(pre_from, pre_to) > pad_prefix)
+
875 pre_from += pad_prefix;
+
876
+
877 XRPL_ASSERT(post_to >= post_from, "xrpl::to_string(Number) : first distance check");
+
878
+
879 pre_from = std::find_if(pre_from, pre_to, [](char c) { return c != '0'; });
+
880
+
881 // Crop trailing zeroes. Take advantage of the fact that there's always a
+
882 // fixed amount of trailing zeroes and skip them.
+
883 if (std::distance(post_from, post_to) > pad_suffix)
+
884 post_to -= pad_suffix;
+
885
+
886 XRPL_ASSERT(post_to >= post_from, "xrpl::to_string(Number) : second distance check");
+
887
+
888 post_to = std::find_if(std::make_reverse_iterator(post_to), std::make_reverse_iterator(post_from), [](char c) {
+
889 return c != '0';
+
890 }).base();
+
891
+
892 std::string ret;
+
893
+
894 if (negative)
+
895 ret.append(1, '-');
+
896
+
897 // Assemble the output:
+
898 if (pre_from == pre_to)
+
899 ret.append(1, '0');
+
900 else
+
901 ret.append(pre_from, pre_to);
+
902
+
903 if (post_to != post_from)
+
904 {
+
905 ret.append(1, '.');
+
906 ret.append(post_from, post_to);
+
907 }
+
908
+
909 return ret;
+
910}
-
910
-
911// Returns f^n
-
912// Uses a log_2(n) number of multiplications
-
913
-
914Number
-
-
915power(Number const& f, unsigned n)
-
916{
-
917 if (n == 0)
-
918 return Number::one();
-
919 if (n == 1)
-
920 return f;
-
921 auto r = power(f, n / 2);
-
922 r *= r;
-
923 if (n % 2 != 0)
-
924 r *= f;
-
925 return r;
-
926}
+
911
+
912// Returns f^n
+
913// Uses a log_2(n) number of multiplications
+
914
+
915Number
+
+
916power(Number const& f, unsigned n)
+
917{
+
918 if (n == 0)
+
919 return Number::one();
+
920 if (n == 1)
+
921 return f;
+
922 auto r = power(f, n / 2);
+
923 r *= r;
+
924 if (n % 2 != 0)
+
925 r *= f;
+
926 return r;
+
927}
-
927
-
928// Returns f^(1/d)
-
929// Uses Newton–Raphson iterations until the result stops changing
-
930// to find the non-negative root of the polynomial g(x) = x^d - f
-
931
-
932// This function, and power(Number f, unsigned n, unsigned d)
-
933// treat corner cases such as 0 roots as advised by Annex F of
-
934// the C standard, which itself is consistent with the IEEE
-
935// floating point standards.
-
936
-
937Number
-
-
938root(Number f, unsigned d)
-
939{
-
940 constexpr Number zero = Number{};
-
941 auto const one = Number::one();
-
942
-
943 if (f == one || d == 1)
-
944 return f;
-
945 if (d == 0)
-
946 {
-
947 if (f == -one)
-
948 return one;
-
949 if (abs(f) < one)
-
950 return zero;
-
951 throw std::overflow_error("Number::root infinity");
-
952 }
-
953 if (f < zero && d % 2 == 0)
-
954 throw std::overflow_error("Number::root nan");
-
955 if (f == zero)
-
956 return f;
-
957
-
958 // Scale f into the range (0, 1) such that f's exponent is a multiple of d
-
959 auto e = f.exponent_ + Number::mantissaLog() + 1;
-
960 auto const di = static_cast<int>(d);
-
961 auto ex = [e = e, di = di]() // Euclidean remainder of e/d
-
962 {
-
963 int k = (e >= 0 ? e : e - (di - 1)) / di;
-
964 int k2 = e - k * di;
-
965 if (k2 == 0)
-
966 return 0;
-
967 return di - k2;
-
968 }();
-
969 e += ex;
-
970 f = f.shiftExponent(-e); // f /= 10^e;
-
971
-
972 XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root(Number, unsigned)", "f is normalized");
-
973 bool neg = false;
-
974 if (f < zero)
-
975 {
-
976 neg = true;
-
977 f = -f;
-
978 }
-
979
-
980 // Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
-
981 auto const D = ((6 * di + 11) * di + 6) * di + 1;
-
982 auto const a0 = 3 * di * ((2 * di - 3) * di + 1);
-
983 auto const a1 = 24 * di * (2 * di - 1);
-
984 auto const a2 = -30 * (di - 1) * di;
-
985 Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D};
-
986 if (neg)
-
987 {
-
988 f = -f;
-
989 r = -r;
-
990 }
-
991
-
992 // Newton–Raphson iteration of f^(1/d) with initial guess r
-
993 // halt when r stops changing, checking for bouncing on the last iteration
-
994 Number rm1{};
-
995 Number rm2{};
-
996 do
-
997 {
-
998 rm2 = rm1;
-
999 rm1 = r;
-
1000 r = (Number(d - 1) * r + f / power(r, d - 1)) / Number(d);
-
1001 } while (r != rm1 && r != rm2);
-
1002
-
1003 // return r * 10^(e/d) to reverse scaling
-
1004 auto const result = r.shiftExponent(e / di);
-
1005 XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root(Number, unsigned)", "result is normalized");
-
1006 return result;
-
1007}
+
928
+
929// Returns f^(1/d)
+
930// Uses Newton–Raphson iterations until the result stops changing
+
931// to find the non-negative root of the polynomial g(x) = x^d - f
+
932
+
933// This function, and power(Number f, unsigned n, unsigned d)
+
934// treat corner cases such as 0 roots as advised by Annex F of
+
935// the C standard, which itself is consistent with the IEEE
+
936// floating point standards.
+
937
+
938Number
+
+
939root(Number f, unsigned d)
+
940{
+
941 constexpr Number zero = Number{};
+
942 auto const one = Number::one();
+
943
+
944 if (f == one || d == 1)
+
945 return f;
+
946 if (d == 0)
+
947 {
+
948 if (f == -one)
+
949 return one;
+
950 if (abs(f) < one)
+
951 return zero;
+
952 throw std::overflow_error("Number::root infinity");
+
953 }
+
954 if (f < zero && d % 2 == 0)
+
955 throw std::overflow_error("Number::root nan");
+
956 if (f == zero)
+
957 return f;
+
958
+
959 // Scale f into the range (0, 1) such that f's exponent is a multiple of d
+
960 auto e = f.exponent_ + Number::mantissaLog() + 1;
+
961 auto const di = static_cast<int>(d);
+
962 auto ex = [e = e, di = di]() // Euclidean remainder of e/d
+
963 {
+
964 int k = (e >= 0 ? e : e - (di - 1)) / di;
+
965 int k2 = e - k * di;
+
966 if (k2 == 0)
+
967 return 0;
+
968 return di - k2;
+
969 }();
+
970 e += ex;
+
971 f = f.shiftExponent(-e); // f /= 10^e;
+
972
+
973 XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root(Number, unsigned)", "f is normalized");
+
974 bool neg = false;
+
975 if (f < zero)
+
976 {
+
977 neg = true;
+
978 f = -f;
+
979 }
+
980
+
981 // Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
+
982 auto const D = ((6 * di + 11) * di + 6) * di + 1;
+
983 auto const a0 = 3 * di * ((2 * di - 3) * di + 1);
+
984 auto const a1 = 24 * di * (2 * di - 1);
+
985 auto const a2 = -30 * (di - 1) * di;
+
986 Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D};
+
987 if (neg)
+
988 {
+
989 f = -f;
+
990 r = -r;
+
991 }
+
992
+
993 // Newton–Raphson iteration of f^(1/d) with initial guess r
+
994 // halt when r stops changing, checking for bouncing on the last iteration
+
995 Number rm1{};
+
996 Number rm2{};
+
997 do
+
998 {
+
999 rm2 = rm1;
+
1000 rm1 = r;
+
1001 r = (Number(d - 1) * r + f / power(r, d - 1)) / Number(d);
+
1002 } while (r != rm1 && r != rm2);
+
1003
+
1004 // return r * 10^(e/d) to reverse scaling
+
1005 auto const result = r.shiftExponent(e / di);
+
1006 XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root(Number, unsigned)", "result is normalized");
+
1007 return result;
+
1008}
-
1008
-
1009Number
-
- -
1011{
-
1012 constexpr Number zero = Number{};
-
1013 auto const one = Number::one();
-
1014
-
1015 if (f == one)
-
1016 return f;
-
1017 if (f < zero)
-
1018 throw std::overflow_error("Number::root nan");
-
1019 if (f == zero)
-
1020 return f;
-
1021
-
1022 // Scale f into the range (0, 1) such that f's exponent is a multiple of d
-
1023 auto e = f.exponent_ + Number::mantissaLog() + 1;
-
1024 if (e % 2 != 0)
-
1025 ++e;
-
1026 f = f.shiftExponent(-e); // f /= 10^e;
-
1027 XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root2(Number)", "f is normalized");
-
1028
-
1029 // Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
-
1030 auto const D = 105;
-
1031 auto const a0 = 18;
-
1032 auto const a1 = 144;
-
1033 auto const a2 = -60;
-
1034 Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D};
-
1035
-
1036 // Newton–Raphson iteration of f^(1/2) with initial guess r
-
1037 // halt when r stops changing, checking for bouncing on the last iteration
-
1038 Number rm1{};
-
1039 Number rm2{};
-
1040 do
-
1041 {
-
1042 rm2 = rm1;
-
1043 rm1 = r;
-
1044 r = (r + f / r) / Number(2);
-
1045 } while (r != rm1 && r != rm2);
-
1046
-
1047 // return r * 10^(e/2) to reverse scaling
-
1048 auto const result = r.shiftExponent(e / 2);
-
1049 XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root2(Number)", "result is normalized");
-
1050
-
1051 return result;
-
1052}
+
1009
+
1010Number
+
+ +
1012{
+
1013 constexpr Number zero = Number{};
+
1014 auto const one = Number::one();
+
1015
+
1016 if (f == one)
+
1017 return f;
+
1018 if (f < zero)
+
1019 throw std::overflow_error("Number::root nan");
+
1020 if (f == zero)
+
1021 return f;
+
1022
+
1023 // Scale f into the range (0, 1) such that f's exponent is a multiple of d
+
1024 auto e = f.exponent_ + Number::mantissaLog() + 1;
+
1025 if (e % 2 != 0)
+
1026 ++e;
+
1027 f = f.shiftExponent(-e); // f /= 10^e;
+
1028 XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root2(Number)", "f is normalized");
+
1029
+
1030 // Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
+
1031 auto const D = 105;
+
1032 auto const a0 = 18;
+
1033 auto const a1 = 144;
+
1034 auto const a2 = -60;
+
1035 Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D};
+
1036
+
1037 // Newton–Raphson iteration of f^(1/2) with initial guess r
+
1038 // halt when r stops changing, checking for bouncing on the last iteration
+
1039 Number rm1{};
+
1040 Number rm2{};
+
1041 do
+
1042 {
+
1043 rm2 = rm1;
+
1044 rm1 = r;
+
1045 r = (r + f / r) / Number(2);
+
1046 } while (r != rm1 && r != rm2);
+
1047
+
1048 // return r * 10^(e/2) to reverse scaling
+
1049 auto const result = r.shiftExponent(e / 2);
+
1050 XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root2(Number)", "result is normalized");
+
1051
+
1052 return result;
+
1053}
-
1053
-
1054// Returns f^(n/d)
-
1055
-
1056Number
-
-
1057power(Number const& f, unsigned n, unsigned d)
-
1058{
-
1059 constexpr Number zero = Number{};
-
1060 auto const one = Number::one();
-
1061
-
1062 if (f == one)
-
1063 return f;
-
1064 auto g = std::gcd(n, d);
-
1065 if (g == 0)
-
1066 throw std::overflow_error("Number::power nan");
-
1067 if (d == 0)
-
1068 {
-
1069 if (f == -one)
-
1070 return one;
-
1071 if (abs(f) < one)
-
1072 return zero;
-
1073 // abs(f) > one
-
1074 throw std::overflow_error("Number::power infinity");
-
1075 }
-
1076 if (n == 0)
-
1077 return one;
-
1078 n /= g;
-
1079 d /= g;
-
1080 if ((n % 2) == 1 && (d % 2) == 0 && f < zero)
-
1081 throw std::overflow_error("Number::power nan");
-
1082 return root(power(f, n), d);
-
1083}
+
1054
+
1055// Returns f^(n/d)
+
1056
+
1057Number
+
+
1058power(Number const& f, unsigned n, unsigned d)
+
1059{
+
1060 constexpr Number zero = Number{};
+
1061 auto const one = Number::one();
+
1062
+
1063 if (f == one)
+
1064 return f;
+
1065 auto g = std::gcd(n, d);
+
1066 if (g == 0)
+
1067 throw std::overflow_error("Number::power nan");
+
1068 if (d == 0)
+
1069 {
+
1070 if (f == -one)
+
1071 return one;
+
1072 if (abs(f) < one)
+
1073 return zero;
+
1074 // abs(f) > one
+
1075 throw std::overflow_error("Number::power infinity");
+
1076 }
+
1077 if (n == 0)
+
1078 return one;
+
1079 n /= g;
+
1080 d /= g;
+
1081 if ((n % 2) == 1 && (d % 2) == 0 && f < zero)
+
1082 throw std::overflow_error("Number::power nan");
+
1083 return root(power(f, n), d);
+
1084}
-
1084
-
1085} // namespace xrpl
+
1085
+
1086} // namespace xrpl
T append(T... args)
@@ -1271,9 +1272,9 @@ $(document).ready(function() { init_codefold(0); });
static rounding_mode setround(rounding_mode mode)
Definition Number.cpp:39
Number & operator/=(Number const &x)
Definition Number.cpp:682
Number & operator+=(Number const &x)
Definition Number.cpp:497
-
Number truncate() const noexcept
Definition Number.cpp:805
+
Number truncate() const noexcept
Definition Number.cpp:806
std::int64_t rep
Definition Number.h:208
-
friend std::string to_string(Number const &amount)
Definition Number.cpp:823
+
friend std::string to_string(Number const &amount)
Definition Number.cpp:824
@@ -1293,7 +1294,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr int exponent() const noexcept
Returns the exponent of the external view of the Number.
Definition Number.h:563
static constexpr int maxExponent
Definition Number.h:218
static thread_local std::reference_wrapper< MantissaRange const > range_
Definition Number.h:457
-
friend Number root2(Number f)
Definition Number.cpp:1010
+
friend Number root2(Number f)
Definition Number.cpp:1011
static MantissaRange::mantissa_scale getMantissaScale()
Returns which mantissa scale is currently in use for normalization.
Definition Number.cpp:45
int exponent_
Definition Number.h:213
static constexpr MantissaRange largeRange
Definition Number.h:446
@@ -1304,7 +1305,7 @@ $(document).ready(function() { init_codefold(0); });
static constexpr Number oneLarge()
oneLarge is needed because the ranges are private
Definition Number.cpp:335
bool negative_
Definition Number.h:211
static int mantissaLog()
Definition Number.h:413
-
friend Number root(Number f, unsigned d)
Definition Number.cpp:938
+
friend Number root(Number f, unsigned d)
Definition Number.cpp:939
@@ -1322,7 +1323,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition RangeSet.h:34
-
Number power(Number const &f, unsigned n)
Definition Number.cpp:915
+
Number power(Number const &f, unsigned n)
Definition Number.cpp:916
constexpr Number oneSml
Definition Number.cpp:332
constexpr Number abs(Number x) noexcept
Definition Number.h:706
static unsigned divu10(uint128_t &u)
Definition Number.cpp:602
diff --git a/Number_8h_source.html b/Number_8h_source.html index 93d997685f..1743a4b0d6 100644 --- a/Number_8h_source.html +++ b/Number_8h_source.html @@ -869,8 +869,8 @@ $(document).ready(function() { init_codefold(0); });
constexpr rep mantissa() const noexcept
Returns the mantissa of the external view of the Number.
Definition Number.h:542
static rounding_mode setround(rounding_mode mode)
Definition Number.cpp:39
static void normalize(bool &negative, T &mantissa, int &exponent, internalrep const &minMantissa, internalrep const &maxMantissa)
Normalize Number components to an arbitrary range.
-
Number truncate() const noexcept
Definition Number.cpp:805
-
friend std::string to_string(Number const &amount)
Definition Number.cpp:823
+
Number truncate() const noexcept
Definition Number.cpp:806
+
friend std::string to_string(Number const &amount)
Definition Number.cpp:824
@@ -898,7 +898,7 @@ $(document).ready(function() { init_codefold(0); });
static constexpr int maxExponent
Definition Number.h:218
friend std::ostream & operator<<(std::ostream &os, Number const &x)
Definition Number.h:365
static thread_local std::reference_wrapper< MantissaRange const > range_
Definition Number.h:457
-
friend Number root2(Number f)
Definition Number.cpp:1010
+
friend Number root2(Number f)
Definition Number.cpp:1011
constexpr int signum() const noexcept
Return the sign of the amount.
Definition Number.h:338
Number & operator-=(Number const &x)
Definition Number.h:624
static MantissaRange::mantissa_scale getMantissaScale()
Returns which mantissa scale is currently in use for normalization.
Definition Number.cpp:45
@@ -913,7 +913,7 @@ $(document).ready(function() { init_codefold(0); });
bool negative_
Definition Number.h:211
static int mantissaLog()
Definition Number.h:413
friend constexpr bool operator<=(Number const &x, Number const &y) noexcept
Definition Number.h:353
-
friend Number root(Number f, unsigned d)
Definition Number.cpp:938
+
friend Number root(Number f, unsigned d)
Definition Number.cpp:939
Number::rounding_mode mode_
Definition Number.h:760
saveNumberRoundMode(Number::rounding_mode mode) noexcept
Definition Number.h:767
@@ -938,13 +938,13 @@ $(document).ready(function() { init_codefold(0); });
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
static constexpr Number numZero
Definition Number.h:515
Number operator/(Number const &x, Number const &y)
Definition Number.h:654
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
-
Number power(Number const &f, unsigned n)
Definition Number.cpp:915
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
+
Number power(Number const &f, unsigned n)
Definition Number.cpp:916
Number operator*(Number const &x, Number const &y)
Definition Number.h:646
constexpr std::optional< int > logTen(T value)
Definition Number.h:21
static std::int64_t constexpr maxMantissa
Definition IOUAmount.cpp:49
constexpr bool isPowerOfTen(T value)
Definition Number.h:36
-
Number root2(Number f)
Definition Number.cpp:1010
+
Number root2(Number f)
Definition Number.cpp:1011
constexpr Number abs(Number x) noexcept
Definition Number.h:706
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
constexpr Number squelch(Number const &x, Number const &limit) noexcept
Definition Number.h:737
diff --git a/Number__test_8cpp_source.html b/Number__test_8cpp_source.html index 487c607332..fe41e890d8 100644 --- a/Number__test_8cpp_source.html +++ b/Number__test_8cpp_source.html @@ -1507,7 +1507,7 @@ $(document).ready(function() { init_codefold(0); });
A currency issued by an account.
Definition Issue.h:13
Sets the new scale and restores the old scale when it leaves scope.
Definition Number.h:800
-
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:190
+
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:192
@@ -1558,10 +1558,10 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static std::int64_t constexpr minMantissa
Definition IOUAmount.cpp:48
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
-
Number power(Number const &f, unsigned n)
Definition Number.cpp:915
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
+
Number power(Number const &f, unsigned n)
Definition Number.cpp:916
static std::int64_t constexpr maxMantissa
Definition IOUAmount.cpp:49
-
Number root2(Number f)
Definition Number.cpp:1010
+
Number root2(Number f)
Definition Number.cpp:1011
constexpr XRPAmount INITIAL_XRP
Configure the native currency.
constexpr Number squelch(Number const &x, Number const &limit) noexcept
Definition Number.h:737
diff --git a/Offer__test_8cpp_source.html b/Offer__test_8cpp_source.html index 9881e3217f..bcfafc9487 100644 --- a/Offer__test_8cpp_source.html +++ b/Offer__test_8cpp_source.html @@ -5317,7 +5317,7 @@ $(document).ready(function() { init_codefold(0); });
AccountID account
Definition Issue.h:16
Issue const & issue() const
Definition STAmount.h:454
-
void negate()
Definition STAmount.h:530
+
void negate()
Definition STAmount.h:532
std::string getText() const override
Definition STAmount.cpp:639
@@ -5445,7 +5445,7 @@ $(document).ready(function() { init_codefold(0); });
static epsilon_t const epsilon
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
XRPAmount txfee(Env const &env, std::uint16_t n)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value ledgerEntryRoot(Env &env, Account const &acct)
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:23
@@ -5474,7 +5474,7 @@ $(document).ready(function() { init_codefold(0); });
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition Seed.cpp:57
constexpr std::uint32_t asfDefaultRipple
Definition TxFlags.h:64
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
constexpr std::uint32_t tfNoRippleDirect
Definition TxFlags.h:87
diff --git a/OpenLedger_8cpp_source.html b/OpenLedger_8cpp_source.html index 5e2a78f633..f322f0d627 100644 --- a/OpenLedger_8cpp_source.html +++ b/OpenLedger_8cpp_source.html @@ -361,13 +361,13 @@ $(document).ready(function() { init_codefold(0); });
constexpr struct xrpl::open_ledger_t open_ledger
ApplyResult apply(Application &app, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
Definition apply.cpp:118
constexpr std::uint32_t tfInnerBatchTxn
Definition TxFlags.h:41
-
bool isTefFailure(TER x) noexcept
Definition TER.h:637
+
bool isTefFailure(TER x) noexcept
Definition TER.h:631
std::string debugTxstr(std::shared_ptr< STTx const > const &tx)
std::string debugTostr(OrderedTxs const &set)
ApplyFlags
Definition ApplyView.h:10
@ tapRETRY
Definition ApplyView.h:19
-
bool isTelLocal(TER x) noexcept
Definition TER.h:625
-
bool isTemMalformed(TER x) noexcept
Definition TER.h:631
+
bool isTelLocal(TER x) noexcept
Definition TER.h:619
+
bool isTemMalformed(TER x) noexcept
Definition TER.h:625
T str(T... args)
diff --git a/Oracle_8cpp_source.html b/Oracle_8cpp_source.html index 21331be1f6..d3b56c5931 100644 --- a/Oracle_8cpp_source.html +++ b/Oracle_8cpp_source.html @@ -459,9 +459,9 @@ $(document).ready(function() { init_codefold(0); });
T append(T... args)
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
UInt asUInt() const
-
bool isNumeric() const
+
bool isNumeric() const
time_point now() const override
Returns the current time.
diff --git a/Oracle__test_8cpp_source.html b/Oracle__test_8cpp_source.html index 2cf3cc1fa3..55700b0ef5 100644 --- a/Oracle__test_8cpp_source.html +++ b/Oracle__test_8cpp_source.html @@ -133,694 +133,726 @@ $(document).ready(function() { init_codefold(0); });
47 env.fund(env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner);
48 Oracle oracle(env, {.owner = owner, .fee = static_cast<int>(env.current()->fees().base.drops())});
49 BEAST_EXPECT(oracle.exists());
-
50 oracle.set(UpdateArg{
-
51 .series =
-
52 {
-
53 {"XRP", "EUR", 740, 1},
-
54 {"XRP", "GBP", 740, 1},
-
55 {"XRP", "CNY", 740, 1},
-
56 {"XRP", "CAD", 740, 1},
-
57 {"XRP", "AUD", 740, 1},
-
58 },
-
59 .fee = static_cast<int>(env.current()->fees().base.drops()),
- -
61 }
-
62
-
63 {
-
64 Env env(*this);
-
65 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
66 env.fund(XRP(1'000), owner);
-
67 Oracle oracle(env, {.owner = owner, .fee = baseFee}, false);
-
68
-
69 // Invalid flag
-
70 oracle.set(CreateArg{.flags = tfSellNFToken, .fee = baseFee, .err = ter(temINVALID_FLAG)});
-
71
-
72 // Duplicate token pair
-
73 oracle.set(CreateArg{
-
74 .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", 750, 1}}, .fee = baseFee, .err = ter(temMALFORMED)});
-
75
-
76 // Price is not included
-
77 oracle.set(CreateArg{
-
78 .series = {{"XRP", "USD", 740, 1}, {"XRP", "EUR", std::nullopt, 1}},
-
79 .fee = baseFee,
-
80 .err = ter(temMALFORMED)});
-
81
-
82 // Token pair is in update and delete
-
83 oracle.set(CreateArg{
-
84 .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", std::nullopt, 1}},
-
85 .fee = baseFee,
-
86 .err = ter(temMALFORMED)});
-
87 // Token pair is in add and delete
-
88 oracle.set(CreateArg{
-
89 .series = {{"XRP", "EUR", 740, 1}, {"XRP", "EUR", std::nullopt, 1}},
-
90 .fee = baseFee,
-
91 .err = ter(temMALFORMED)});
-
92
-
93 // Array of token pair is 0 or exceeds 10
-
94 oracle.set(CreateArg{
-
95 .series =
-
96 {{"XRP", "US1", 740, 1},
-
97 {"XRP", "US2", 750, 1},
-
98 {"XRP", "US3", 740, 1},
-
99 {"XRP", "US4", 750, 1},
-
100 {"XRP", "US5", 740, 1},
-
101 {"XRP", "US6", 750, 1},
-
102 {"XRP", "US7", 740, 1},
-
103 {"XRP", "US8", 750, 1},
-
104 {"XRP", "US9", 740, 1},
-
105 {"XRP", "U10", 750, 1},
-
106 {"XRP", "U11", 740, 1}},
-
107 .fee = baseFee,
-
108 .err = ter(temARRAY_TOO_LARGE)});
-
109 oracle.set(CreateArg{.series = {}, .fee = baseFee, .err = ter(temARRAY_EMPTY)});
-
110 }
-
111
-
112 // Array of token pair exceeds 10 after update
-
113 {
-
114 Env env{*this};
-
115 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
116 env.fund(XRP(1'000), owner);
-
117
-
118 Oracle oracle(env, CreateArg{.owner = owner, .series = {{{"XRP", "USD", 740, 1}}}, .fee = baseFee});
-
119 oracle.set(UpdateArg{
-
120 .series =
-
121 {
-
122 {"XRP", "US1", 740, 1},
-
123 {"XRP", "US2", 750, 1},
-
124 {"XRP", "US3", 740, 1},
-
125 {"XRP", "US4", 750, 1},
-
126 {"XRP", "US5", 740, 1},
-
127 {"XRP", "US6", 750, 1},
-
128 {"XRP", "US7", 740, 1},
-
129 {"XRP", "US8", 750, 1},
-
130 {"XRP", "US9", 740, 1},
-
131 {"XRP", "U10", 750, 1},
-
132 },
-
133 .fee = baseFee,
-
134 .err = ter(tecARRAY_TOO_LARGE)});
-
135 }
-
136
-
137 {
-
138 Env env(*this);
-
139 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
140 env.fund(XRP(1'000), owner);
-
141 Oracle oracle(env, {.owner = owner, .fee = baseFee}, false);
-
142
-
143 // Asset class or provider not included on create
-
144 oracle.set(CreateArg{
-
145 .assetClass = std::nullopt, .provider = "provider", .fee = baseFee, .err = ter(temMALFORMED)});
-
146 oracle.set(CreateArg{
-
147 .assetClass = "currency",
-
148 .provider = std::nullopt,
-
149 .uri = "URI",
-
150 .fee = baseFee,
-
151 .err = ter(temMALFORMED)});
-
152
-
153 // Asset class or provider are included on update
-
154 // and don't match the current values
-
155 oracle.set(CreateArg{.fee = static_cast<int>(env.current()->fees().base.drops())});
-
156 BEAST_EXPECT(oracle.exists());
-
157 oracle.set(UpdateArg{
-
158 .series = {{"XRP", "USD", 740, 1}}, .provider = "provider1", .fee = baseFee, .err = ter(temMALFORMED)});
-
159 oracle.set(UpdateArg{
-
160 .series = {{"XRP", "USD", 740, 1}},
-
161 .assetClass = "currency1",
-
162 .fee = baseFee,
-
163 .err = ter(temMALFORMED)});
-
164 }
-
165
-
166 {
-
167 Env env(*this);
-
168 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
169 env.fund(XRP(1'000), owner);
-
170 Oracle oracle(env, {.owner = owner, .fee = baseFee}, false);
-
171
-
172 // Fields too long
-
173 // Asset class
-
174 std::string assetClass(17, '0');
-
175 oracle.set(CreateArg{.assetClass = assetClass, .fee = baseFee, .err = ter(temMALFORMED)});
-
176 // provider
-
177 std::string const large(257, '0');
-
178 oracle.set(CreateArg{.provider = large, .fee = baseFee, .err = ter(temMALFORMED)});
-
179 // URI
-
180 oracle.set(CreateArg{.uri = large, .fee = baseFee, .err = ter(temMALFORMED)});
-
181 // Empty field
-
182 // Asset class
-
183 oracle.set(CreateArg{.assetClass = "", .fee = baseFee, .err = ter(temMALFORMED)});
-
184 // provider
-
185 oracle.set(CreateArg{.provider = "", .fee = baseFee, .err = ter(temMALFORMED)});
-
186 // URI
-
187 oracle.set(CreateArg{.uri = "", .fee = baseFee, .err = ter(temMALFORMED)});
-
188 }
-
189
-
190 {
-
191 // Different owner creates a new object and fails because
-
192 // of missing fields currency/provider
-
193 Env env(*this);
-
194 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
195 Account const some("some");
-
196 env.fund(XRP(1'000), owner);
-
197 env.fund(XRP(1'000), some);
-
198 Oracle oracle(env, {.owner = owner, .fee = baseFee});
-
199 BEAST_EXPECT(oracle.exists());
-
200 oracle.set(
-
201 UpdateArg{.owner = some, .series = {{"XRP", "USD", 740, 1}}, .fee = baseFee, .err = ter(temMALFORMED)});
-
202 }
-
203
-
204 {
-
205 // Invalid update time
-
206 using namespace std::chrono;
-
207 Env env(*this);
-
208 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
209 auto closeTime = [&]() {
-
210 return duration_cast<seconds>(env.current()->header().closeTime.time_since_epoch() - 10'000s).count();
-
211 };
+
50 oracle.set(
+ +
52 .series =
+
53 {
+
54 {"XRP", "EUR", 740, 1},
+
55 {"XRP", "GBP", 740, 1},
+
56 {"XRP", "CNY", 740, 1},
+
57 {"XRP", "CAD", 740, 1},
+
58 {"XRP", "AUD", 740, 1},
+
59 },
+
60 .fee = static_cast<int>(env.current()->fees().base.drops()),
+ +
62 }
+
63
+
64 {
+
65 Env env(*this);
+
66 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
67 env.fund(XRP(1'000), owner);
+
68 Oracle oracle(env, {.owner = owner, .fee = baseFee}, false);
+
69
+
70 // Invalid flag
+
71 oracle.set(CreateArg{.flags = tfSellNFToken, .fee = baseFee, .err = ter(temINVALID_FLAG)});
+
72
+
73 // Duplicate token pair
+
74 oracle.set(
+ +
76 .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", 750, 1}},
+
77 .fee = baseFee,
+
78 .err = ter(temMALFORMED)});
+
79
+
80 // Price is not included
+
81 oracle.set(
+ +
83 .series = {{"XRP", "USD", 740, 1}, {"XRP", "EUR", std::nullopt, 1}},
+
84 .fee = baseFee,
+
85 .err = ter(temMALFORMED)});
+
86
+
87 // Token pair is in update and delete
+
88 oracle.set(
+ +
90 .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", std::nullopt, 1}},
+
91 .fee = baseFee,
+
92 .err = ter(temMALFORMED)});
+
93 // Token pair is in add and delete
+
94 oracle.set(
+ +
96 .series = {{"XRP", "EUR", 740, 1}, {"XRP", "EUR", std::nullopt, 1}},
+
97 .fee = baseFee,
+
98 .err = ter(temMALFORMED)});
+
99
+
100 // Array of token pair is 0 or exceeds 10
+
101 oracle.set(
+
102 CreateArg{
+
103 .series =
+
104 {{"XRP", "US1", 740, 1},
+
105 {"XRP", "US2", 750, 1},
+
106 {"XRP", "US3", 740, 1},
+
107 {"XRP", "US4", 750, 1},
+
108 {"XRP", "US5", 740, 1},
+
109 {"XRP", "US6", 750, 1},
+
110 {"XRP", "US7", 740, 1},
+
111 {"XRP", "US8", 750, 1},
+
112 {"XRP", "US9", 740, 1},
+
113 {"XRP", "U10", 750, 1},
+
114 {"XRP", "U11", 740, 1}},
+
115 .fee = baseFee,
+
116 .err = ter(temARRAY_TOO_LARGE)});
+
117 oracle.set(CreateArg{.series = {}, .fee = baseFee, .err = ter(temARRAY_EMPTY)});
+
118 }
+
119
+
120 // Array of token pair exceeds 10 after update
+
121 {
+
122 Env env{*this};
+
123 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
124 env.fund(XRP(1'000), owner);
+
125
+
126 Oracle oracle(env, CreateArg{.owner = owner, .series = {{{"XRP", "USD", 740, 1}}}, .fee = baseFee});
+
127 oracle.set(
+
128 UpdateArg{
+
129 .series =
+
130 {
+
131 {"XRP", "US1", 740, 1},
+
132 {"XRP", "US2", 750, 1},
+
133 {"XRP", "US3", 740, 1},
+
134 {"XRP", "US4", 750, 1},
+
135 {"XRP", "US5", 740, 1},
+
136 {"XRP", "US6", 750, 1},
+
137 {"XRP", "US7", 740, 1},
+
138 {"XRP", "US8", 750, 1},
+
139 {"XRP", "US9", 740, 1},
+
140 {"XRP", "U10", 750, 1},
+
141 },
+
142 .fee = baseFee,
+
143 .err = ter(tecARRAY_TOO_LARGE)});
+
144 }
+
145
+
146 {
+
147 Env env(*this);
+
148 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
149 env.fund(XRP(1'000), owner);
+
150 Oracle oracle(env, {.owner = owner, .fee = baseFee}, false);
+
151
+
152 // Asset class or provider not included on create
+
153 oracle.set(
+
154 CreateArg{
+
155 .assetClass = std::nullopt, .provider = "provider", .fee = baseFee, .err = ter(temMALFORMED)});
+
156 oracle.set(
+
157 CreateArg{
+
158 .assetClass = "currency",
+
159 .provider = std::nullopt,
+
160 .uri = "URI",
+
161 .fee = baseFee,
+
162 .err = ter(temMALFORMED)});
+
163
+
164 // Asset class or provider are included on update
+
165 // and don't match the current values
+
166 oracle.set(CreateArg{.fee = static_cast<int>(env.current()->fees().base.drops())});
+
167 BEAST_EXPECT(oracle.exists());
+
168 oracle.set(
+
169 UpdateArg{
+
170 .series = {{"XRP", "USD", 740, 1}},
+
171 .provider = "provider1",
+
172 .fee = baseFee,
+
173 .err = ter(temMALFORMED)});
+
174 oracle.set(
+
175 UpdateArg{
+
176 .series = {{"XRP", "USD", 740, 1}},
+
177 .assetClass = "currency1",
+
178 .fee = baseFee,
+
179 .err = ter(temMALFORMED)});
+
180 }
+
181
+
182 {
+
183 Env env(*this);
+
184 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
185 env.fund(XRP(1'000), owner);
+
186 Oracle oracle(env, {.owner = owner, .fee = baseFee}, false);
+
187
+
188 // Fields too long
+
189 // Asset class
+
190 std::string assetClass(17, '0');
+
191 oracle.set(CreateArg{.assetClass = assetClass, .fee = baseFee, .err = ter(temMALFORMED)});
+
192 // provider
+
193 std::string const large(257, '0');
+
194 oracle.set(CreateArg{.provider = large, .fee = baseFee, .err = ter(temMALFORMED)});
+
195 // URI
+
196 oracle.set(CreateArg{.uri = large, .fee = baseFee, .err = ter(temMALFORMED)});
+
197 // Empty field
+
198 // Asset class
+
199 oracle.set(CreateArg{.assetClass = "", .fee = baseFee, .err = ter(temMALFORMED)});
+
200 // provider
+
201 oracle.set(CreateArg{.provider = "", .fee = baseFee, .err = ter(temMALFORMED)});
+
202 // URI
+
203 oracle.set(CreateArg{.uri = "", .fee = baseFee, .err = ter(temMALFORMED)});
+
204 }
+
205
+
206 {
+
207 // Different owner creates a new object and fails because
+
208 // of missing fields currency/provider
+
209 Env env(*this);
+
210 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
211 Account const some("some");
212 env.fund(XRP(1'000), owner);
-
213 Oracle oracle(env, {.owner = owner, .fee = baseFee});
-
214 BEAST_EXPECT(oracle.exists());
-
215 env.close(seconds(400));
-
216 // Less than the last close time - 300s
-
217 oracle.set(UpdateArg{
-
218 .series = {{"XRP", "USD", 740, 1}},
-
219 .lastUpdateTime = static_cast<std::uint32_t>(closeTime() - 301),
-
220 .fee = baseFee,
-
221 .err = ter(tecINVALID_UPDATE_TIME)});
-
222 // Greater than last close time + 300s
-
223 oracle.set(UpdateArg{
-
224 .series = {{"XRP", "USD", 740, 1}},
-
225 .lastUpdateTime = static_cast<std::uint32_t>(closeTime() + 311),
-
226 .fee = baseFee,
-
227 .err = ter(tecINVALID_UPDATE_TIME)});
-
228 oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 1}}, .fee = baseFee});
-
229 BEAST_EXPECT(oracle.expectLastUpdateTime(static_cast<std::uint32_t>(testStartTime.count() + 450)));
-
230 // Less than the previous lastUpdateTime
-
231 oracle.set(UpdateArg{
-
232 .series = {{"XRP", "USD", 740, 1}},
-
233 .lastUpdateTime = static_cast<std::uint32_t>(449),
-
234 .fee = baseFee,
-
235 .err = ter(tecINVALID_UPDATE_TIME)});
-
236 // Less than the epoch time
-
237 oracle.set(UpdateArg{
-
238 .series = {{"XRP", "USD", 740, 1}},
-
239 .lastUpdateTime = static_cast<int>(epoch_offset.count() - 1),
-
240 .fee = baseFee,
-
241 .err = ter(tecINVALID_UPDATE_TIME)});
-
242 }
-
243
-
244 {
-
245 // delete token pair that doesn't exist
-
246 Env env(*this);
-
247 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
248 env.fund(XRP(1'000), owner);
-
249 Oracle oracle(env, {.owner = owner, .fee = baseFee});
-
250 BEAST_EXPECT(oracle.exists());
-
251 oracle.set(UpdateArg{
-
252 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
-
253 .fee = baseFee,
- -
255 // delete all token pairs
-
256 oracle.set(UpdateArg{
-
257 .series = {{"XRP", "USD", std::nullopt, std::nullopt}}, .fee = baseFee, .err = ter(tecARRAY_EMPTY)});
-
258 }
-
259
-
260 {
-
261 // same BaseAsset and QuoteAsset
-
262 Env env(*this);
-
263 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
264 env.fund(XRP(1'000), owner);
-
265 Oracle oracle(
-
266 env, {.owner = owner, .series = {{"USD", "USD", 740, 1}}, .fee = baseFee, .err = ter(temMALFORMED)});
-
267 }
-
268
-
269 {
-
270 // Scale is greater than maxPriceScale
-
271 Env env(*this);
-
272 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
273 env.fund(XRP(1'000), owner);
-
274 Oracle oracle(
-
275 env,
-
276 {.owner = owner,
-
277 .series = {{"USD", "BTC", 740, maxPriceScale + 1}},
-
278 .fee = baseFee,
-
279 .err = ter(temMALFORMED)});
-
280 }
-
281
-
282 {
-
283 // Updating token pair to add and delete
-
284 Env env(*this);
-
285 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
286 env.fund(XRP(1'000), owner);
-
287 Oracle oracle(env, {.owner = owner, .fee = baseFee});
-
288 oracle.set(UpdateArg{
-
289 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}, {"XRP", "EUR", 740, 1}},
-
290 .fee = baseFee,
-
291 .err = ter(temMALFORMED)});
-
292 // Delete token pair that doesn't exist in this oracle
-
293 oracle.set(UpdateArg{
-
294 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
-
295 .fee = baseFee,
- -
297 // Delete token pair in oracle, which is not in the ledger
-
298 oracle.set(UpdateArg{
-
299 .documentID = 10,
-
300 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
-
301 .fee = baseFee,
-
302 .err = ter(temMALFORMED)});
-
303 }
-
304
-
305 {
-
306 // Bad fee
-
307 Env env(*this);
-
308 env.fund(XRP(1'000), owner);
-
309 Oracle oracle(env, {.owner = owner, .fee = -1, .err = ter(temBAD_FEE)});
-
310 Oracle oracle1(env, {.owner = owner, .fee = static_cast<int>(env.current()->fees().base.drops())});
-
311 oracle.set(UpdateArg{.owner = owner, .fee = -1, .err = ter(temBAD_FEE)});
-
312 }
-
313 }
+
213 env.fund(XRP(1'000), some);
+
214 Oracle oracle(env, {.owner = owner, .fee = baseFee});
+
215 BEAST_EXPECT(oracle.exists());
+
216 oracle.set(
+
217 UpdateArg{.owner = some, .series = {{"XRP", "USD", 740, 1}}, .fee = baseFee, .err = ter(temMALFORMED)});
+
218 }
+
219
+
220 {
+
221 // Invalid update time
+
222 using namespace std::chrono;
+
223 Env env(*this);
+
224 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
225 auto closeTime = [&]() {
+
226 return duration_cast<seconds>(env.current()->header().closeTime.time_since_epoch() - 10'000s).count();
+
227 };
+
228 env.fund(XRP(1'000), owner);
+
229 Oracle oracle(env, {.owner = owner, .fee = baseFee});
+
230 BEAST_EXPECT(oracle.exists());
+
231 env.close(seconds(400));
+
232 // Less than the last close time - 300s
+
233 oracle.set(
+
234 UpdateArg{
+
235 .series = {{"XRP", "USD", 740, 1}},
+
236 .lastUpdateTime = static_cast<std::uint32_t>(closeTime() - 301),
+
237 .fee = baseFee,
+
238 .err = ter(tecINVALID_UPDATE_TIME)});
+
239 // Greater than last close time + 300s
+
240 oracle.set(
+
241 UpdateArg{
+
242 .series = {{"XRP", "USD", 740, 1}},
+
243 .lastUpdateTime = static_cast<std::uint32_t>(closeTime() + 311),
+
244 .fee = baseFee,
+
245 .err = ter(tecINVALID_UPDATE_TIME)});
+
246 oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 1}}, .fee = baseFee});
+
247 BEAST_EXPECT(oracle.expectLastUpdateTime(static_cast<std::uint32_t>(testStartTime.count() + 450)));
+
248 // Less than the previous lastUpdateTime
+
249 oracle.set(
+
250 UpdateArg{
+
251 .series = {{"XRP", "USD", 740, 1}},
+
252 .lastUpdateTime = static_cast<std::uint32_t>(449),
+
253 .fee = baseFee,
+
254 .err = ter(tecINVALID_UPDATE_TIME)});
+
255 // Less than the epoch time
+
256 oracle.set(
+
257 UpdateArg{
+
258 .series = {{"XRP", "USD", 740, 1}},
+
259 .lastUpdateTime = static_cast<int>(epoch_offset.count() - 1),
+
260 .fee = baseFee,
+
261 .err = ter(tecINVALID_UPDATE_TIME)});
+
262 }
+
263
+
264 {
+
265 // delete token pair that doesn't exist
+
266 Env env(*this);
+
267 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
268 env.fund(XRP(1'000), owner);
+
269 Oracle oracle(env, {.owner = owner, .fee = baseFee});
+
270 BEAST_EXPECT(oracle.exists());
+
271 oracle.set(
+
272 UpdateArg{
+
273 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
+
274 .fee = baseFee,
+ +
276 // delete all token pairs
+
277 oracle.set(
+
278 UpdateArg{
+
279 .series = {{"XRP", "USD", std::nullopt, std::nullopt}},
+
280 .fee = baseFee,
+
281 .err = ter(tecARRAY_EMPTY)});
+
282 }
+
283
+
284 {
+
285 // same BaseAsset and QuoteAsset
+
286 Env env(*this);
+
287 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
288 env.fund(XRP(1'000), owner);
+
289 Oracle oracle(
+
290 env, {.owner = owner, .series = {{"USD", "USD", 740, 1}}, .fee = baseFee, .err = ter(temMALFORMED)});
+
291 }
+
292
+
293 {
+
294 // Scale is greater than maxPriceScale
+
295 Env env(*this);
+
296 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
297 env.fund(XRP(1'000), owner);
+
298 Oracle oracle(
+
299 env,
+
300 {.owner = owner,
+
301 .series = {{"USD", "BTC", 740, maxPriceScale + 1}},
+
302 .fee = baseFee,
+
303 .err = ter(temMALFORMED)});
+
304 }
+
305
+
306 {
+
307 // Updating token pair to add and delete
+
308 Env env(*this);
+
309 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
310 env.fund(XRP(1'000), owner);
+
311 Oracle oracle(env, {.owner = owner, .fee = baseFee});
+
312 oracle.set(
+
313 UpdateArg{
+
314 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}, {"XRP", "EUR", 740, 1}},
+
315 .fee = baseFee,
+
316 .err = ter(temMALFORMED)});
+
317 // Delete token pair that doesn't exist in this oracle
+
318 oracle.set(
+
319 UpdateArg{
+
320 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
+
321 .fee = baseFee,
+ +
323 // Delete token pair in oracle, which is not in the ledger
+
324 oracle.set(
+
325 UpdateArg{
+
326 .documentID = 10,
+
327 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
+
328 .fee = baseFee,
+
329 .err = ter(temMALFORMED)});
+
330 }
+
331
+
332 {
+
333 // Bad fee
+
334 Env env(*this);
+
335 env.fund(XRP(1'000), owner);
+
336 Oracle oracle(env, {.owner = owner, .fee = -1, .err = ter(temBAD_FEE)});
+
337 Oracle oracle1(env, {.owner = owner, .fee = static_cast<int>(env.current()->fees().base.drops())});
+
338 oracle.set(UpdateArg{.owner = owner, .fee = -1, .err = ter(temBAD_FEE)});
+
339 }
+
340 }
-
314
-
315 void
-
- -
317 {
-
318 testcase("Create");
-
319 using namespace jtx;
-
320 Account const owner("owner");
-
321
-
322 auto test = [&](Env& env, DataSeries const& series, std::uint16_t adj) {
-
323 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
324 env.fund(XRP(1'000), owner);
-
325 auto const count = ownerCount(env, owner);
-
326 Oracle oracle(env, {.owner = owner, .series = series, .fee = baseFee});
-
327 BEAST_EXPECT(oracle.exists());
-
328 BEAST_EXPECT(ownerCount(env, owner) == (count + adj));
-
329 auto const entry = oracle.ledgerEntry();
-
330 BEAST_EXPECT(entry[jss::node][jss::Owner] == owner.human());
-
331 if (features[fixIncludeKeyletFields])
-
332 {
-
333 BEAST_EXPECT(entry[jss::node][jss::OracleDocumentID] == oracle.documentID());
-
334 }
-
335 else
-
336 {
-
337 BEAST_EXPECT(!entry[jss::node].isMember(jss::OracleDocumentID));
-
338 }
-
339 BEAST_EXPECT(oracle.expectLastUpdateTime(946694810));
-
340 };
341
-
342 {
-
343 // owner count is adjusted by 1
-
344 Env env(*this, features);
-
345 test(env, {{"XRP", "USD", 740, 1}}, 1);
-
346 }
-
347
-
348 {
-
349 // owner count is adjusted by 2
-
350 Env env(*this, features);
-
351 test(
-
352 env,
-
353 {{"XRP", "USD", 740, 1},
-
354 {"BTC", "USD", 740, 1},
-
355 {"ETH", "USD", 740, 1},
-
356 {"CAN", "USD", 740, 1},
-
357 {"YAN", "USD", 740, 1},
-
358 {"GBP", "USD", 740, 1}},
-
359 2);
-
360 }
-
361
-
362 {
-
363 // Different owner creates a new object
-
364 Env env(*this, features);
-
365 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
366 Account const some("some");
-
367 env.fund(XRP(1'000), owner);
-
368 env.fund(XRP(1'000), some);
-
369 Oracle oracle(env, {.owner = owner, .fee = baseFee});
-
370 BEAST_EXPECT(oracle.exists());
-
371 oracle.set(CreateArg{.owner = some, .series = {{"912810RR9", "USD", 740, 1}}, .fee = baseFee});
-
372 BEAST_EXPECT(Oracle::exists(env, some, oracle.documentID()));
+
342 void
+
+ +
344 {
+
345 testcase("Create");
+
346 using namespace jtx;
+
347 Account const owner("owner");
+
348
+
349 auto test = [&](Env& env, DataSeries const& series, std::uint16_t adj) {
+
350 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
351 env.fund(XRP(1'000), owner);
+
352 auto const count = ownerCount(env, owner);
+
353 Oracle oracle(env, {.owner = owner, .series = series, .fee = baseFee});
+
354 BEAST_EXPECT(oracle.exists());
+
355 BEAST_EXPECT(ownerCount(env, owner) == (count + adj));
+
356 auto const entry = oracle.ledgerEntry();
+
357 BEAST_EXPECT(entry[jss::node][jss::Owner] == owner.human());
+
358 if (features[fixIncludeKeyletFields])
+
359 {
+
360 BEAST_EXPECT(entry[jss::node][jss::OracleDocumentID] == oracle.documentID());
+
361 }
+
362 else
+
363 {
+
364 BEAST_EXPECT(!entry[jss::node].isMember(jss::OracleDocumentID));
+
365 }
+
366 BEAST_EXPECT(oracle.expectLastUpdateTime(946694810));
+
367 };
+
368
+
369 {
+
370 // owner count is adjusted by 1
+
371 Env env(*this, features);
+
372 test(env, {{"XRP", "USD", 740, 1}}, 1);
373 }
-
374 }
-
-
375
-
376 void
-
- -
378 {
-
379 testcase("Invalid Delete");
-
380
-
381 using namespace jtx;
-
382 Env env(*this);
-
383 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
384 Account const owner("owner");
-
385 env.fund(XRP(1'000), owner);
-
386 Oracle oracle(env, {.owner = owner, .fee = baseFee});
-
387 BEAST_EXPECT(oracle.exists());
+
374
+
375 {
+
376 // owner count is adjusted by 2
+
377 Env env(*this, features);
+
378 test(
+
379 env,
+
380 {{"XRP", "USD", 740, 1},
+
381 {"BTC", "USD", 740, 1},
+
382 {"ETH", "USD", 740, 1},
+
383 {"CAN", "USD", 740, 1},
+
384 {"YAN", "USD", 740, 1},
+
385 {"GBP", "USD", 740, 1}},
+
386 2);
+
387 }
388
389 {
-
390 // Invalid account
-
391 Account const bad("bad");
-
392 env.memoize(bad);
-
393 oracle.remove({.owner = bad, .seq = seq(1), .fee = baseFee, .err = ter(terNO_ACCOUNT)});
-
394 }
-
395
-
396 // Invalid DocumentID
-
397 oracle.remove({.documentID = 2, .fee = baseFee, .err = ter(tecNO_ENTRY)});
-
398
-
399 // Invalid owner
-
400 Account const invalid("invalid");
-
401 env.fund(XRP(1'000), invalid);
-
402 oracle.remove({.owner = invalid, .fee = baseFee, .err = ter(tecNO_ENTRY)});
-
403
-
404 // Invalid flags
-
405 oracle.remove({.flags = tfSellNFToken, .fee = baseFee, .err = ter(temINVALID_FLAG)});
-
406
-
407 // Bad fee
-
408 oracle.remove({.fee = -1, .err = ter(temBAD_FEE)});
-
409 }
+
390 // Different owner creates a new object
+
391 Env env(*this, features);
+
392 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
393 Account const some("some");
+
394 env.fund(XRP(1'000), owner);
+
395 env.fund(XRP(1'000), some);
+
396 Oracle oracle(env, {.owner = owner, .fee = baseFee});
+
397 BEAST_EXPECT(oracle.exists());
+
398 oracle.set(CreateArg{.owner = some, .series = {{"912810RR9", "USD", 740, 1}}, .fee = baseFee});
+
399 BEAST_EXPECT(Oracle::exists(env, some, oracle.documentID()));
+
400 }
+
401 }
-
410
-
411 void
-
- -
413 {
-
414 testcase("Delete");
-
415 using namespace jtx;
-
416 Account const owner("owner");
-
417
-
418 auto test = [&](Env& env, DataSeries const& series, std::uint16_t adj) {
-
419 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
420 env.fund(XRP(1'000), owner);
-
421 Oracle oracle(env, {.owner = owner, .series = series, .fee = baseFee});
-
422 auto const count = ownerCount(env, owner);
-
423 BEAST_EXPECT(oracle.exists());
-
424 oracle.remove({.fee = baseFee});
-
425 BEAST_EXPECT(!oracle.exists());
-
426 BEAST_EXPECT(ownerCount(env, owner) == (count - adj));
-
427 };
-
428
-
429 {
-
430 // owner count is adjusted by 1
-
431 Env env(*this);
-
432 test(env, {{"XRP", "USD", 740, 1}}, 1);
-
433 }
-
434
-
435 {
-
436 // owner count is adjusted by 2
-
437 Env env(*this);
-
438 test(
-
439 env,
-
440 {
-
441 {"XRP", "USD", 740, 1},
-
442 {"BTC", "USD", 740, 1},
-
443 {"ETH", "USD", 740, 1},
-
444 {"CAN", "USD", 740, 1},
-
445 {"YAN", "USD", 740, 1},
-
446 {"GBP", "USD", 740, 1},
-
447 },
-
448 2);
-
449 }
-
450
-
451 {
-
452 // deleting the account deletes the oracles
-
453 Env env(*this);
-
454 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
402
+
403 void
+
+ +
405 {
+
406 testcase("Invalid Delete");
+
407
+
408 using namespace jtx;
+
409 Env env(*this);
+
410 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
411 Account const owner("owner");
+
412 env.fund(XRP(1'000), owner);
+
413 Oracle oracle(env, {.owner = owner, .fee = baseFee});
+
414 BEAST_EXPECT(oracle.exists());
+
415
+
416 {
+
417 // Invalid account
+
418 Account const bad("bad");
+
419 env.memoize(bad);
+
420 oracle.remove({.owner = bad, .seq = seq(1), .fee = baseFee, .err = ter(terNO_ACCOUNT)});
+
421 }
+
422
+
423 // Invalid DocumentID
+
424 oracle.remove({.documentID = 2, .fee = baseFee, .err = ter(tecNO_ENTRY)});
+
425
+
426 // Invalid owner
+
427 Account const invalid("invalid");
+
428 env.fund(XRP(1'000), invalid);
+
429 oracle.remove({.owner = invalid, .fee = baseFee, .err = ter(tecNO_ENTRY)});
+
430
+
431 // Invalid flags
+
432 oracle.remove({.flags = tfSellNFToken, .fee = baseFee, .err = ter(temINVALID_FLAG)});
+
433
+
434 // Bad fee
+
435 oracle.remove({.fee = -1, .err = ter(temBAD_FEE)});
+
436 }
+
+
437
+
438 void
+
+ +
440 {
+
441 testcase("Delete");
+
442 using namespace jtx;
+
443 Account const owner("owner");
+
444
+
445 auto test = [&](Env& env, DataSeries const& series, std::uint16_t adj) {
+
446 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
447 env.fund(XRP(1'000), owner);
+
448 Oracle oracle(env, {.owner = owner, .series = series, .fee = baseFee});
+
449 auto const count = ownerCount(env, owner);
+
450 BEAST_EXPECT(oracle.exists());
+
451 oracle.remove({.fee = baseFee});
+
452 BEAST_EXPECT(!oracle.exists());
+
453 BEAST_EXPECT(ownerCount(env, owner) == (count - adj));
+
454 };
455
-
456 auto const alice = Account("alice");
-
457 auto const acctDelFee{drops(env.current()->fees().increment)};
-
458 env.fund(XRP(1'000), owner);
-
459 env.fund(XRP(1'000), alice);
-
460 Oracle oracle(env, {.owner = owner, .series = {{"XRP", "USD", 740, 1}}, .fee = baseFee});
-
461 Oracle oracle1(env, {.owner = owner, .documentID = 2, .series = {{"XRP", "EUR", 740, 1}}, .fee = baseFee});
-
462 BEAST_EXPECT(ownerCount(env, owner) == 2);
-
463 BEAST_EXPECT(oracle.exists());
-
464 BEAST_EXPECT(oracle1.exists());
-
465 auto const index = env.closed()->seq();
-
466 auto const hash = env.closed()->header().hash;
-
467 for (int i = 0; i < 256; ++i)
-
468 env.close();
-
469 env(acctdelete(owner, alice), fee(acctDelFee));
-
470 env.close();
-
471 BEAST_EXPECT(!oracle.exists());
-
472 BEAST_EXPECT(!oracle1.exists());
-
473
-
474 // can still get the oracles via the ledger index or hash
-
475 auto verifyLedgerData = [&](auto const& field, auto const& value) {
-
476 Json::Value jvParams;
-
477 jvParams[field] = value;
-
478 jvParams[jss::binary] = false;
-
479 jvParams[jss::type] = jss::oracle;
-
480 Json::Value jrr = env.rpc("json", "ledger_data", to_string(jvParams));
-
481 BEAST_EXPECT(jrr[jss::result][jss::state].size() == 2);
-
482 };
-
483 verifyLedgerData(jss::ledger_index, index);
-
484 verifyLedgerData(jss::ledger_hash, to_string(hash));
-
485 }
-
486 }
+
456 {
+
457 // owner count is adjusted by 1
+
458 Env env(*this);
+
459 test(env, {{"XRP", "USD", 740, 1}}, 1);
+
460 }
+
461
+
462 {
+
463 // owner count is adjusted by 2
+
464 Env env(*this);
+
465 test(
+
466 env,
+
467 {
+
468 {"XRP", "USD", 740, 1},
+
469 {"BTC", "USD", 740, 1},
+
470 {"ETH", "USD", 740, 1},
+
471 {"CAN", "USD", 740, 1},
+
472 {"YAN", "USD", 740, 1},
+
473 {"GBP", "USD", 740, 1},
+
474 },
+
475 2);
+
476 }
+
477
+
478 {
+
479 // deleting the account deletes the oracles
+
480 Env env(*this);
+
481 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
482
+
483 auto const alice = Account("alice");
+
484 auto const acctDelFee{drops(env.current()->fees().increment)};
+
485 env.fund(XRP(1'000), owner);
+
486 env.fund(XRP(1'000), alice);
+
487 Oracle oracle(env, {.owner = owner, .series = {{"XRP", "USD", 740, 1}}, .fee = baseFee});
+
488 Oracle oracle1(env, {.owner = owner, .documentID = 2, .series = {{"XRP", "EUR", 740, 1}}, .fee = baseFee});
+
489 BEAST_EXPECT(ownerCount(env, owner) == 2);
+
490 BEAST_EXPECT(oracle.exists());
+
491 BEAST_EXPECT(oracle1.exists());
+
492 auto const index = env.closed()->seq();
+
493 auto const hash = env.closed()->header().hash;
+
494 for (int i = 0; i < 256; ++i)
+
495 env.close();
+
496 env(acctdelete(owner, alice), fee(acctDelFee));
+
497 env.close();
+
498 BEAST_EXPECT(!oracle.exists());
+
499 BEAST_EXPECT(!oracle1.exists());
+
500
+
501 // can still get the oracles via the ledger index or hash
+
502 auto verifyLedgerData = [&](auto const& field, auto const& value) {
+
503 Json::Value jvParams;
+
504 jvParams[field] = value;
+
505 jvParams[jss::binary] = false;
+
506 jvParams[jss::type] = jss::oracle;
+
507 Json::Value jrr = env.rpc("json", "ledger_data", to_string(jvParams));
+
508 BEAST_EXPECT(jrr[jss::result][jss::state].size() == 2);
+
509 };
+
510 verifyLedgerData(jss::ledger_index, index);
+
511 verifyLedgerData(jss::ledger_hash, to_string(hash));
+
512 }
+
513 }
-
487
-
488 void
-
- -
490 {
-
491 testcase("Update");
-
492 using namespace jtx;
-
493 Account const owner("owner");
-
494
-
495 {
-
496 Env env(*this);
-
497 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
498 env.fund(XRP(1'000), owner);
-
499 auto count = ownerCount(env, owner);
-
500 Oracle oracle(env, {.owner = owner, .fee = baseFee});
-
501 BEAST_EXPECT(oracle.exists());
-
502
-
503 // update existing pair
-
504 oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 2}}, .fee = baseFee});
-
505 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 740, 2}}));
-
506 // owner count is increased by 1 since the oracle object is added
-
507 // with one token pair
-
508 count += 1;
-
509 BEAST_EXPECT(ownerCount(env, owner) == count);
-
510
-
511 // add new pairs, not-included pair is reset
-
512 oracle.set(UpdateArg{.series = {{"XRP", "EUR", 700, 2}}, .fee = baseFee});
-
513 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 0, 0}, {"XRP", "EUR", 700, 2}}));
-
514 // owner count is not changed since the number of pairs is 2
-
515 BEAST_EXPECT(ownerCount(env, owner) == count);
-
516
-
517 // update both pairs
-
518 oracle.set(UpdateArg{.series = {{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}}, .fee = baseFee});
-
519 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}}));
-
520 // owner count is not changed since the number of pairs is 2
-
521 BEAST_EXPECT(ownerCount(env, owner) == count);
-
522
-
523 // owner count is increased by 1 since the number of pairs is 6
-
524 oracle.set(UpdateArg{
-
525 .series =
-
526 {
-
527 {"BTC", "USD", 741, 2},
-
528 {"ETH", "EUR", 710, 2},
-
529 {"YAN", "EUR", 710, 2},
-
530 {"CAN", "EUR", 710, 2},
-
531 },
-
532 .fee = baseFee});
-
533 count += 1;
-
534 BEAST_EXPECT(ownerCount(env, owner) == count);
-
535
-
536 // update two pairs and delete four
-
537 oracle.set(UpdateArg{.series = {{"BTC", "USD", std::nullopt, std::nullopt}}, .fee = baseFee});
-
538 oracle.set(UpdateArg{
-
539 .series =
-
540 {{"XRP", "USD", 742, 2},
-
541 {"XRP", "EUR", 711, 2},
-
542 {"ETH", "EUR", std::nullopt, std::nullopt},
-
543 {"YAN", "EUR", std::nullopt, std::nullopt},
-
544 {"CAN", "EUR", std::nullopt, std::nullopt}},
-
545 .fee = baseFee});
-
546 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 742, 2}, {"XRP", "EUR", 711, 2}}));
-
547 // owner count is decreased by 1 since the number of pairs is 2
-
548 count -= 1;
-
549 BEAST_EXPECT(ownerCount(env, owner) == count);
-
550 }
-
551
-
552 // Min reserve to create and update
-
553 {
-
554 Env env(*this);
-
555 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
556 env.fund(env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner);
-
557 Oracle oracle(env, {.owner = owner, .fee = baseFee});
-
558 oracle.set(UpdateArg{.series = {{"XRP", "USD", 742, 2}}, .fee = baseFee});
-
559 }
-
560
-
561 for (bool const withFixOrder : {false, true})
-
562 {
-
563 // Should be same order as creation
-
564 Env env(*this, withFixOrder ? testable_amendments() : testable_amendments() - fixPriceOracleOrder);
-
565 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
566
-
567 auto test = [&](Env& env, DataSeries const& series) {
-
568 env.fund(XRP(1'000), owner);
-
569 Oracle oracle(env, {.owner = owner, .series = series, .fee = baseFee});
-
570 BEAST_EXPECT(oracle.exists());
-
571 auto sle = env.le(keylet::oracle(owner, oracle.documentID()));
-
572 BEAST_EXPECT(sle->getFieldArray(sfPriceDataSeries).size() == series.size());
-
573
-
574 auto const beforeQuoteAssetName1 =
-
575 sle->getFieldArray(sfPriceDataSeries)[0].getFieldCurrency(sfQuoteAsset).getText();
-
576 auto const beforeQuoteAssetName2 =
-
577 sle->getFieldArray(sfPriceDataSeries)[1].getFieldCurrency(sfQuoteAsset).getText();
-
578
-
579 oracle.set(UpdateArg{.series = series, .fee = baseFee});
-
580 sle = env.le(keylet::oracle(owner, oracle.documentID()));
-
581
-
582 auto const afterQuoteAssetName1 =
-
583 sle->getFieldArray(sfPriceDataSeries)[0].getFieldCurrency(sfQuoteAsset).getText();
-
584 auto const afterQuoteAssetName2 =
-
585 sle->getFieldArray(sfPriceDataSeries)[1].getFieldCurrency(sfQuoteAsset).getText();
-
586
-
587 if (env.current()->rules().enabled(fixPriceOracleOrder))
-
588 {
-
589 BEAST_EXPECT(afterQuoteAssetName1 == beforeQuoteAssetName1);
-
590 BEAST_EXPECT(afterQuoteAssetName2 == beforeQuoteAssetName2);
-
591 }
-
592 else
-
593 {
-
594 BEAST_EXPECT(afterQuoteAssetName1 != beforeQuoteAssetName1);
-
595 BEAST_EXPECT(afterQuoteAssetName2 != beforeQuoteAssetName2);
-
596 }
-
597 };
-
598 test(env, {{"XRP", "USD", 742, 2}, {"XRP", "EUR", 711, 2}});
-
599 }
-
600 }
+
514
+
515 void
+
+ +
517 {
+
518 testcase("Update");
+
519 using namespace jtx;
+
520 Account const owner("owner");
+
521
+
522 {
+
523 Env env(*this);
+
524 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
525 env.fund(XRP(1'000), owner);
+
526 auto count = ownerCount(env, owner);
+
527 Oracle oracle(env, {.owner = owner, .fee = baseFee});
+
528 BEAST_EXPECT(oracle.exists());
+
529
+
530 // update existing pair
+
531 oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 2}}, .fee = baseFee});
+
532 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 740, 2}}));
+
533 // owner count is increased by 1 since the oracle object is added
+
534 // with one token pair
+
535 count += 1;
+
536 BEAST_EXPECT(ownerCount(env, owner) == count);
+
537
+
538 // add new pairs, not-included pair is reset
+
539 oracle.set(UpdateArg{.series = {{"XRP", "EUR", 700, 2}}, .fee = baseFee});
+
540 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 0, 0}, {"XRP", "EUR", 700, 2}}));
+
541 // owner count is not changed since the number of pairs is 2
+
542 BEAST_EXPECT(ownerCount(env, owner) == count);
+
543
+
544 // update both pairs
+
545 oracle.set(UpdateArg{.series = {{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}}, .fee = baseFee});
+
546 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}}));
+
547 // owner count is not changed since the number of pairs is 2
+
548 BEAST_EXPECT(ownerCount(env, owner) == count);
+
549
+
550 // owner count is increased by 1 since the number of pairs is 6
+
551 oracle.set(
+
552 UpdateArg{
+
553 .series =
+
554 {
+
555 {"BTC", "USD", 741, 2},
+
556 {"ETH", "EUR", 710, 2},
+
557 {"YAN", "EUR", 710, 2},
+
558 {"CAN", "EUR", 710, 2},
+
559 },
+
560 .fee = baseFee});
+
561 count += 1;
+
562 BEAST_EXPECT(ownerCount(env, owner) == count);
+
563
+
564 // update two pairs and delete four
+
565 oracle.set(UpdateArg{.series = {{"BTC", "USD", std::nullopt, std::nullopt}}, .fee = baseFee});
+
566 oracle.set(
+
567 UpdateArg{
+
568 .series =
+
569 {{"XRP", "USD", 742, 2},
+
570 {"XRP", "EUR", 711, 2},
+
571 {"ETH", "EUR", std::nullopt, std::nullopt},
+
572 {"YAN", "EUR", std::nullopt, std::nullopt},
+
573 {"CAN", "EUR", std::nullopt, std::nullopt}},
+
574 .fee = baseFee});
+
575 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 742, 2}, {"XRP", "EUR", 711, 2}}));
+
576 // owner count is decreased by 1 since the number of pairs is 2
+
577 count -= 1;
+
578 BEAST_EXPECT(ownerCount(env, owner) == count);
+
579 }
+
580
+
581 // Min reserve to create and update
+
582 {
+
583 Env env(*this);
+
584 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
585 env.fund(env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner);
+
586 Oracle oracle(env, {.owner = owner, .fee = baseFee});
+
587 oracle.set(UpdateArg{.series = {{"XRP", "USD", 742, 2}}, .fee = baseFee});
+
588 }
+
589
+
590 for (bool const withFixOrder : {false, true})
+
591 {
+
592 // Should be same order as creation
+
593 Env env(*this, withFixOrder ? testable_amendments() : testable_amendments() - fixPriceOracleOrder);
+
594 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
595
+
596 auto test = [&](Env& env, DataSeries const& series) {
+
597 env.fund(XRP(1'000), owner);
+
598 Oracle oracle(env, {.owner = owner, .series = series, .fee = baseFee});
+
599 BEAST_EXPECT(oracle.exists());
+
600 auto sle = env.le(keylet::oracle(owner, oracle.documentID()));
+
601 BEAST_EXPECT(sle->getFieldArray(sfPriceDataSeries).size() == series.size());
+
602
+
603 auto const beforeQuoteAssetName1 =
+
604 sle->getFieldArray(sfPriceDataSeries)[0].getFieldCurrency(sfQuoteAsset).getText();
+
605 auto const beforeQuoteAssetName2 =
+
606 sle->getFieldArray(sfPriceDataSeries)[1].getFieldCurrency(sfQuoteAsset).getText();
+
607
+
608 oracle.set(UpdateArg{.series = series, .fee = baseFee});
+
609 sle = env.le(keylet::oracle(owner, oracle.documentID()));
+
610
+
611 auto const afterQuoteAssetName1 =
+
612 sle->getFieldArray(sfPriceDataSeries)[0].getFieldCurrency(sfQuoteAsset).getText();
+
613 auto const afterQuoteAssetName2 =
+
614 sle->getFieldArray(sfPriceDataSeries)[1].getFieldCurrency(sfQuoteAsset).getText();
+
615
+
616 if (env.current()->rules().enabled(fixPriceOracleOrder))
+
617 {
+
618 BEAST_EXPECT(afterQuoteAssetName1 == beforeQuoteAssetName1);
+
619 BEAST_EXPECT(afterQuoteAssetName2 == beforeQuoteAssetName2);
+
620 }
+
621 else
+
622 {
+
623 BEAST_EXPECT(afterQuoteAssetName1 != beforeQuoteAssetName1);
+
624 BEAST_EXPECT(afterQuoteAssetName2 != beforeQuoteAssetName2);
+
625 }
+
626 };
+
627 test(env, {{"XRP", "USD", 742, 2}, {"XRP", "EUR", 711, 2}});
+
628 }
+
629 }
-
601
-
602 void
-
- -
604 {
-
605 testcase("Multisig");
-
606 using namespace jtx;
-
607 Oracle::setFee(100'000);
-
608
-
609 Env env(*this);
-
610 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
611
-
612 Account const alice{"alice", KeyType::secp256k1};
-
613 Account const bogie{"bogie", KeyType::secp256k1};
-
614 Account const ed{"ed", KeyType::secp256k1};
-
615 Account const becky{"becky", KeyType::ed25519};
-
616 Account const zelda{"zelda", KeyType::secp256k1};
-
617 Account const bob{"bob", KeyType::secp256k1};
-
618 env.fund(XRP(10'000), alice, becky, zelda, ed, bob);
-
619
-
620 // alice uses a regular key with the master disabled.
-
621 Account const alie{"alie", KeyType::secp256k1};
-
622 env(regkey(alice, alie));
-
623 env(fset(alice, asfDisableMaster), sig(alice));
-
624
-
625 // Attach signers to alice.
-
626 env(signers(alice, 2, {{becky, 1}, {bogie, 1}, {ed, 2}}), sig(alie));
-
627 env.close();
-
628
-
629 env.require(owners(alice, 1));
630
-
631 // Create
-
632 // Force close (true) and time advancement because the close time
-
633 // is no longer 0.
-
634 Oracle oracle(env, CreateArg{.owner = alice, .fee = baseFee, .close = true}, false);
-
635 oracle.set(CreateArg{.msig = msig(becky), .fee = baseFee, .err = ter(tefBAD_QUORUM)});
-
636 oracle.set(CreateArg{.msig = msig(zelda), .fee = baseFee, .err = ter(tefBAD_SIGNATURE)});
-
637 oracle.set(CreateArg{.msig = msig(becky, bogie), .fee = baseFee});
-
638 BEAST_EXPECT(oracle.exists());
-
639
-
640 // Update
-
641 oracle.set(UpdateArg{
-
642 .series = {{"XRP", "USD", 740, 1}}, .msig = msig(becky), .fee = baseFee, .err = ter(tefBAD_QUORUM)});
-
643 oracle.set(UpdateArg{
-
644 .series = {{"XRP", "USD", 740, 1}}, .msig = msig(zelda), .fee = baseFee, .err = ter(tefBAD_SIGNATURE)});
-
645 oracle.set(UpdateArg{.series = {{"XRP", "USD", 741, 1}}, .msig = msig(becky, bogie), .fee = baseFee});
-
646 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 741, 1}}));
-
647 // remove the signer list
-
648 env(signers(alice, jtx::none), sig(alie));
-
649 env.close();
-
650 env.require(owners(alice, 1));
-
651 // create new signer list
-
652 env(signers(alice, 2, {{zelda, 1}, {bob, 1}, {ed, 2}}), sig(alie));
-
653 env.close();
-
654 // old list fails
-
655 oracle.set(UpdateArg{
-
656 .series = {{"XRP", "USD", 740, 1}},
-
657 .msig = msig(becky, bogie),
-
658 .fee = baseFee,
-
659 .err = ter(tefBAD_SIGNATURE)});
-
660 // updated list succeeds
-
661 oracle.set(UpdateArg{.series = {{"XRP", "USD", 7412, 2}}, .msig = msig(zelda, bob), .fee = baseFee});
-
662 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 7412, 2}}));
-
663 oracle.set(UpdateArg{.series = {{"XRP", "USD", 74245, 3}}, .msig = msig(ed), .fee = baseFee});
-
664 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 74245, 3}}));
-
665
-
666 // Remove
-
667 oracle.remove({.msig = msig(bob), .fee = baseFee, .err = ter(tefBAD_QUORUM)});
-
668 oracle.remove({.msig = msig(becky), .fee = baseFee, .err = ter(tefBAD_SIGNATURE)});
-
669 oracle.remove({.msig = msig(ed), .fee = baseFee});
-
670 BEAST_EXPECT(!oracle.exists());
-
671 }
-
-
672
-
673 void
-
- -
675 {
-
676 testcase("Amendment");
-
677 using namespace jtx;
-
678
-
679 auto const features = testable_amendments() - featurePriceOracle;
-
680 Account const owner("owner");
-
681 Env env(*this, features);
-
682 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
-
683
-
684 env.fund(XRP(1'000), owner);
-
685 {
-
686 Oracle oracle(env, {.owner = owner, .fee = baseFee, .err = ter(temDISABLED)});
-
687 }
-
688
-
689 {
-
690 Oracle oracle(env, {.owner = owner, .fee = baseFee}, false);
-
691 oracle.remove({.fee = baseFee, .err = ter(temDISABLED)});
-
692 }
-
693 }
-
-
694
-
695public:
-
696 void
-
-
697 run() override
-
698 {
-
699 using namespace jtx;
-
700 auto const all = testable_amendments();
-
701 testInvalidSet();
-
702 testInvalidDelete();
-
703 testCreate(all);
-
704 testCreate(all - fixIncludeKeyletFields);
-
705 testDelete();
-
706 testUpdate();
-
707 testAmendment();
-
708 testMultisig();
-
709 }
-
-
710};
-
-
711
-
712BEAST_DEFINE_TESTSUITE(Oracle, app, xrpl);
-
713
-
714} // namespace oracle
+
631 void
+
+ +
633 {
+
634 testcase("Multisig");
+
635 using namespace jtx;
+
636 Oracle::setFee(100'000);
+
637
+
638 Env env(*this);
+
639 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
+
640
+
641 Account const alice{"alice", KeyType::secp256k1};
+
642 Account const bogie{"bogie", KeyType::secp256k1};
+
643 Account const ed{"ed", KeyType::secp256k1};
+
644 Account const becky{"becky", KeyType::ed25519};
+
645 Account const zelda{"zelda", KeyType::secp256k1};
+
646 Account const bob{"bob", KeyType::secp256k1};
+
647 env.fund(XRP(10'000), alice, becky, zelda, ed, bob);
+
648
+
649 // alice uses a regular key with the master disabled.
+
650 Account const alie{"alie", KeyType::secp256k1};
+
651 env(regkey(alice, alie));
+
652 env(fset(alice, asfDisableMaster), sig(alice));
+
653
+
654 // Attach signers to alice.
+
655 env(signers(alice, 2, {{becky, 1}, {bogie, 1}, {ed, 2}}), sig(alie));
+
656 env.close();
+
657
+
658 env.require(owners(alice, 1));
+
659
+
660 // Create
+
661 // Force close (true) and time advancement because the close time
+
662 // is no longer 0.
+
663 Oracle oracle(env, CreateArg{.owner = alice, .fee = baseFee, .close = true}, false);
+
664 oracle.set(CreateArg{.msig = msig(becky), .fee = baseFee, .err = ter(tefBAD_QUORUM)});
+
665 oracle.set(CreateArg{.msig = msig(zelda), .fee = baseFee, .err = ter(tefBAD_SIGNATURE)});
+
666 oracle.set(CreateArg{.msig = msig(becky, bogie), .fee = baseFee});
+
667 BEAST_EXPECT(oracle.exists());
+
668
+
669 // Update
+
670 oracle.set(
+
671 UpdateArg{
+
672 .series = {{"XRP", "USD", 740, 1}}, .msig = msig(becky), .fee = baseFee, .err = ter(tefBAD_QUORUM)});
+
673 oracle.set(
+
674 UpdateArg{
+
675 .series = {{"XRP", "USD", 740, 1}}, .msig = msig(zelda), .fee = baseFee, .err = ter(tefBAD_SIGNATURE)});
+
676 oracle.set(UpdateArg{.series = {{"XRP", "USD", 741, 1}}, .msig = msig(becky, bogie), .fee = baseFee});
+
677 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 741, 1}}));
+
678 // remove the signer list
+
679 env(signers(alice, jtx::none), sig(alie));
+
680 env.close();
+
681 env.require(owners(alice, 1));
+
682 // create new signer list
+
683 env(signers(alice, 2, {{zelda, 1}, {bob, 1}, {ed, 2}}), sig(alie));
+
684 env.close();
+
685 // old list fails
+
686 oracle.set(
+
687 UpdateArg{
+
688 .series = {{"XRP", "USD", 740, 1}},
+
689 .msig = msig(becky, bogie),
+
690 .fee = baseFee,
+
691 .err = ter(tefBAD_SIGNATURE)});
+
692 // updated list succeeds
+
693 oracle.set(UpdateArg{.series = {{"XRP", "USD", 7412, 2}}, .msig = msig(zelda, bob), .fee = baseFee});
+
694 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 7412, 2}}));
+
695 oracle.set(UpdateArg{.series = {{"XRP", "USD", 74245, 3}}, .msig = msig(ed), .fee = baseFee});
+
696 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 74245, 3}}));
+
697
+
698 // Remove
+
699 oracle.remove({.msig = msig(bob), .fee = baseFee, .err = ter(tefBAD_QUORUM)});
+
700 oracle.remove({.msig = msig(becky), .fee = baseFee, .err = ter(tefBAD_SIGNATURE)});
+
701 oracle.remove({.msig = msig(ed), .fee = baseFee});
+
702 BEAST_EXPECT(!oracle.exists());
+
703 }
+
704
+
705 void
+
+ +
707 {
+
708 testcase("Amendment");
+
709 using namespace jtx;
+
710
+
711 auto const features = testable_amendments() - featurePriceOracle;
+
712 Account const owner("owner");
+
713 Env env(*this, features);
+
714 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
715
-
716} // namespace jtx
-
717
-
718} // namespace test
-
719
-
720} // namespace xrpl
+
716 env.fund(XRP(1'000), owner);
+
717 {
+
718 Oracle oracle(env, {.owner = owner, .fee = baseFee, .err = ter(temDISABLED)});
+
719 }
+
720
+
721 {
+
722 Oracle oracle(env, {.owner = owner, .fee = baseFee}, false);
+
723 oracle.remove({.fee = baseFee, .err = ter(temDISABLED)});
+
724 }
+
725 }
+
+
726
+
727public:
+
728 void
+
+
729 run() override
+
730 {
+
731 using namespace jtx;
+
732 auto const all = testable_amendments();
+
733 testInvalidSet();
+
734 testInvalidDelete();
+
735 testCreate(all);
+
736 testCreate(all - fixIncludeKeyletFields);
+
737 testDelete();
+
738 testUpdate();
+
739 testAmendment();
+
740 testMultisig();
+
741 }
+
+
742};
+
+
743
+
744BEAST_DEFINE_TESTSUITE(Oracle, app, xrpl);
+
745
+
746} // namespace oracle
+
+
747
+
748} // namespace jtx
+
749
+
750} // namespace test
+
751
+
752} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
A testsuite class.
Definition suite.h:51
@@ -853,7 +885,7 @@ $(document).ready(function() { init_codefold(0); });
std::vector< std::tuple< std::string, std::string, std::optional< std::uint32_t >, std::optional< std::uint8_t > > > DataSeries
Definition Oracle.h:35
static constexpr std::chrono::seconds testStartTime
Definition Oracle.h:88
std::uint32_t ownerCount(Env const &env, Account const &account)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition regkey.cpp:10
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
@@ -894,14 +926,14 @@ $(document).ready(function() { init_codefold(0); });
std::optional< AnyValue > uri
Definition Oracle.h:45
-
void run() override
Runs the suite.
- -
void testCreate(FeatureBitset features)
- - +
void run() override
Runs the suite.
+ +
void testCreate(FeatureBitset features)
+ + - - + +
std::optional< AccountID > owner
Definition Oracle.h:58
std::optional< AnyValue > documentID
Definition Oracle.h:59
diff --git a/OverlayImpl_8cpp_source.html b/OverlayImpl_8cpp_source.html index f10de9e6e5..75180ebd90 100644 --- a/OverlayImpl_8cpp_source.html +++ b/OverlayImpl_8cpp_source.html @@ -154,1522 +154,1523 @@ $(document).ready(function() { init_codefold(0); });
61{
62 timer_.expires_after(std::chrono::seconds(1));
-
63 timer_.async_wait(boost::asio::bind_executor(
-
64 overlay_.strand_, std::bind(&Timer::on_timer, shared_from_this(), std::placeholders::_1)));
-
65}
+
63 timer_.async_wait(
+
64 boost::asio::bind_executor(
+
65 overlay_.strand_, std::bind(&Timer::on_timer, shared_from_this(), std::placeholders::_1)));
+
66}
-
66
-
67void
-
- -
69{
-
70 if (ec || stopping_)
-
71 {
-
72 if (ec && ec != boost::asio::error::operation_aborted)
-
73 {
-
74 JLOG(overlay_.journal_.error()) << "on_timer: " << ec.message();
-
75 }
-
76 return;
-
77 }
-
78
-
79 overlay_.m_peerFinder->once_per_second();
-
80 overlay_.sendEndpoints();
-
81 overlay_.autoConnect();
-
82 if (overlay_.app_.config().TX_REDUCE_RELAY_ENABLE)
-
83 overlay_.sendTxQueue();
-
84
-
85 if ((++overlay_.timer_count_ % Tuning::checkIdlePeers) == 0)
-
86 overlay_.deleteIdlePeers();
-
87
-
88 async_wait();
-
89}
+
67
+
68void
+
+ +
70{
+
71 if (ec || stopping_)
+
72 {
+
73 if (ec && ec != boost::asio::error::operation_aborted)
+
74 {
+
75 JLOG(overlay_.journal_.error()) << "on_timer: " << ec.message();
+
76 }
+
77 return;
+
78 }
+
79
+
80 overlay_.m_peerFinder->once_per_second();
+
81 overlay_.sendEndpoints();
+
82 overlay_.autoConnect();
+
83 if (overlay_.app_.config().TX_REDUCE_RELAY_ENABLE)
+
84 overlay_.sendTxQueue();
+
85
+
86 if ((++overlay_.timer_count_ % Tuning::checkIdlePeers) == 0)
+
87 overlay_.deleteIdlePeers();
+
88
+
89 async_wait();
+
90}
-
90
-
91//------------------------------------------------------------------------------
-
92
-
- -
94 Application& app,
-
95 Setup const& setup,
-
96 ServerHandler& serverHandler,
- -
98 Resolver& resolver,
-
99 boost::asio::io_context& io_context,
-
100 BasicConfig const& config,
-
101 beast::insight::Collector::ptr const& collector)
-
102 : app_(app)
-
103 , io_context_(io_context)
-
104 , work_(std::in_place, boost::asio::make_work_guard(io_context_))
-
105 , strand_(boost::asio::make_strand(io_context_))
-
106 , setup_(setup)
-
107 , journal_(app_.journal("Overlay"))
-
108 , serverHandler_(serverHandler)
- -
110 , m_peerFinder(PeerFinder::make_Manager(io_context, stopwatch(), app_.journal("PeerFinder"), config, collector))
-
111 , m_resolver(resolver)
-
112 , next_id_(1)
-
113 , timer_count_(0)
-
114 , slots_(app.logs(), *this, app.config())
-
115 , m_stats(std::bind(&OverlayImpl::collect_metrics, this), collector, [counts = m_traffic.getCounts(), collector]() {
- -
117
-
118 for (auto const& pair : counts)
-
119 ret.emplace(pair.first, TrafficGauges(pair.second.name, collector));
-
120
-
121 return ret;
-
122 }())
-
123{
- -
125}
+
91
+
92//------------------------------------------------------------------------------
+
93
+
+ +
95 Application& app,
+
96 Setup const& setup,
+
97 ServerHandler& serverHandler,
+ +
99 Resolver& resolver,
+
100 boost::asio::io_context& io_context,
+
101 BasicConfig const& config,
+
102 beast::insight::Collector::ptr const& collector)
+
103 : app_(app)
+
104 , io_context_(io_context)
+
105 , work_(std::in_place, boost::asio::make_work_guard(io_context_))
+
106 , strand_(boost::asio::make_strand(io_context_))
+
107 , setup_(setup)
+
108 , journal_(app_.journal("Overlay"))
+
109 , serverHandler_(serverHandler)
+ +
111 , m_peerFinder(PeerFinder::make_Manager(io_context, stopwatch(), app_.journal("PeerFinder"), config, collector))
+
112 , m_resolver(resolver)
+
113 , next_id_(1)
+
114 , timer_count_(0)
+
115 , slots_(app.logs(), *this, app.config())
+
116 , m_stats(std::bind(&OverlayImpl::collect_metrics, this), collector, [counts = m_traffic.getCounts(), collector]() {
+ +
118
+
119 for (auto const& pair : counts)
+
120 ret.emplace(pair.first, TrafficGauges(pair.second.name, collector));
+
121
+
122 return ret;
+
123 }())
+
124{
+ +
126}
-
126
-
127Handoff
-
- -
129 std::unique_ptr<stream_type>&& stream_ptr,
-
130 http_request_type&& request,
-
131 endpoint_type remote_endpoint)
-
132{
-
133 auto const id = next_id_++;
-
134 beast::WrappedSink sink(app_.logs()["Peer"], makePrefix(id));
-
135 beast::Journal journal(sink);
-
136
-
137 Handoff handoff;
-
138 if (processRequest(request, handoff))
-
139 return handoff;
-
140 if (!isPeerUpgrade(request))
-
141 return handoff;
-
142
-
143 handoff.moved = true;
-
144
-
145 JLOG(journal.debug()) << "Peer connection upgrade from " << remote_endpoint;
-
146
-
147 error_code ec;
-
148 auto const local_endpoint(stream_ptr->next_layer().socket().local_endpoint(ec));
-
149 if (ec)
-
150 {
-
151 JLOG(journal.debug()) << remote_endpoint << " failed: " << ec.message();
-
152 return handoff;
-
153 }
-
154
- -
156 if (consumer.disconnect(journal))
-
157 return handoff;
-
158
-
159 auto const [slot, result] = m_peerFinder->new_inbound_slot(
- -
161
-
162 if (slot == nullptr)
-
163 {
-
164 // connection refused either IP limit exceeded or self-connect
-
165 handoff.moved = false;
-
166 JLOG(journal.debug()) << "Peer " << remote_endpoint << " refused, " << to_string(result);
-
167 return handoff;
-
168 }
-
169
-
170 // Validate HTTP request
-
171
-
172 {
-
173 auto const types = beast::rfc2616::split_commas(request["Connect-As"]);
-
174 if (std::find_if(types.begin(), types.end(), [](std::string const& s) { return boost::iequals(s, "peer"); }) ==
-
175 types.end())
-
176 {
-
177 handoff.moved = false;
-
178 handoff.response = makeRedirectResponse(slot, request, remote_endpoint.address());
-
179 handoff.keep_alive = beast::rfc2616::is_keep_alive(request);
-
180 return handoff;
-
181 }
-
182 }
-
183
-
184 auto const negotiatedVersion = negotiateProtocolVersion(request["Upgrade"]);
-
185 if (!negotiatedVersion)
-
186 {
-
187 m_peerFinder->on_closed(slot);
-
188 handoff.moved = false;
-
189 handoff.response =
-
190 makeErrorResponse(slot, request, remote_endpoint.address(), "Unable to agree on a protocol version");
-
191 handoff.keep_alive = false;
-
192 return handoff;
-
193 }
-
194
-
195 auto const sharedValue = makeSharedValue(*stream_ptr, journal);
-
196 if (!sharedValue)
-
197 {
-
198 m_peerFinder->on_closed(slot);
-
199 handoff.moved = false;
-
200 handoff.response = makeErrorResponse(slot, request, remote_endpoint.address(), "Incorrect security cookie");
-
201 handoff.keep_alive = false;
-
202 return handoff;
-
203 }
-
204
-
205 try
-
206 {
-
207 auto publicKey =
-
208 verifyHandshake(request, *sharedValue, setup_.networkID, setup_.public_ip, remote_endpoint.address(), app_);
-
209
-
210 consumer.setPublicKey(publicKey);
-
211
-
212 {
-
213 // The node gets a reserved slot if it is in our cluster
-
214 // or if it has a reservation.
-
215 bool const reserved =
-
216 static_cast<bool>(app_.cluster().member(publicKey)) || app_.peerReservations().contains(publicKey);
-
217 auto const result = m_peerFinder->activate(slot, publicKey, reserved);
-
218 if (result != PeerFinder::Result::success)
-
219 {
-
220 m_peerFinder->on_closed(slot);
-
221 JLOG(journal.debug()) << "Peer " << remote_endpoint << " redirected, " << to_string(result);
-
222 handoff.moved = false;
-
223 handoff.response = makeRedirectResponse(slot, request, remote_endpoint.address());
-
224 handoff.keep_alive = false;
-
225 return handoff;
-
226 }
-
227 }
-
228
-
229 auto const peer = std::make_shared<PeerImp>(
-
230 app_, id, slot, std::move(request), publicKey, *negotiatedVersion, consumer, std::move(stream_ptr), *this);
-
231 {
-
232 // As we are not on the strand, run() must be called
-
233 // while holding the lock, otherwise new I/O can be
-
234 // queued after a call to stop().
-
235 std::lock_guard<decltype(mutex_)> lock(mutex_);
-
236 {
-
237 auto const result = m_peers.emplace(peer->slot(), peer);
-
238 XRPL_ASSERT(result.second, "xrpl::OverlayImpl::onHandoff : peer is inserted");
-
239 (void)result.second;
-
240 }
-
241 list_.emplace(peer.get(), peer);
-
242
-
243 peer->run();
-
244 }
-
245 handoff.moved = true;
-
246 return handoff;
-
247 }
-
248 catch (std::exception const& e)
-
249 {
-
250 JLOG(journal.debug()) << "Peer " << remote_endpoint << " fails handshake (" << e.what() << ")";
-
251
-
252 m_peerFinder->on_closed(slot);
-
253 handoff.moved = false;
-
254 handoff.response = makeErrorResponse(slot, request, remote_endpoint.address(), e.what());
-
255 handoff.keep_alive = false;
-
256 return handoff;
-
257 }
-
258}
+
127
+
128Handoff
+
+ +
130 std::unique_ptr<stream_type>&& stream_ptr,
+
131 http_request_type&& request,
+
132 endpoint_type remote_endpoint)
+
133{
+
134 auto const id = next_id_++;
+
135 beast::WrappedSink sink(app_.logs()["Peer"], makePrefix(id));
+
136 beast::Journal journal(sink);
+
137
+
138 Handoff handoff;
+
139 if (processRequest(request, handoff))
+
140 return handoff;
+
141 if (!isPeerUpgrade(request))
+
142 return handoff;
+
143
+
144 handoff.moved = true;
+
145
+
146 JLOG(journal.debug()) << "Peer connection upgrade from " << remote_endpoint;
+
147
+
148 error_code ec;
+
149 auto const local_endpoint(stream_ptr->next_layer().socket().local_endpoint(ec));
+
150 if (ec)
+
151 {
+
152 JLOG(journal.debug()) << remote_endpoint << " failed: " << ec.message();
+
153 return handoff;
+
154 }
+
155
+ +
157 if (consumer.disconnect(journal))
+
158 return handoff;
+
159
+
160 auto const [slot, result] = m_peerFinder->new_inbound_slot(
+ +
162
+
163 if (slot == nullptr)
+
164 {
+
165 // connection refused either IP limit exceeded or self-connect
+
166 handoff.moved = false;
+
167 JLOG(journal.debug()) << "Peer " << remote_endpoint << " refused, " << to_string(result);
+
168 return handoff;
+
169 }
+
170
+
171 // Validate HTTP request
+
172
+
173 {
+
174 auto const types = beast::rfc2616::split_commas(request["Connect-As"]);
+
175 if (std::find_if(types.begin(), types.end(), [](std::string const& s) { return boost::iequals(s, "peer"); }) ==
+
176 types.end())
+
177 {
+
178 handoff.moved = false;
+
179 handoff.response = makeRedirectResponse(slot, request, remote_endpoint.address());
+
180 handoff.keep_alive = beast::rfc2616::is_keep_alive(request);
+
181 return handoff;
+
182 }
+
183 }
+
184
+
185 auto const negotiatedVersion = negotiateProtocolVersion(request["Upgrade"]);
+
186 if (!negotiatedVersion)
+
187 {
+
188 m_peerFinder->on_closed(slot);
+
189 handoff.moved = false;
+
190 handoff.response =
+
191 makeErrorResponse(slot, request, remote_endpoint.address(), "Unable to agree on a protocol version");
+
192 handoff.keep_alive = false;
+
193 return handoff;
+
194 }
+
195
+
196 auto const sharedValue = makeSharedValue(*stream_ptr, journal);
+
197 if (!sharedValue)
+
198 {
+
199 m_peerFinder->on_closed(slot);
+
200 handoff.moved = false;
+
201 handoff.response = makeErrorResponse(slot, request, remote_endpoint.address(), "Incorrect security cookie");
+
202 handoff.keep_alive = false;
+
203 return handoff;
+
204 }
+
205
+
206 try
+
207 {
+
208 auto publicKey =
+
209 verifyHandshake(request, *sharedValue, setup_.networkID, setup_.public_ip, remote_endpoint.address(), app_);
+
210
+
211 consumer.setPublicKey(publicKey);
+
212
+
213 {
+
214 // The node gets a reserved slot if it is in our cluster
+
215 // or if it has a reservation.
+
216 bool const reserved =
+
217 static_cast<bool>(app_.cluster().member(publicKey)) || app_.peerReservations().contains(publicKey);
+
218 auto const result = m_peerFinder->activate(slot, publicKey, reserved);
+
219 if (result != PeerFinder::Result::success)
+
220 {
+
221 m_peerFinder->on_closed(slot);
+
222 JLOG(journal.debug()) << "Peer " << remote_endpoint << " redirected, " << to_string(result);
+
223 handoff.moved = false;
+
224 handoff.response = makeRedirectResponse(slot, request, remote_endpoint.address());
+
225 handoff.keep_alive = false;
+
226 return handoff;
+
227 }
+
228 }
+
229
+
230 auto const peer = std::make_shared<PeerImp>(
+
231 app_, id, slot, std::move(request), publicKey, *negotiatedVersion, consumer, std::move(stream_ptr), *this);
+
232 {
+
233 // As we are not on the strand, run() must be called
+
234 // while holding the lock, otherwise new I/O can be
+
235 // queued after a call to stop().
+
236 std::lock_guard<decltype(mutex_)> lock(mutex_);
+
237 {
+
238 auto const result = m_peers.emplace(peer->slot(), peer);
+
239 XRPL_ASSERT(result.second, "xrpl::OverlayImpl::onHandoff : peer is inserted");
+
240 (void)result.second;
+
241 }
+
242 list_.emplace(peer.get(), peer);
+
243
+
244 peer->run();
+
245 }
+
246 handoff.moved = true;
+
247 return handoff;
+
248 }
+
249 catch (std::exception const& e)
+
250 {
+
251 JLOG(journal.debug()) << "Peer " << remote_endpoint << " fails handshake (" << e.what() << ")";
+
252
+
253 m_peerFinder->on_closed(slot);
+
254 handoff.moved = false;
+
255 handoff.response = makeErrorResponse(slot, request, remote_endpoint.address(), e.what());
+
256 handoff.keep_alive = false;
+
257 return handoff;
+
258 }
+
259}
-
259
-
260//------------------------------------------------------------------------------
-
261
-
262bool
-
- -
264{
-
265 if (!is_upgrade(request))
-
266 return false;
-
267 auto const versions = parseProtocolVersions(request["Upgrade"]);
-
268 return !versions.empty();
-
269}
+
260
+
261//------------------------------------------------------------------------------
+
262
+
263bool
+
+ +
265{
+
266 if (!is_upgrade(request))
+
267 return false;
+
268 auto const versions = parseProtocolVersions(request["Upgrade"]);
+
269 return !versions.empty();
+
270}
-
270
- -
- -
273{
- -
275 ss << "[" << std::setfill('0') << std::setw(3) << id << "] ";
-
276 return ss.str();
-
277}
+
271
+ +
+ +
274{
+ +
276 ss << "[" << std::setfill('0') << std::setw(3) << id << "] ";
+
277 return ss.str();
+
278}
-
278
- -
- - -
282 http_request_type const& request,
-
283 address_type remote_address)
-
284{
-
285 boost::beast::http::response<json_body> msg;
-
286 msg.version(request.version());
-
287 msg.result(boost::beast::http::status::service_unavailable);
-
288 msg.insert("Server", BuildInfo::getFullVersionString());
-
289 {
- -
291 ostr << remote_address;
-
292 msg.insert("Remote-Address", ostr.str());
-
293 }
-
294 msg.insert("Content-Type", "application/json");
-
295 msg.insert(boost::beast::http::field::connection, "close");
-
296 msg.body() = Json::objectValue;
-
297 {
-
298 Json::Value& ips = (msg.body()["peer-ips"] = Json::arrayValue);
-
299 for (auto const& _ : m_peerFinder->redirect(slot))
-
300 ips.append(_.address.to_string());
-
301 }
-
302 msg.prepare_payload();
- -
304}
+
279
+ +
+ + +
283 http_request_type const& request,
+
284 address_type remote_address)
+
285{
+
286 boost::beast::http::response<json_body> msg;
+
287 msg.version(request.version());
+
288 msg.result(boost::beast::http::status::service_unavailable);
+
289 msg.insert("Server", BuildInfo::getFullVersionString());
+
290 {
+ +
292 ostr << remote_address;
+
293 msg.insert("Remote-Address", ostr.str());
+
294 }
+
295 msg.insert("Content-Type", "application/json");
+
296 msg.insert(boost::beast::http::field::connection, "close");
+
297 msg.body() = Json::objectValue;
+
298 {
+
299 Json::Value& ips = (msg.body()["peer-ips"] = Json::arrayValue);
+
300 for (auto const& _ : m_peerFinder->redirect(slot))
+
301 ips.append(_.address.to_string());
+
302 }
+
303 msg.prepare_payload();
+ +
305}
-
305
- -
- - -
309 http_request_type const& request,
-
310 address_type remote_address,
-
311 std::string text)
-
312{
-
313 boost::beast::http::response<boost::beast::http::empty_body> msg;
-
314 msg.version(request.version());
-
315 msg.result(boost::beast::http::status::bad_request);
-
316 msg.reason("Bad Request (" + text + ")");
-
317 msg.insert("Server", BuildInfo::getFullVersionString());
-
318 msg.insert("Remote-Address", remote_address.to_string());
-
319 msg.insert(boost::beast::http::field::connection, "close");
-
320 msg.prepare_payload();
- -
322}
+
306
+ +
+ + +
310 http_request_type const& request,
+
311 address_type remote_address,
+
312 std::string text)
+
313{
+
314 boost::beast::http::response<boost::beast::http::empty_body> msg;
+
315 msg.version(request.version());
+
316 msg.result(boost::beast::http::status::bad_request);
+
317 msg.reason("Bad Request (" + text + ")");
+
318 msg.insert("Server", BuildInfo::getFullVersionString());
+
319 msg.insert("Remote-Address", remote_address.to_string());
+
320 msg.insert(boost::beast::http::field::connection, "close");
+
321 msg.prepare_payload();
+ +
323}
-
323
-
324//------------------------------------------------------------------------------
-
325
-
326void
-
- -
328{
-
329 XRPL_ASSERT(work_, "xrpl::OverlayImpl::connect : work is set");
-
330
-
331 auto usage = resourceManager().newOutboundEndpoint(remote_endpoint);
-
332 if (usage.disconnect(journal_))
-
333 {
-
334 JLOG(journal_.info()) << "Over resource limit: " << remote_endpoint;
-
335 return;
-
336 }
-
337
-
338 auto const [slot, result] = peerFinder().new_outbound_slot(remote_endpoint);
-
339 if (slot == nullptr)
-
340 {
-
341 JLOG(journal_.debug()) << "Connect: No slot for " << remote_endpoint << ": " << to_string(result);
-
342 return;
-
343 }
-
344
- -
346 app_,
- - -
349 usage,
- -
351 next_id_++,
-
352 slot,
-
353 app_.journal("Peer"),
-
354 *this);
-
355
- -
357 list_.emplace(p.get(), p);
-
358 p->run();
-
359}
+
324
+
325//------------------------------------------------------------------------------
+
326
+
327void
+
+ +
329{
+
330 XRPL_ASSERT(work_, "xrpl::OverlayImpl::connect : work is set");
+
331
+
332 auto usage = resourceManager().newOutboundEndpoint(remote_endpoint);
+
333 if (usage.disconnect(journal_))
+
334 {
+
335 JLOG(journal_.info()) << "Over resource limit: " << remote_endpoint;
+
336 return;
+
337 }
+
338
+
339 auto const [slot, result] = peerFinder().new_outbound_slot(remote_endpoint);
+
340 if (slot == nullptr)
+
341 {
+
342 JLOG(journal_.debug()) << "Connect: No slot for " << remote_endpoint << ": " << to_string(result);
+
343 return;
+
344 }
+
345
+ +
347 app_,
+ + +
350 usage,
+ +
352 next_id_++,
+
353 slot,
+
354 app_.journal("Peer"),
+
355 *this);
+
356
+ +
358 list_.emplace(p.get(), p);
+
359 p->run();
+
360}
-
360
-
361//------------------------------------------------------------------------------
-
362
-
363// Adds a peer that is already handshaked and active
-
364void
-
- -
366{
-
367 beast::WrappedSink sink{journal_.sink(), peer->prefix()};
-
368 beast::Journal journal{sink};
-
369
- -
371
-
372 {
-
373 auto const result = m_peers.emplace(peer->slot(), peer);
-
374 XRPL_ASSERT(result.second, "xrpl::OverlayImpl::add_active : peer is inserted");
-
375 (void)result.second;
-
376 }
-
377
-
378 {
-
379 auto const result = ids_.emplace(std::piecewise_construct, std::make_tuple(peer->id()), std::make_tuple(peer));
-
380 XRPL_ASSERT(result.second, "xrpl::OverlayImpl::add_active : peer ID is inserted");
-
381 (void)result.second;
-
382 }
-
383
-
384 list_.emplace(peer.get(), peer);
-
385
-
386 JLOG(journal.debug()) << "activated";
-
387
-
388 // As we are not on the strand, run() must be called
-
389 // while holding the lock, otherwise new I/O can be
-
390 // queued after a call to stop().
-
391 peer->run();
-
392}
+
361
+
362//------------------------------------------------------------------------------
+
363
+
364// Adds a peer that is already handshaked and active
+
365void
+
+ +
367{
+
368 beast::WrappedSink sink{journal_.sink(), peer->prefix()};
+
369 beast::Journal journal{sink};
+
370
+ +
372
+
373 {
+
374 auto const result = m_peers.emplace(peer->slot(), peer);
+
375 XRPL_ASSERT(result.second, "xrpl::OverlayImpl::add_active : peer is inserted");
+
376 (void)result.second;
+
377 }
+
378
+
379 {
+
380 auto const result = ids_.emplace(std::piecewise_construct, std::make_tuple(peer->id()), std::make_tuple(peer));
+
381 XRPL_ASSERT(result.second, "xrpl::OverlayImpl::add_active : peer ID is inserted");
+
382 (void)result.second;
+
383 }
+
384
+
385 list_.emplace(peer.get(), peer);
+
386
+
387 JLOG(journal.debug()) << "activated";
+
388
+
389 // As we are not on the strand, run() must be called
+
390 // while holding the lock, otherwise new I/O can be
+
391 // queued after a call to stop().
+
392 peer->run();
+
393}
-
393
-
394void
-
- -
396{
- -
398 auto const iter = m_peers.find(slot);
-
399 XRPL_ASSERT(iter != m_peers.end(), "xrpl::OverlayImpl::remove : valid input");
-
400 m_peers.erase(iter);
-
401}
+
394
+
395void
+
+ +
397{
+ +
399 auto const iter = m_peers.find(slot);
+
400 XRPL_ASSERT(iter != m_peers.end(), "xrpl::OverlayImpl::remove : valid input");
+
401 m_peers.erase(iter);
+
402}
-
402
-
403void
-
- -
405{
- -
407 app_.config(),
-
408 serverHandler_.setup().overlay.port(),
-
409 app_.getValidationPublicKey().has_value(),
- -
411
-
412 m_peerFinder->setConfig(config);
-
413 m_peerFinder->start();
-
414
-
415 // Populate our boot cache: if there are no entries in [ips] then we use
-
416 // the entries in [ips_fixed].
-
417 auto bootstrapIps = app_.config().IPS.empty() ? app_.config().IPS_FIXED : app_.config().IPS;
-
418
-
419 // If nothing is specified, default to several well-known high-capacity
-
420 // servers to serve as bootstrap:
-
421 if (bootstrapIps.empty())
-
422 {
-
423 // Pool of servers operated by Ripple Labs Inc. - https://ripple.com
-
424 bootstrapIps.push_back("r.ripple.com 51235");
-
425
-
426 // Pool of servers operated by ISRDC - https://isrdc.in
-
427 bootstrapIps.push_back("sahyadri.isrdc.in 51235");
-
428
-
429 // Pool of servers operated by @Xrpkuwait - https://xrpkuwait.com
-
430 bootstrapIps.push_back("hubs.xrpkuwait.com 51235");
-
431
-
432 // Pool of servers operated by XRPL Commons - https://xrpl-commons.org
-
433 bootstrapIps.push_back("hub.xrpl-commons.org 51235");
-
434 }
-
435
- -
437 bootstrapIps, [this](std::string const& name, std::vector<beast::IP::Endpoint> const& addresses) {
- -
439 ips.reserve(addresses.size());
-
440 for (auto const& addr : addresses)
-
441 {
-
442 if (addr.port() == 0)
-
443 ips.push_back(to_string(addr.at_port(DEFAULT_PEER_PORT)));
-
444 else
-
445 ips.push_back(to_string(addr));
-
446 }
-
447
-
448 std::string const base("config: ");
-
449 if (!ips.empty())
-
450 m_peerFinder->addFallbackStrings(base + name, ips);
-
451 });
-
452
-
453 // Add the ips_fixed from the xrpld.cfg file
- -
455 {
- - -
458 [this](std::string const& name, std::vector<beast::IP::Endpoint> const& addresses) {
- -
460 ips.reserve(addresses.size());
-
461
-
462 for (auto& addr : addresses)
-
463 {
-
464 if (addr.port() == 0)
-
465 ips.emplace_back(addr.address(), DEFAULT_PEER_PORT);
-
466 else
-
467 ips.emplace_back(addr);
-
468 }
-
469
-
470 if (!ips.empty())
-
471 m_peerFinder->addFixedPeer(name, ips);
-
472 });
-
473 }
-
474 auto const timer = std::make_shared<Timer>(*this);
- -
476 list_.emplace(timer.get(), timer);
-
477 timer_ = timer;
-
478 timer->async_wait();
-
479}
+
403
+
404void
+
+ +
406{
+ +
408 app_.config(),
+
409 serverHandler_.setup().overlay.port(),
+
410 app_.getValidationPublicKey().has_value(),
+ +
412
+
413 m_peerFinder->setConfig(config);
+
414 m_peerFinder->start();
+
415
+
416 // Populate our boot cache: if there are no entries in [ips] then we use
+
417 // the entries in [ips_fixed].
+
418 auto bootstrapIps = app_.config().IPS.empty() ? app_.config().IPS_FIXED : app_.config().IPS;
+
419
+
420 // If nothing is specified, default to several well-known high-capacity
+
421 // servers to serve as bootstrap:
+
422 if (bootstrapIps.empty())
+
423 {
+
424 // Pool of servers operated by Ripple Labs Inc. - https://ripple.com
+
425 bootstrapIps.push_back("r.ripple.com 51235");
+
426
+
427 // Pool of servers operated by ISRDC - https://isrdc.in
+
428 bootstrapIps.push_back("sahyadri.isrdc.in 51235");
+
429
+
430 // Pool of servers operated by @Xrpkuwait - https://xrpkuwait.com
+
431 bootstrapIps.push_back("hubs.xrpkuwait.com 51235");
+
432
+
433 // Pool of servers operated by XRPL Commons - https://xrpl-commons.org
+
434 bootstrapIps.push_back("hub.xrpl-commons.org 51235");
+
435 }
+
436
+ +
438 bootstrapIps, [this](std::string const& name, std::vector<beast::IP::Endpoint> const& addresses) {
+ +
440 ips.reserve(addresses.size());
+
441 for (auto const& addr : addresses)
+
442 {
+
443 if (addr.port() == 0)
+
444 ips.push_back(to_string(addr.at_port(DEFAULT_PEER_PORT)));
+
445 else
+
446 ips.push_back(to_string(addr));
+
447 }
+
448
+
449 std::string const base("config: ");
+
450 if (!ips.empty())
+
451 m_peerFinder->addFallbackStrings(base + name, ips);
+
452 });
+
453
+
454 // Add the ips_fixed from the xrpld.cfg file
+ +
456 {
+ + +
459 [this](std::string const& name, std::vector<beast::IP::Endpoint> const& addresses) {
+ +
461 ips.reserve(addresses.size());
+
462
+
463 for (auto& addr : addresses)
+
464 {
+
465 if (addr.port() == 0)
+
466 ips.emplace_back(addr.address(), DEFAULT_PEER_PORT);
+
467 else
+
468 ips.emplace_back(addr);
+
469 }
+
470
+
471 if (!ips.empty())
+
472 m_peerFinder->addFixedPeer(name, ips);
+
473 });
+
474 }
+
475 auto const timer = std::make_shared<Timer>(*this);
+ +
477 list_.emplace(timer.get(), timer);
+
478 timer_ = timer;
+
479 timer->async_wait();
+
480}
-
480
-
481void
-
- -
483{
-
484 boost::asio::dispatch(strand_, std::bind(&OverlayImpl::stopChildren, this));
-
485 {
-
486 std::unique_lock<decltype(mutex_)> lock(mutex_);
-
487 cond_.wait(lock, [this] { return list_.empty(); });
-
488 }
-
489 m_peerFinder->stop();
-
490}
+
481
+
482void
+
+ +
484{
+
485 boost::asio::dispatch(strand_, std::bind(&OverlayImpl::stopChildren, this));
+
486 {
+
487 std::unique_lock<decltype(mutex_)> lock(mutex_);
+
488 cond_.wait(lock, [this] { return list_.empty(); });
+
489 }
+
490 m_peerFinder->stop();
+
491}
-
491
-
492//------------------------------------------------------------------------------
-
493//
-
494// PropertyStream
-
495//
-
496//------------------------------------------------------------------------------
-
497
-
498void
-
- -
500{
-
501 beast::PropertyStream::Set set("traffic", stream);
-
502 auto const stats = m_traffic.getCounts();
-
503 for (auto const& pair : stats)
-
504 {
- -
506 item["category"] = pair.second.name;
-
507 item["bytes_in"] = std::to_string(pair.second.bytesIn.load());
-
508 item["messages_in"] = std::to_string(pair.second.messagesIn.load());
-
509 item["bytes_out"] = std::to_string(pair.second.bytesOut.load());
-
510 item["messages_out"] = std::to_string(pair.second.messagesOut.load());
-
511 }
-
512}
+
492
+
493//------------------------------------------------------------------------------
+
494//
+
495// PropertyStream
+
496//
+
497//------------------------------------------------------------------------------
+
498
+
499void
+
+ +
501{
+
502 beast::PropertyStream::Set set("traffic", stream);
+
503 auto const stats = m_traffic.getCounts();
+
504 for (auto const& pair : stats)
+
505 {
+ +
507 item["category"] = pair.second.name;
+
508 item["bytes_in"] = std::to_string(pair.second.bytesIn.load());
+
509 item["messages_in"] = std::to_string(pair.second.messagesIn.load());
+
510 item["bytes_out"] = std::to_string(pair.second.bytesOut.load());
+
511 item["messages_out"] = std::to_string(pair.second.messagesOut.load());
+
512 }
+
513}
-
513
-
514//------------------------------------------------------------------------------
-
520void
-
- -
522{
-
523 beast::WrappedSink sink{journal_.sink(), peer->prefix()};
-
524 beast::Journal journal{sink};
-
525
-
526 // Now track this peer
-
527 {
- -
529 auto const result(ids_.emplace(std::piecewise_construct, std::make_tuple(peer->id()), std::make_tuple(peer)));
-
530 XRPL_ASSERT(result.second, "xrpl::OverlayImpl::activate : peer ID is inserted");
-
531 (void)result.second;
-
532 }
-
533
-
534 JLOG(journal.debug()) << "activated";
-
535
-
536 // We just accepted this peer so we have non-zero active peers
-
537 XRPL_ASSERT(size(), "xrpl::OverlayImpl::activate : nonzero peers");
-
538}
+
514
+
515//------------------------------------------------------------------------------
+
521void
+
+ +
523{
+
524 beast::WrappedSink sink{journal_.sink(), peer->prefix()};
+
525 beast::Journal journal{sink};
+
526
+
527 // Now track this peer
+
528 {
+ +
530 auto const result(ids_.emplace(std::piecewise_construct, std::make_tuple(peer->id()), std::make_tuple(peer)));
+
531 XRPL_ASSERT(result.second, "xrpl::OverlayImpl::activate : peer ID is inserted");
+
532 (void)result.second;
+
533 }
+
534
+
535 JLOG(journal.debug()) << "activated";
+
536
+
537 // We just accepted this peer so we have non-zero active peers
+
538 XRPL_ASSERT(size(), "xrpl::OverlayImpl::activate : nonzero peers");
+
539}
-
539
-
540void
-
- -
542{
- -
544 ids_.erase(id);
-
545}
+
540
+
541void
+
+ +
543{
+ +
545 ids_.erase(id);
+
546}
-
546
-
547void
-
- -
549{
-
550 auto const n = m->list_size();
-
551 auto const& journal = from->pJournal();
-
552
-
553 protocol::TMManifests relay;
-
554
-
555 for (std::size_t i = 0; i < n; ++i)
-
556 {
-
557 auto& s = m->list().Get(i).stobject();
-
558
-
559 if (auto mo = deserializeManifest(s))
-
560 {
-
561 auto const serialized = mo->serialized;
-
562
-
563 auto const result = app_.validatorManifests().applyManifest(std::move(*mo));
-
564
-
565 if (result == ManifestDisposition::accepted)
-
566 {
-
567 relay.add_list()->set_stobject(s);
-
568
-
569 // N.B.: this is important; the applyManifest call above moves
-
570 // the loaded Manifest out of the optional so we need to
-
571 // reload it here.
-
572 mo = deserializeManifest(serialized);
-
573 XRPL_ASSERT(
-
574 mo,
-
575 "xrpl::OverlayImpl::onManifests : manifest "
-
576 "deserialization succeeded");
-
577
-
578 app_.getOPs().pubManifest(*mo);
-
579
-
580 if (app_.validators().listed(mo->masterKey))
-
581 {
-
582 auto db = app_.getWalletDB().checkoutDb();
-
583 addValidatorManifest(*db, serialized);
-
584 }
-
585 }
-
586 }
-
587 else
-
588 {
-
589 JLOG(journal.debug()) << "Malformed manifest #" << i + 1 << ": " << strHex(s);
-
590 continue;
-
591 }
-
592 }
-
593
-
594 if (!relay.list().empty())
-
595 for_each([m2 = std::make_shared<Message>(relay, protocol::mtMANIFESTS)](std::shared_ptr<PeerImp>&& p) {
-
596 p->send(m2);
-
597 });
-
598}
+
547
+
548void
+
+ +
550{
+
551 auto const n = m->list_size();
+
552 auto const& journal = from->pJournal();
+
553
+
554 protocol::TMManifests relay;
+
555
+
556 for (std::size_t i = 0; i < n; ++i)
+
557 {
+
558 auto& s = m->list().Get(i).stobject();
+
559
+
560 if (auto mo = deserializeManifest(s))
+
561 {
+
562 auto const serialized = mo->serialized;
+
563
+
564 auto const result = app_.validatorManifests().applyManifest(std::move(*mo));
+
565
+
566 if (result == ManifestDisposition::accepted)
+
567 {
+
568 relay.add_list()->set_stobject(s);
+
569
+
570 // N.B.: this is important; the applyManifest call above moves
+
571 // the loaded Manifest out of the optional so we need to
+
572 // reload it here.
+
573 mo = deserializeManifest(serialized);
+
574 XRPL_ASSERT(
+
575 mo,
+
576 "xrpl::OverlayImpl::onManifests : manifest "
+
577 "deserialization succeeded");
+
578
+
579 app_.getOPs().pubManifest(*mo);
+
580
+
581 if (app_.validators().listed(mo->masterKey))
+
582 {
+
583 auto db = app_.getWalletDB().checkoutDb();
+
584 addValidatorManifest(*db, serialized);
+
585 }
+
586 }
+
587 }
+
588 else
+
589 {
+
590 JLOG(journal.debug()) << "Malformed manifest #" << i + 1 << ": " << strHex(s);
+
591 continue;
+
592 }
+
593 }
+
594
+
595 if (!relay.list().empty())
+
596 for_each([m2 = std::make_shared<Message>(relay, protocol::mtMANIFESTS)](std::shared_ptr<PeerImp>&& p) {
+
597 p->send(m2);
+
598 });
+
599}
-
599
-
600void
-
- -
602{
-
603 m_traffic.addCount(cat, true, size);
-
604}
+
600
+
601void
+
+ +
603{
+
604 m_traffic.addCount(cat, true, size);
+
605}
-
605
-
606void
-
- -
608{
-
609 m_traffic.addCount(cat, false, size);
-
610}
+
606
+
607void
+
+ +
609{
+
610 m_traffic.addCount(cat, false, size);
+
611}
- -
- -
617{
- -
619 return ids_.size();
-
620}
+ +
+ +
618{
+ +
620 return ids_.size();
+
621}
-
621
-
622int
-
- -
624{
-
625 return m_peerFinder->config().maxPeers;
-
626}
+
622
+
623int
+
+ +
625{
+
626 return m_peerFinder->config().maxPeers;
+
627}
-
627
- -
- -
630{
-
631 using namespace std::chrono;
-
632 Json::Value jv;
-
633 auto& av = jv["active"] = Json::Value(Json::arrayValue);
-
634
- -
636 auto& pv = av.append(Json::Value(Json::objectValue));
-
637 pv[jss::public_key] = base64_encode(sp->getNodePublic().data(), sp->getNodePublic().size());
-
638 pv[jss::type] = sp->slot()->inbound() ? "in" : "out";
-
639 pv[jss::uptime] = static_cast<std::uint32_t>(duration_cast<seconds>(sp->uptime()).count());
-
640 if (sp->crawl())
-
641 {
-
642 pv[jss::ip] = sp->getRemoteAddress().address().to_string();
-
643 if (sp->slot()->inbound())
-
644 {
-
645 if (auto port = sp->slot()->listening_port())
-
646 pv[jss::port] = *port;
-
647 }
-
648 else
-
649 {
-
650 pv[jss::port] = std::to_string(sp->getRemoteAddress().port());
-
651 }
-
652 }
-
653
-
654 {
-
655 auto version{sp->getVersion()};
-
656 if (!version.empty())
-
657 // Could move here if Json::value supported moving from strings
-
658 pv[jss::version] = std::string{version};
-
659 }
-
660
-
661 std::uint32_t minSeq, maxSeq;
-
662 sp->ledgerRange(minSeq, maxSeq);
-
663 if (minSeq != 0 || maxSeq != 0)
-
664 pv[jss::complete_ledgers] = std::to_string(minSeq) + "-" + std::to_string(maxSeq);
-
665 });
-
666
-
667 return jv;
-
668}
+
628
+ +
+ +
631{
+
632 using namespace std::chrono;
+
633 Json::Value jv;
+
634 auto& av = jv["active"] = Json::Value(Json::arrayValue);
+
635
+ +
637 auto& pv = av.append(Json::Value(Json::objectValue));
+
638 pv[jss::public_key] = base64_encode(sp->getNodePublic().data(), sp->getNodePublic().size());
+
639 pv[jss::type] = sp->slot()->inbound() ? "in" : "out";
+
640 pv[jss::uptime] = static_cast<std::uint32_t>(duration_cast<seconds>(sp->uptime()).count());
+
641 if (sp->crawl())
+
642 {
+
643 pv[jss::ip] = sp->getRemoteAddress().address().to_string();
+
644 if (sp->slot()->inbound())
+
645 {
+
646 if (auto port = sp->slot()->listening_port())
+
647 pv[jss::port] = *port;
+
648 }
+
649 else
+
650 {
+
651 pv[jss::port] = std::to_string(sp->getRemoteAddress().port());
+
652 }
+
653 }
+
654
+
655 {
+
656 auto version{sp->getVersion()};
+
657 if (!version.empty())
+
658 // Could move here if Json::value supported moving from strings
+
659 pv[jss::version] = std::string{version};
+
660 }
+
661
+
662 std::uint32_t minSeq, maxSeq;
+
663 sp->ledgerRange(minSeq, maxSeq);
+
664 if (minSeq != 0 || maxSeq != 0)
+
665 pv[jss::complete_ledgers] = std::to_string(minSeq) + "-" + std::to_string(maxSeq);
+
666 });
+
667
+
668 return jv;
+
669}
-
669
- -
- -
672{
-
673 bool const humanReadable = false;
-
674 bool const admin = false;
-
675 bool const counters = false;
-
676
-
677 Json::Value server_info = app_.getOPs().getServerInfo(humanReadable, admin, counters);
-
678
-
679 // Filter out some information
-
680 server_info.removeMember(jss::hostid);
-
681 server_info.removeMember(jss::load_factor_fee_escalation);
-
682 server_info.removeMember(jss::load_factor_fee_queue);
-
683 server_info.removeMember(jss::validation_quorum);
-
684
-
685 if (server_info.isMember(jss::validated_ledger))
-
686 {
-
687 Json::Value& validated_ledger = server_info[jss::validated_ledger];
-
688
-
689 validated_ledger.removeMember(jss::base_fee);
-
690 validated_ledger.removeMember(jss::reserve_base_xrp);
-
691 validated_ledger.removeMember(jss::reserve_inc_xrp);
-
692 }
-
693
-
694 return server_info;
-
695}
+
670
+ +
+ +
673{
+
674 bool const humanReadable = false;
+
675 bool const admin = false;
+
676 bool const counters = false;
+
677
+
678 Json::Value server_info = app_.getOPs().getServerInfo(humanReadable, admin, counters);
+
679
+
680 // Filter out some information
+
681 server_info.removeMember(jss::hostid);
+
682 server_info.removeMember(jss::load_factor_fee_escalation);
+
683 server_info.removeMember(jss::load_factor_fee_queue);
+
684 server_info.removeMember(jss::validation_quorum);
+
685
+
686 if (server_info.isMember(jss::validated_ledger))
+
687 {
+
688 Json::Value& validated_ledger = server_info[jss::validated_ledger];
+
689
+
690 validated_ledger.removeMember(jss::base_fee);
+
691 validated_ledger.removeMember(jss::reserve_base_xrp);
+
692 validated_ledger.removeMember(jss::reserve_inc_xrp);
+
693 }
+
694
+
695 return server_info;
+
696}
-
696
- -
- -
699{
-
700 return getCountsJson(app_, 10);
-
701}
+
697
+ +
+ +
700{
+
701 return getCountsJson(app_, 10);
+
702}
-
702
- -
- -
705{
-
706 Json::Value validators = app_.validators().getJson();
-
707
-
708 if (validators.isMember(jss::publisher_lists))
-
709 {
-
710 Json::Value& publisher_lists = validators[jss::publisher_lists];
-
711
-
712 for (auto& publisher : publisher_lists)
-
713 {
-
714 publisher.removeMember(jss::list);
-
715 }
-
716 }
-
717
-
718 validators.removeMember(jss::signing_keys);
-
719 validators.removeMember(jss::trusted_validator_keys);
-
720 validators.removeMember(jss::validation_quorum);
-
721
-
722 Json::Value validatorSites = app_.validatorSites().getJson();
-
723
-
724 if (validatorSites.isMember(jss::validator_sites))
-
725 {
-
726 validators[jss::validator_sites] = std::move(validatorSites[jss::validator_sites]);
-
727 }
-
728
-
729 return validators;
-
730}
+
703
+ +
+ +
706{
+
707 Json::Value validators = app_.validators().getJson();
+
708
+
709 if (validators.isMember(jss::publisher_lists))
+
710 {
+
711 Json::Value& publisher_lists = validators[jss::publisher_lists];
+
712
+
713 for (auto& publisher : publisher_lists)
+
714 {
+
715 publisher.removeMember(jss::list);
+
716 }
+
717 }
+
718
+
719 validators.removeMember(jss::signing_keys);
+
720 validators.removeMember(jss::trusted_validator_keys);
+
721 validators.removeMember(jss::validation_quorum);
+
722
+
723 Json::Value validatorSites = app_.validatorSites().getJson();
+
724
+
725 if (validatorSites.isMember(jss::validator_sites))
+
726 {
+
727 validators[jss::validator_sites] = std::move(validatorSites[jss::validator_sites]);
+
728 }
+
729
+
730 return validators;
+
731}
-
731
-
732// Returns information on verified peers.
- -
- -
735{
- -
737 for (auto const& peer : getActivePeers())
-
738 {
-
739 json.append(peer->json());
-
740 }
-
741 return json;
-
742}
+
732
+
733// Returns information on verified peers.
+ +
+ +
736{
+ +
738 for (auto const& peer : getActivePeers())
+
739 {
+
740 json.append(peer->json());
+
741 }
+
742 return json;
+
743}
-
743
-
744bool
-
- -
746{
-
747 if (req.target() != "/crawl" || setup_.crawlOptions == CrawlOptions::Disabled)
-
748 return false;
-
749
-
750 boost::beast::http::response<json_body> msg;
-
751 msg.version(req.version());
-
752 msg.result(boost::beast::http::status::ok);
-
753 msg.insert("Server", BuildInfo::getFullVersionString());
-
754 msg.insert("Content-Type", "application/json");
-
755 msg.insert("Connection", "close");
-
756 msg.body()["version"] = Json::Value(2u);
-
757
- -
759 {
-
760 msg.body()["overlay"] = getOverlayInfo();
-
761 }
- -
763 {
-
764 msg.body()["server"] = getServerInfo();
-
765 }
- -
767 {
-
768 msg.body()["counts"] = getServerCounts();
-
769 }
- -
771 {
-
772 msg.body()["unl"] = getUnlInfo();
-
773 }
-
774
-
775 msg.prepare_payload();
- -
777 return true;
-
778}
+
744
+
745bool
+
+ +
747{
+
748 if (req.target() != "/crawl" || setup_.crawlOptions == CrawlOptions::Disabled)
+
749 return false;
+
750
+
751 boost::beast::http::response<json_body> msg;
+
752 msg.version(req.version());
+
753 msg.result(boost::beast::http::status::ok);
+
754 msg.insert("Server", BuildInfo::getFullVersionString());
+
755 msg.insert("Content-Type", "application/json");
+
756 msg.insert("Connection", "close");
+
757 msg.body()["version"] = Json::Value(2u);
+
758
+ +
760 {
+
761 msg.body()["overlay"] = getOverlayInfo();
+
762 }
+ +
764 {
+
765 msg.body()["server"] = getServerInfo();
+
766 }
+ +
768 {
+
769 msg.body()["counts"] = getServerCounts();
+
770 }
+ +
772 {
+
773 msg.body()["unl"] = getUnlInfo();
+
774 }
+
775
+
776 msg.prepare_payload();
+ +
778 return true;
+
779}
-
779
-
780bool
-
- -
782{
-
783 // If the target is in the form "/vl/<validator_list_public_key>",
-
784 // return the most recent validator list for that key.
-
785 constexpr std::string_view prefix("/vl/");
-
786
-
787 if (!req.target().starts_with(prefix.data()) || !setup_.vlEnabled)
-
788 return false;
-
789
-
790 std::uint32_t version = 1;
-
791
-
792 boost::beast::http::response<json_body> msg;
-
793 msg.version(req.version());
-
794 msg.insert("Server", BuildInfo::getFullVersionString());
-
795 msg.insert("Content-Type", "application/json");
-
796 msg.insert("Connection", "close");
-
797
-
798 auto fail = [&msg, &handoff](auto status) {
-
799 msg.result(status);
-
800 msg.insert("Content-Length", "0");
-
801
-
802 msg.body() = Json::nullValue;
-
803
-
804 msg.prepare_payload();
- -
806 return true;
-
807 };
-
808
-
809 std::string_view key = req.target().substr(prefix.size());
-
810
-
811 if (auto slash = key.find('/'); slash != std::string_view::npos)
-
812 {
-
813 auto verString = key.substr(0, slash);
-
814 if (!boost::conversion::try_lexical_convert(verString, version))
-
815 return fail(boost::beast::http::status::bad_request);
-
816 key = key.substr(slash + 1);
-
817 }
-
818
-
819 if (key.empty())
-
820 return fail(boost::beast::http::status::bad_request);
-
821
-
822 // find the list
-
823 auto vl = app_.validators().getAvailable(key, version);
-
824
-
825 if (!vl)
-
826 {
-
827 // 404 not found
-
828 return fail(boost::beast::http::status::not_found);
-
829 }
-
830 else if (!*vl)
-
831 {
-
832 return fail(boost::beast::http::status::bad_request);
-
833 }
-
834 else
-
835 {
-
836 msg.result(boost::beast::http::status::ok);
-
837
-
838 msg.body() = *vl;
-
839
-
840 msg.prepare_payload();
- -
842 return true;
-
843 }
-
844}
+
780
+
781bool
+
+ +
783{
+
784 // If the target is in the form "/vl/<validator_list_public_key>",
+
785 // return the most recent validator list for that key.
+
786 constexpr std::string_view prefix("/vl/");
+
787
+
788 if (!req.target().starts_with(prefix.data()) || !setup_.vlEnabled)
+
789 return false;
+
790
+
791 std::uint32_t version = 1;
+
792
+
793 boost::beast::http::response<json_body> msg;
+
794 msg.version(req.version());
+
795 msg.insert("Server", BuildInfo::getFullVersionString());
+
796 msg.insert("Content-Type", "application/json");
+
797 msg.insert("Connection", "close");
+
798
+
799 auto fail = [&msg, &handoff](auto status) {
+
800 msg.result(status);
+
801 msg.insert("Content-Length", "0");
+
802
+
803 msg.body() = Json::nullValue;
+
804
+
805 msg.prepare_payload();
+ +
807 return true;
+
808 };
+
809
+
810 std::string_view key = req.target().substr(prefix.size());
+
811
+
812 if (auto slash = key.find('/'); slash != std::string_view::npos)
+
813 {
+
814 auto verString = key.substr(0, slash);
+
815 if (!boost::conversion::try_lexical_convert(verString, version))
+
816 return fail(boost::beast::http::status::bad_request);
+
817 key = key.substr(slash + 1);
+
818 }
+
819
+
820 if (key.empty())
+
821 return fail(boost::beast::http::status::bad_request);
+
822
+
823 // find the list
+
824 auto vl = app_.validators().getAvailable(key, version);
+
825
+
826 if (!vl)
+
827 {
+
828 // 404 not found
+
829 return fail(boost::beast::http::status::not_found);
+
830 }
+
831 else if (!*vl)
+
832 {
+
833 return fail(boost::beast::http::status::bad_request);
+
834 }
+
835 else
+
836 {
+
837 msg.result(boost::beast::http::status::ok);
+
838
+
839 msg.body() = *vl;
+
840
+
841 msg.prepare_payload();
+ +
843 return true;
+
844 }
+
845}
-
845
-
846bool
-
- -
848{
-
849 if (req.target() != "/health")
-
850 return false;
-
851 boost::beast::http::response<json_body> msg;
-
852 msg.version(req.version());
-
853 msg.insert("Server", BuildInfo::getFullVersionString());
-
854 msg.insert("Content-Type", "application/json");
-
855 msg.insert("Connection", "close");
-
856
-
857 auto info = getServerInfo();
-
858
-
859 int last_validated_ledger_age = -1;
-
860 if (info.isMember(jss::validated_ledger))
-
861 last_validated_ledger_age = info[jss::validated_ledger][jss::age].asInt();
-
862 bool amendment_blocked = false;
-
863 if (info.isMember(jss::amendment_blocked))
-
864 amendment_blocked = true;
-
865 int number_peers = info[jss::peers].asInt();
-
866 std::string server_state = info[jss::server_state].asString();
-
867 auto load_factor = info[jss::load_factor_server].asDouble() / info[jss::load_base].asDouble();
-
868
-
869 enum { healthy, warning, critical };
-
870 int health = healthy;
-
871 auto set_health = [&health](int state) {
-
872 if (health < state)
-
873 health = state;
-
874 };
-
875
-
876 msg.body()[jss::info] = Json::objectValue;
-
877 if (last_validated_ledger_age >= 7 || last_validated_ledger_age < 0)
-
878 {
-
879 msg.body()[jss::info][jss::validated_ledger] = last_validated_ledger_age;
-
880 if (last_validated_ledger_age < 20)
-
881 set_health(warning);
-
882 else
-
883 set_health(critical);
-
884 }
-
885
-
886 if (amendment_blocked)
-
887 {
-
888 msg.body()[jss::info][jss::amendment_blocked] = true;
-
889 set_health(critical);
-
890 }
-
891
-
892 if (number_peers <= 7)
-
893 {
-
894 msg.body()[jss::info][jss::peers] = number_peers;
-
895 if (number_peers != 0)
-
896 set_health(warning);
-
897 else
-
898 set_health(critical);
-
899 }
-
900
-
901 if (!(server_state == "full" || server_state == "validating" || server_state == "proposing"))
-
902 {
-
903 msg.body()[jss::info][jss::server_state] = server_state;
-
904 if (server_state == "syncing" || server_state == "tracking" || server_state == "connected")
-
905 {
-
906 set_health(warning);
-
907 }
-
908 else
-
909 set_health(critical);
-
910 }
-
911
-
912 if (load_factor > 100)
-
913 {
-
914 msg.body()[jss::info][jss::load_factor] = load_factor;
-
915 if (load_factor < 1000)
-
916 set_health(warning);
-
917 else
-
918 set_health(critical);
-
919 }
-
920
-
921 switch (health)
-
922 {
-
923 case healthy:
-
924 msg.result(boost::beast::http::status::ok);
-
925 break;
-
926 case warning:
-
927 msg.result(boost::beast::http::status::service_unavailable);
-
928 break;
-
929 case critical:
-
930 msg.result(boost::beast::http::status::internal_server_error);
-
931 break;
-
932 }
-
933
-
934 msg.prepare_payload();
- -
936 return true;
-
937}
+
846
+
847bool
+
+ +
849{
+
850 if (req.target() != "/health")
+
851 return false;
+
852 boost::beast::http::response<json_body> msg;
+
853 msg.version(req.version());
+
854 msg.insert("Server", BuildInfo::getFullVersionString());
+
855 msg.insert("Content-Type", "application/json");
+
856 msg.insert("Connection", "close");
+
857
+
858 auto info = getServerInfo();
+
859
+
860 int last_validated_ledger_age = -1;
+
861 if (info.isMember(jss::validated_ledger))
+
862 last_validated_ledger_age = info[jss::validated_ledger][jss::age].asInt();
+
863 bool amendment_blocked = false;
+
864 if (info.isMember(jss::amendment_blocked))
+
865 amendment_blocked = true;
+
866 int number_peers = info[jss::peers].asInt();
+
867 std::string server_state = info[jss::server_state].asString();
+
868 auto load_factor = info[jss::load_factor_server].asDouble() / info[jss::load_base].asDouble();
+
869
+
870 enum { healthy, warning, critical };
+
871 int health = healthy;
+
872 auto set_health = [&health](int state) {
+
873 if (health < state)
+
874 health = state;
+
875 };
+
876
+
877 msg.body()[jss::info] = Json::objectValue;
+
878 if (last_validated_ledger_age >= 7 || last_validated_ledger_age < 0)
+
879 {
+
880 msg.body()[jss::info][jss::validated_ledger] = last_validated_ledger_age;
+
881 if (last_validated_ledger_age < 20)
+
882 set_health(warning);
+
883 else
+
884 set_health(critical);
+
885 }
+
886
+
887 if (amendment_blocked)
+
888 {
+
889 msg.body()[jss::info][jss::amendment_blocked] = true;
+
890 set_health(critical);
+
891 }
+
892
+
893 if (number_peers <= 7)
+
894 {
+
895 msg.body()[jss::info][jss::peers] = number_peers;
+
896 if (number_peers != 0)
+
897 set_health(warning);
+
898 else
+
899 set_health(critical);
+
900 }
+
901
+
902 if (!(server_state == "full" || server_state == "validating" || server_state == "proposing"))
+
903 {
+
904 msg.body()[jss::info][jss::server_state] = server_state;
+
905 if (server_state == "syncing" || server_state == "tracking" || server_state == "connected")
+
906 {
+
907 set_health(warning);
+
908 }
+
909 else
+
910 set_health(critical);
+
911 }
+
912
+
913 if (load_factor > 100)
+
914 {
+
915 msg.body()[jss::info][jss::load_factor] = load_factor;
+
916 if (load_factor < 1000)
+
917 set_health(warning);
+
918 else
+
919 set_health(critical);
+
920 }
+
921
+
922 switch (health)
+
923 {
+
924 case healthy:
+
925 msg.result(boost::beast::http::status::ok);
+
926 break;
+
927 case warning:
+
928 msg.result(boost::beast::http::status::service_unavailable);
+
929 break;
+
930 case critical:
+
931 msg.result(boost::beast::http::status::internal_server_error);
+
932 break;
+
933 }
+
934
+
935 msg.prepare_payload();
+ +
937 return true;
+
938}
-
938
-
939bool
-
- -
941{
-
942 // Take advantage of || short-circuiting
-
943 return processCrawl(req, handoff) || processValidatorList(req, handoff) || processHealth(req, handoff);
-
944}
+
939
+
940bool
+
+ +
942{
+
943 // Take advantage of || short-circuiting
+
944 return processCrawl(req, handoff) || processValidatorList(req, handoff) || processHealth(req, handoff);
+
945}
-
945
- -
- -
948{
- -
950 ret.reserve(size());
-
951
-
952 for_each([&ret](std::shared_ptr<PeerImp>&& sp) { ret.emplace_back(std::move(sp)); });
-
953
-
954 return ret;
-
955}
+
946
+ +
+ +
949{
+ +
951 ret.reserve(size());
+
952
+
953 for_each([&ret](std::shared_ptr<PeerImp>&& sp) { ret.emplace_back(std::move(sp)); });
+
954
+
955 return ret;
+
956}
-
956
- -
- -
959 std::set<Peer::id_t> const& toSkip,
-
960 std::size_t& active,
-
961 std::size_t& disabled,
-
962 std::size_t& enabledInSkip) const
-
963{
- - -
966
-
967 active = ids_.size();
-
968 disabled = enabledInSkip = 0;
-
969 ret.reserve(ids_.size());
-
970
-
971 // NOTE The purpose of p is to delay the destruction of PeerImp
- -
973 for (auto& [id, w] : ids_)
-
974 {
-
975 if (p = w.lock(); p != nullptr)
-
976 {
-
977 bool const reduceRelayEnabled = p->txReduceRelayEnabled();
-
978 // tx reduced relay feature disabled
-
979 if (!reduceRelayEnabled)
-
980 ++disabled;
-
981
-
982 if (toSkip.count(id) == 0)
-
983 ret.emplace_back(std::move(p));
-
984 else if (reduceRelayEnabled)
-
985 ++enabledInSkip;
-
986 }
-
987 }
-
988
-
989 return ret;
-
990}
+
957
+ +
+ +
960 std::set<Peer::id_t> const& toSkip,
+
961 std::size_t& active,
+
962 std::size_t& disabled,
+
963 std::size_t& enabledInSkip) const
+
964{
+ + +
967
+
968 active = ids_.size();
+
969 disabled = enabledInSkip = 0;
+
970 ret.reserve(ids_.size());
+
971
+
972 // NOTE The purpose of p is to delay the destruction of PeerImp
+ +
974 for (auto& [id, w] : ids_)
+
975 {
+
976 if (p = w.lock(); p != nullptr)
+
977 {
+
978 bool const reduceRelayEnabled = p->txReduceRelayEnabled();
+
979 // tx reduced relay feature disabled
+
980 if (!reduceRelayEnabled)
+
981 ++disabled;
+
982
+
983 if (toSkip.count(id) == 0)
+
984 ret.emplace_back(std::move(p));
+
985 else if (reduceRelayEnabled)
+
986 ++enabledInSkip;
+
987 }
+
988 }
+
989
+
990 return ret;
+
991}
-
991
-
992void
-
- -
994{
-
995 for_each([index](std::shared_ptr<PeerImp>&& sp) { sp->checkTracking(index); });
-
996}
+
992
+
993void
+
+ +
995{
+
996 for_each([index](std::shared_ptr<PeerImp>&& sp) { sp->checkTracking(index); });
+
997}
-
997
- -
- -
1000{
-
1001 std::lock_guard lock(mutex_);
-
1002 auto const iter = ids_.find(id);
-
1003 if (iter != ids_.end())
-
1004 return iter->second.lock();
-
1005 return {};
-
1006}
+
998
+ +
+ +
1001{
+
1002 std::lock_guard lock(mutex_);
+
1003 auto const iter = ids_.find(id);
+
1004 if (iter != ids_.end())
+
1005 return iter->second.lock();
+
1006 return {};
+
1007}
-
1007
-
1008// A public key hash map was not used due to the peer connect/disconnect
-
1009// update overhead outweighing the performance of a small set linear search.
- -
- -
1012{
-
1013 std::lock_guard lock(mutex_);
-
1014 // NOTE The purpose of peer is to delay the destruction of PeerImp
- -
1016 for (auto const& e : ids_)
-
1017 {
-
1018 if (peer = e.second.lock(); peer != nullptr)
-
1019 {
-
1020 if (peer->getNodePublic() == pubKey)
-
1021 return peer;
-
1022 }
-
1023 }
-
1024 return {};
-
1025}
+
1008
+
1009// A public key hash map was not used due to the peer connect/disconnect
+
1010// update overhead outweighing the performance of a small set linear search.
+ +
+ +
1013{
+
1014 std::lock_guard lock(mutex_);
+
1015 // NOTE The purpose of peer is to delay the destruction of PeerImp
+ +
1017 for (auto const& e : ids_)
+
1018 {
+
1019 if (peer = e.second.lock(); peer != nullptr)
+
1020 {
+
1021 if (peer->getNodePublic() == pubKey)
+
1022 return peer;
+
1023 }
+
1024 }
+
1025 return {};
+
1026}
-
1026
-
1027void
-
-
1028OverlayImpl::broadcast(protocol::TMProposeSet& m)
-
1029{
-
1030 auto const sm = std::make_shared<Message>(m, protocol::mtPROPOSE_LEDGER);
-
1031 for_each([&](std::shared_ptr<PeerImp>&& p) { p->send(sm); });
-
1032}
+
1027
+
1028void
+
+
1029OverlayImpl::broadcast(protocol::TMProposeSet& m)
+
1030{
+
1031 auto const sm = std::make_shared<Message>(m, protocol::mtPROPOSE_LEDGER);
+
1032 for_each([&](std::shared_ptr<PeerImp>&& p) { p->send(sm); });
+
1033}
-
1033
- -
-
1035OverlayImpl::relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey const& validator)
-
1036{
-
1037 if (auto const toSkip = app_.getHashRouter().shouldRelay(uid))
-
1038 {
-
1039 auto const sm = std::make_shared<Message>(m, protocol::mtPROPOSE_LEDGER, validator);
- -
1041 if (toSkip->find(p->id()) == toSkip->end())
-
1042 p->send(sm);
-
1043 });
-
1044 return *toSkip;
-
1045 }
-
1046 return {};
-
1047}
+
1034
+ +
+
1036OverlayImpl::relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey const& validator)
+
1037{
+
1038 if (auto const toSkip = app_.getHashRouter().shouldRelay(uid))
+
1039 {
+
1040 auto const sm = std::make_shared<Message>(m, protocol::mtPROPOSE_LEDGER, validator);
+ +
1042 if (toSkip->find(p->id()) == toSkip->end())
+
1043 p->send(sm);
+
1044 });
+
1045 return *toSkip;
+
1046 }
+
1047 return {};
+
1048}
-
1048
-
1049void
-
-
1050OverlayImpl::broadcast(protocol::TMValidation& m)
-
1051{
-
1052 auto const sm = std::make_shared<Message>(m, protocol::mtVALIDATION);
-
1053 for_each([sm](std::shared_ptr<PeerImp>&& p) { p->send(sm); });
-
1054}
+
1049
+
1050void
+
+
1051OverlayImpl::broadcast(protocol::TMValidation& m)
+
1052{
+
1053 auto const sm = std::make_shared<Message>(m, protocol::mtVALIDATION);
+
1054 for_each([sm](std::shared_ptr<PeerImp>&& p) { p->send(sm); });
+
1055}
-
1055
- -
-
1057OverlayImpl::relay(protocol::TMValidation& m, uint256 const& uid, PublicKey const& validator)
-
1058{
-
1059 if (auto const toSkip = app_.getHashRouter().shouldRelay(uid))
-
1060 {
-
1061 auto const sm = std::make_shared<Message>(m, protocol::mtVALIDATION, validator);
- -
1063 if (toSkip->find(p->id()) == toSkip->end())
-
1064 p->send(sm);
-
1065 });
-
1066 return *toSkip;
-
1067 }
-
1068 return {};
-
1069}
+
1056
+ +
+
1058OverlayImpl::relay(protocol::TMValidation& m, uint256 const& uid, PublicKey const& validator)
+
1059{
+
1060 if (auto const toSkip = app_.getHashRouter().shouldRelay(uid))
+
1061 {
+
1062 auto const sm = std::make_shared<Message>(m, protocol::mtVALIDATION, validator);
+ +
1064 if (toSkip->find(p->id()) == toSkip->end())
+
1065 p->send(sm);
+
1066 });
+
1067 return *toSkip;
+
1068 }
+
1069 return {};
+
1070}
-
1070
- -
- -
1073{
- -
1075
-
1076 if (auto seq = app_.validatorManifests().sequence(); seq != manifestListSeq_)
-
1077 {
-
1078 protocol::TMManifests tm;
-
1079
- -
1081 [&tm](std::size_t s) { tm.mutable_list()->Reserve(s); },
-
1082 [&tm, &hr = app_.getHashRouter()](Manifest const& manifest) {
-
1083 tm.add_list()->set_stobject(manifest.serialized.data(), manifest.serialized.size());
-
1084 hr.addSuppression(manifest.hash());
-
1085 });
-
1086
- -
1088
-
1089 if (tm.list_size() != 0)
-
1090 manifestMessage_ = std::make_shared<Message>(tm, protocol::mtMANIFESTS);
-
1091
-
1092 manifestListSeq_ = seq;
-
1093 }
-
1094
-
1095 return manifestMessage_;
-
1096}
+
1071
+ +
+ +
1074{
+ +
1076
+
1077 if (auto seq = app_.validatorManifests().sequence(); seq != manifestListSeq_)
+
1078 {
+
1079 protocol::TMManifests tm;
+
1080
+ +
1082 [&tm](std::size_t s) { tm.mutable_list()->Reserve(s); },
+
1083 [&tm, &hr = app_.getHashRouter()](Manifest const& manifest) {
+
1084 tm.add_list()->set_stobject(manifest.serialized.data(), manifest.serialized.size());
+
1085 hr.addSuppression(manifest.hash());
+
1086 });
+
1087
+ +
1089
+
1090 if (tm.list_size() != 0)
+
1091 manifestMessage_ = std::make_shared<Message>(tm, protocol::mtMANIFESTS);
+
1092
+
1093 manifestListSeq_ = seq;
+
1094 }
+
1095
+
1096 return manifestMessage_;
+
1097}
-
1097
-
1098void
-
- -
1100 uint256 const& hash,
- -
1102 std::set<Peer::id_t> const& toSkip)
-
1103{
-
1104 bool relay = tx.has_value();
-
1105 if (relay)
-
1106 {
-
1107 auto& txn = tx->get();
-
1108 SerialIter sit(makeSlice(txn.rawtransaction()));
-
1109 try
-
1110 {
-
1111 relay = !isPseudoTx(STTx{sit});
-
1112 }
-
1113 catch (std::exception const&)
-
1114 {
-
1115 // Could not construct STTx, not relaying
-
1116 JLOG(journal_.debug()) << "Could not construct STTx: " << hash;
-
1117 return;
-
1118 }
-
1119 }
-
1120
-
1121 Overlay::PeerSequence peers = {};
-
1122 std::size_t total = 0;
-
1123 std::size_t disabled = 0;
-
1124 std::size_t enabledInSkip = 0;
-
1125
-
1126 if (!relay)
-
1127 {
- -
1129 return;
-
1130
-
1131 peers = getActivePeers(toSkip, total, disabled, enabledInSkip);
-
1132 JLOG(journal_.trace()) << "not relaying tx, total peers " << peers.size();
-
1133 for (auto const& p : peers)
-
1134 p->addTxQueue(hash);
-
1135 return;
-
1136 }
-
1137
-
1138 auto& txn = tx->get();
-
1139 auto const sm = std::make_shared<Message>(txn, protocol::mtTRANSACTION);
-
1140 peers = getActivePeers(toSkip, total, disabled, enabledInSkip);
-
1141 auto const minRelay = app_.config().TX_REDUCE_RELAY_MIN_PEERS + disabled;
-
1142
-
1143 if (!app_.config().TX_REDUCE_RELAY_ENABLE || total <= minRelay)
-
1144 {
-
1145 for (auto const& p : peers)
-
1146 p->send(sm);
- -
1148 txMetrics_.addMetrics(total, toSkip.size(), 0);
-
1149 return;
-
1150 }
-
1151
-
1152 // We have more peers than the minimum (disabled + minimum enabled),
-
1153 // relay to all disabled and some randomly selected enabled that
-
1154 // do not have the transaction.
-
1155 auto const enabledTarget =
-
1156 app_.config().TX_REDUCE_RELAY_MIN_PEERS + (total - minRelay) * app_.config().TX_RELAY_PERCENTAGE / 100;
-
1157
-
1158 txMetrics_.addMetrics(enabledTarget, toSkip.size(), disabled);
-
1159
-
1160 if (enabledTarget > enabledInSkip)
-
1161 std::shuffle(peers.begin(), peers.end(), default_prng());
-
1162
-
1163 JLOG(journal_.trace()) << "relaying tx, total peers " << peers.size() << " selected " << enabledTarget << " skip "
-
1164 << toSkip.size() << " disabled " << disabled;
-
1165
-
1166 // count skipped peers with the enabled feature towards the quota
-
1167 std::uint16_t enabledAndRelayed = enabledInSkip;
-
1168 for (auto const& p : peers)
-
1169 {
-
1170 // always relay to a peer with the disabled feature
-
1171 if (!p->txReduceRelayEnabled())
-
1172 {
-
1173 p->send(sm);
-
1174 }
-
1175 else if (enabledAndRelayed < enabledTarget)
-
1176 {
-
1177 enabledAndRelayed++;
-
1178 p->send(sm);
-
1179 }
-
1180 else
-
1181 {
-
1182 p->addTxQueue(hash);
-
1183 }
-
1184 }
-
1185}
+
1098
+
1099void
+
+ +
1101 uint256 const& hash,
+ +
1103 std::set<Peer::id_t> const& toSkip)
+
1104{
+
1105 bool relay = tx.has_value();
+
1106 if (relay)
+
1107 {
+
1108 auto& txn = tx->get();
+
1109 SerialIter sit(makeSlice(txn.rawtransaction()));
+
1110 try
+
1111 {
+
1112 relay = !isPseudoTx(STTx{sit});
+
1113 }
+
1114 catch (std::exception const&)
+
1115 {
+
1116 // Could not construct STTx, not relaying
+
1117 JLOG(journal_.debug()) << "Could not construct STTx: " << hash;
+
1118 return;
+
1119 }
+
1120 }
+
1121
+
1122 Overlay::PeerSequence peers = {};
+
1123 std::size_t total = 0;
+
1124 std::size_t disabled = 0;
+
1125 std::size_t enabledInSkip = 0;
+
1126
+
1127 if (!relay)
+
1128 {
+ +
1130 return;
+
1131
+
1132 peers = getActivePeers(toSkip, total, disabled, enabledInSkip);
+
1133 JLOG(journal_.trace()) << "not relaying tx, total peers " << peers.size();
+
1134 for (auto const& p : peers)
+
1135 p->addTxQueue(hash);
+
1136 return;
+
1137 }
+
1138
+
1139 auto& txn = tx->get();
+
1140 auto const sm = std::make_shared<Message>(txn, protocol::mtTRANSACTION);
+
1141 peers = getActivePeers(toSkip, total, disabled, enabledInSkip);
+
1142 auto const minRelay = app_.config().TX_REDUCE_RELAY_MIN_PEERS + disabled;
+
1143
+
1144 if (!app_.config().TX_REDUCE_RELAY_ENABLE || total <= minRelay)
+
1145 {
+
1146 for (auto const& p : peers)
+
1147 p->send(sm);
+ +
1149 txMetrics_.addMetrics(total, toSkip.size(), 0);
+
1150 return;
+
1151 }
+
1152
+
1153 // We have more peers than the minimum (disabled + minimum enabled),
+
1154 // relay to all disabled and some randomly selected enabled that
+
1155 // do not have the transaction.
+
1156 auto const enabledTarget =
+
1157 app_.config().TX_REDUCE_RELAY_MIN_PEERS + (total - minRelay) * app_.config().TX_RELAY_PERCENTAGE / 100;
+
1158
+
1159 txMetrics_.addMetrics(enabledTarget, toSkip.size(), disabled);
+
1160
+
1161 if (enabledTarget > enabledInSkip)
+
1162 std::shuffle(peers.begin(), peers.end(), default_prng());
+
1163
+
1164 JLOG(journal_.trace()) << "relaying tx, total peers " << peers.size() << " selected " << enabledTarget << " skip "
+
1165 << toSkip.size() << " disabled " << disabled;
+
1166
+
1167 // count skipped peers with the enabled feature towards the quota
+
1168 std::uint16_t enabledAndRelayed = enabledInSkip;
+
1169 for (auto const& p : peers)
+
1170 {
+
1171 // always relay to a peer with the disabled feature
+
1172 if (!p->txReduceRelayEnabled())
+
1173 {
+
1174 p->send(sm);
+
1175 }
+
1176 else if (enabledAndRelayed < enabledTarget)
+
1177 {
+
1178 enabledAndRelayed++;
+
1179 p->send(sm);
+
1180 }
+
1181 else
+
1182 {
+
1183 p->addTxQueue(hash);
+
1184 }
+
1185 }
+
1186}
-
1186
-
1187//------------------------------------------------------------------------------
-
1188
-
1189void
-
- -
1191{
-
1192 std::lock_guard lock(mutex_);
-
1193 list_.erase(&child);
-
1194 if (list_.empty())
-
1195 cond_.notify_all();
-
1196}
+
1187
+
1188//------------------------------------------------------------------------------
+
1189
+
1190void
+
+ +
1192{
+
1193 std::lock_guard lock(mutex_);
+
1194 list_.erase(&child);
+
1195 if (list_.empty())
+
1196 cond_.notify_all();
+
1197}
-
1197
-
1198void
-
- -
1200{
-
1201 // Calling list_[].second->stop() may cause list_ to be modified
-
1202 // (OverlayImpl::remove() may be called on this same thread). So
-
1203 // iterating directly over list_ to call child->stop() could lead to
-
1204 // undefined behavior.
-
1205 //
-
1206 // Therefore we copy all of the weak/shared ptrs out of list_ before we
-
1207 // start calling stop() on them. That guarantees OverlayImpl::remove()
-
1208 // won't be called until vector<> children leaves scope.
- -
1210 {
-
1211 std::lock_guard lock(mutex_);
-
1212 if (!work_)
-
1213 return;
- -
1215
-
1216 children.reserve(list_.size());
-
1217 for (auto const& element : list_)
-
1218 {
-
1219 children.emplace_back(element.second.lock());
-
1220 }
-
1221 } // lock released
-
1222
-
1223 for (auto const& child : children)
-
1224 {
-
1225 if (child != nullptr)
-
1226 child->stop();
-
1227 }
-
1228}
+
1198
+
1199void
+
+ +
1201{
+
1202 // Calling list_[].second->stop() may cause list_ to be modified
+
1203 // (OverlayImpl::remove() may be called on this same thread). So
+
1204 // iterating directly over list_ to call child->stop() could lead to
+
1205 // undefined behavior.
+
1206 //
+
1207 // Therefore we copy all of the weak/shared ptrs out of list_ before we
+
1208 // start calling stop() on them. That guarantees OverlayImpl::remove()
+
1209 // won't be called until vector<> children leaves scope.
+ +
1211 {
+
1212 std::lock_guard lock(mutex_);
+
1213 if (!work_)
+
1214 return;
+ +
1216
+
1217 children.reserve(list_.size());
+
1218 for (auto const& element : list_)
+
1219 {
+
1220 children.emplace_back(element.second.lock());
+
1221 }
+
1222 } // lock released
+
1223
+
1224 for (auto const& child : children)
+
1225 {
+
1226 if (child != nullptr)
+
1227 child->stop();
+
1228 }
+
1229}
-
1229
-
1230void
-
- -
1232{
-
1233 auto const result = m_peerFinder->autoconnect();
-
1234 for (auto addr : result)
-
1235 connect(addr);
-
1236}
+
1230
+
1231void
+
+ +
1233{
+
1234 auto const result = m_peerFinder->autoconnect();
+
1235 for (auto addr : result)
+
1236 connect(addr);
+
1237}
-
1237
-
1238void
-
- -
1240{
-
1241 auto const result = m_peerFinder->buildEndpointsForPeers();
-
1242 for (auto const& e : result)
-
1243 {
- -
1245 {
-
1246 std::lock_guard lock(mutex_);
-
1247 auto const iter = m_peers.find(e.first);
-
1248 if (iter != m_peers.end())
-
1249 peer = iter->second.lock();
-
1250 }
-
1251 if (peer)
-
1252 peer->sendEndpoints(e.second.begin(), e.second.end());
-
1253 }
-
1254}
+
1238
+
1239void
+
+ +
1241{
+
1242 auto const result = m_peerFinder->buildEndpointsForPeers();
+
1243 for (auto const& e : result)
+
1244 {
+ +
1246 {
+
1247 std::lock_guard lock(mutex_);
+
1248 auto const iter = m_peers.find(e.first);
+
1249 if (iter != m_peers.end())
+
1250 peer = iter->second.lock();
+
1251 }
+
1252 if (peer)
+
1253 peer->sendEndpoints(e.second.begin(), e.second.end());
+
1254 }
+
1255}
-
1255
-
1256void
-
- -
1258{
-
1259 for_each([](auto const& p) {
-
1260 if (p->txReduceRelayEnabled())
-
1261 p->sendTxQueue();
-
1262 });
-
1263}
+
1256
+
1257void
+
+ +
1259{
+
1260 for_each([](auto const& p) {
+
1261 if (p->txReduceRelayEnabled())
+
1262 p->sendTxQueue();
+
1263 });
+
1264}
-
1264
- -
-
1266makeSquelchMessage(PublicKey const& validator, bool squelch, uint32_t squelchDuration)
-
1267{
-
1268 protocol::TMSquelch m;
-
1269 m.set_squelch(squelch);
-
1270 m.set_validatorpubkey(validator.data(), validator.size());
-
1271 if (squelch)
-
1272 m.set_squelchduration(squelchDuration);
-
1273 return std::make_shared<Message>(m, protocol::mtSQUELCH);
-
1274}
+
1265
+ +
+
1267makeSquelchMessage(PublicKey const& validator, bool squelch, uint32_t squelchDuration)
+
1268{
+
1269 protocol::TMSquelch m;
+
1270 m.set_squelch(squelch);
+
1271 m.set_validatorpubkey(validator.data(), validator.size());
+
1272 if (squelch)
+
1273 m.set_squelchduration(squelchDuration);
+
1274 return std::make_shared<Message>(m, protocol::mtSQUELCH);
+
1275}
-
1275
-
1276void
-
- -
1278{
-
1279 if (auto peer = findPeerByShortID(id); peer)
-
1280 {
-
1281 // optimize - multiple message with different
-
1282 // validator might be sent to the same peer
-
1283 peer->send(makeSquelchMessage(validator, false, 0));
-
1284 }
-
1285}
+
1276
+
1277void
+
+ +
1279{
+
1280 if (auto peer = findPeerByShortID(id); peer)
+
1281 {
+
1282 // optimize - multiple message with different
+
1283 // validator might be sent to the same peer
+
1284 peer->send(makeSquelchMessage(validator, false, 0));
+
1285 }
+
1286}
-
1286
-
1287void
-
-
1288OverlayImpl::squelch(PublicKey const& validator, Peer::id_t id, uint32_t squelchDuration) const
-
1289{
-
1290 if (auto peer = findPeerByShortID(id); peer)
-
1291 {
-
1292 peer->send(makeSquelchMessage(validator, true, squelchDuration));
-
1293 }
-
1294}
+
1287
+
1288void
+
+
1289OverlayImpl::squelch(PublicKey const& validator, Peer::id_t id, uint32_t squelchDuration) const
+
1290{
+
1291 if (auto peer = findPeerByShortID(id); peer)
+
1292 {
+
1293 peer->send(makeSquelchMessage(validator, true, squelchDuration));
+
1294 }
+
1295}
-
1295
-
1296void
-
- -
1298 uint256 const& key,
-
1299 PublicKey const& validator,
-
1300 std::set<Peer::id_t>&& peers,
-
1301 protocol::MessageType type)
-
1302{
-
1303 if (!slots_.baseSquelchReady())
-
1304 return;
-
1305
-
1306 if (!strand_.running_in_this_thread())
-
1307 return post(
-
1308 strand_,
-
1309 // Must capture copies of reference parameters (i.e. key, validator)
-
1310 [this, key = key, validator = validator, peers = std::move(peers), type]() mutable {
-
1311 updateSlotAndSquelch(key, validator, std::move(peers), type);
-
1312 });
-
1313
-
1314 for (auto id : peers)
-
1315 slots_.updateSlotAndSquelch(
-
1316 key, validator, id, type, [&]() { reportInboundTraffic(TrafficCount::squelch_ignored, 0); });
-
1317}
+
1296
+
1297void
+
+ +
1299 uint256 const& key,
+
1300 PublicKey const& validator,
+
1301 std::set<Peer::id_t>&& peers,
+
1302 protocol::MessageType type)
+
1303{
+
1304 if (!slots_.baseSquelchReady())
+
1305 return;
+
1306
+
1307 if (!strand_.running_in_this_thread())
+
1308 return post(
+
1309 strand_,
+
1310 // Must capture copies of reference parameters (i.e. key, validator)
+
1311 [this, key = key, validator = validator, peers = std::move(peers), type]() mutable {
+
1312 updateSlotAndSquelch(key, validator, std::move(peers), type);
+
1313 });
+
1314
+
1315 for (auto id : peers)
+
1316 slots_.updateSlotAndSquelch(
+
1317 key, validator, id, type, [&]() { reportInboundTraffic(TrafficCount::squelch_ignored, 0); });
+
1318}
-
1318
-
1319void
-
- -
1321 uint256 const& key,
-
1322 PublicKey const& validator,
-
1323 Peer::id_t peer,
-
1324 protocol::MessageType type)
-
1325{
-
1326 if (!slots_.baseSquelchReady())
-
1327 return;
-
1328
-
1329 if (!strand_.running_in_this_thread())
-
1330 return post(
-
1331 strand_,
-
1332 // Must capture copies of reference parameters (i.e. key, validator)
-
1333 [this, key = key, validator = validator, peer, type]() {
-
1334 updateSlotAndSquelch(key, validator, peer, type);
-
1335 });
-
1336
-
1337 slots_.updateSlotAndSquelch(
-
1338 key, validator, peer, type, [&]() { reportInboundTraffic(TrafficCount::squelch_ignored, 0); });
-
1339}
+
1319
+
1320void
+
+ +
1322 uint256 const& key,
+
1323 PublicKey const& validator,
+
1324 Peer::id_t peer,
+
1325 protocol::MessageType type)
+
1326{
+
1327 if (!slots_.baseSquelchReady())
+
1328 return;
+
1329
+
1330 if (!strand_.running_in_this_thread())
+
1331 return post(
+
1332 strand_,
+
1333 // Must capture copies of reference parameters (i.e. key, validator)
+
1334 [this, key = key, validator = validator, peer, type]() {
+
1335 updateSlotAndSquelch(key, validator, peer, type);
+
1336 });
+
1337
+
1338 slots_.updateSlotAndSquelch(
+
1339 key, validator, peer, type, [&]() { reportInboundTraffic(TrafficCount::squelch_ignored, 0); });
+
1340}
-
1340
-
1341void
-
- -
1343{
-
1344 if (!strand_.running_in_this_thread())
-
1345 return post(strand_, std::bind(&OverlayImpl::deletePeer, this, id));
-
1346
-
1347 slots_.deletePeer(id, true);
-
1348}
+
1341
+
1342void
+
+ +
1344{
+
1345 if (!strand_.running_in_this_thread())
+
1346 return post(strand_, std::bind(&OverlayImpl::deletePeer, this, id));
+
1347
+
1348 slots_.deletePeer(id, true);
+
1349}
-
1349
-
1350void
-
- -
1352{
-
1353 if (!strand_.running_in_this_thread())
-
1354 return post(strand_, std::bind(&OverlayImpl::deleteIdlePeers, this));
-
1355
-
1356 slots_.deleteIdlePeers();
-
1357}
+
1350
+
1351void
+
+ +
1353{
+
1354 if (!strand_.running_in_this_thread())
+
1355 return post(strand_, std::bind(&OverlayImpl::deleteIdlePeers, this));
+
1356
+
1357 slots_.deleteIdlePeers();
+
1358}
-
1358
-
1359//------------------------------------------------------------------------------
-
1360
- -
- -
1363{
-
1364 Overlay::Setup setup;
-
1365
-
1366 {
-
1367 auto const& section = config.section("overlay");
-
1368 setup.context = make_SSLContext("");
-
1369
-
1370 set(setup.ipLimit, "ip_limit", section);
-
1371 if (setup.ipLimit < 0)
-
1372 Throw<std::runtime_error>("Configured IP limit is invalid");
-
1373
-
1374 std::string ip;
-
1375 set(ip, "public_ip", section);
-
1376 if (!ip.empty())
-
1377 {
-
1378 boost::system::error_code ec;
-
1379 setup.public_ip = boost::asio::ip::make_address(ip, ec);
-
1380 if (ec || beast::IP::is_private(setup.public_ip))
-
1381 Throw<std::runtime_error>("Configured public IP is invalid");
-
1382 }
-
1383 }
-
1384
-
1385 {
-
1386 auto const& section = config.section("crawl");
-
1387 auto const& values = section.values();
-
1388
-
1389 if (values.size() > 1)
-
1390 {
-
1391 Throw<std::runtime_error>("Configured [crawl] section is invalid, too many values");
-
1392 }
-
1393
-
1394 bool crawlEnabled = true;
-
1395
-
1396 // Only allow "0|1" as a value
-
1397 if (values.size() == 1)
-
1398 {
-
1399 try
-
1400 {
-
1401 crawlEnabled = boost::lexical_cast<bool>(values.front());
-
1402 }
-
1403 catch (boost::bad_lexical_cast const&)
-
1404 {
-
1405 Throw<std::runtime_error>("Configured [crawl] section has invalid value: " + values.front());
-
1406 }
-
1407 }
-
1408
-
1409 if (crawlEnabled)
-
1410 {
-
1411 if (get<bool>(section, "overlay", true))
-
1412 {
- -
1414 }
-
1415 if (get<bool>(section, "server", true))
-
1416 {
- -
1418 }
-
1419 if (get<bool>(section, "counts", false))
-
1420 {
- -
1422 }
-
1423 if (get<bool>(section, "unl", true))
-
1424 {
- -
1426 }
-
1427 }
-
1428 }
-
1429 {
-
1430 auto const& section = config.section("vl");
-
1431
-
1432 set(setup.vlEnabled, "enabled", section);
-
1433 }
-
1434
-
1435 try
-
1436 {
-
1437 auto id = config.legacy("network_id");
-
1438
-
1439 if (!id.empty())
-
1440 {
-
1441 if (id == "main")
-
1442 id = "0";
-
1443
-
1444 if (id == "testnet")
-
1445 id = "1";
-
1446
-
1447 if (id == "devnet")
-
1448 id = "2";
-
1449
-
1450 setup.networkID = beast::lexicalCastThrow<std::uint32_t>(id);
-
1451 }
-
1452 }
-
1453 catch (...)
-
1454 {
-
1455 Throw<std::runtime_error>(
-
1456 "Configured [network_id] section is invalid: must be a number "
-
1457 "or one of the strings 'main', 'testnet' or 'devnet'.");
-
1458 }
-
1459
-
1460 return setup;
-
1461}
+
1359
+
1360//------------------------------------------------------------------------------
+
1361
+ +
+ +
1364{
+
1365 Overlay::Setup setup;
+
1366
+
1367 {
+
1368 auto const& section = config.section("overlay");
+
1369 setup.context = make_SSLContext("");
+
1370
+
1371 set(setup.ipLimit, "ip_limit", section);
+
1372 if (setup.ipLimit < 0)
+
1373 Throw<std::runtime_error>("Configured IP limit is invalid");
+
1374
+
1375 std::string ip;
+
1376 set(ip, "public_ip", section);
+
1377 if (!ip.empty())
+
1378 {
+
1379 boost::system::error_code ec;
+
1380 setup.public_ip = boost::asio::ip::make_address(ip, ec);
+
1381 if (ec || beast::IP::is_private(setup.public_ip))
+
1382 Throw<std::runtime_error>("Configured public IP is invalid");
+
1383 }
+
1384 }
+
1385
+
1386 {
+
1387 auto const& section = config.section("crawl");
+
1388 auto const& values = section.values();
+
1389
+
1390 if (values.size() > 1)
+
1391 {
+
1392 Throw<std::runtime_error>("Configured [crawl] section is invalid, too many values");
+
1393 }
+
1394
+
1395 bool crawlEnabled = true;
+
1396
+
1397 // Only allow "0|1" as a value
+
1398 if (values.size() == 1)
+
1399 {
+
1400 try
+
1401 {
+
1402 crawlEnabled = boost::lexical_cast<bool>(values.front());
+
1403 }
+
1404 catch (boost::bad_lexical_cast const&)
+
1405 {
+
1406 Throw<std::runtime_error>("Configured [crawl] section has invalid value: " + values.front());
+
1407 }
+
1408 }
+
1409
+
1410 if (crawlEnabled)
+
1411 {
+
1412 if (get<bool>(section, "overlay", true))
+
1413 {
+ +
1415 }
+
1416 if (get<bool>(section, "server", true))
+
1417 {
+ +
1419 }
+
1420 if (get<bool>(section, "counts", false))
+
1421 {
+ +
1423 }
+
1424 if (get<bool>(section, "unl", true))
+
1425 {
+ +
1427 }
+
1428 }
+
1429 }
+
1430 {
+
1431 auto const& section = config.section("vl");
+
1432
+
1433 set(setup.vlEnabled, "enabled", section);
+
1434 }
+
1435
+
1436 try
+
1437 {
+
1438 auto id = config.legacy("network_id");
+
1439
+
1440 if (!id.empty())
+
1441 {
+
1442 if (id == "main")
+
1443 id = "0";
+
1444
+
1445 if (id == "testnet")
+
1446 id = "1";
+
1447
+
1448 if (id == "devnet")
+
1449 id = "2";
+
1450
+
1451 setup.networkID = beast::lexicalCastThrow<std::uint32_t>(id);
+
1452 }
+
1453 }
+
1454 catch (...)
+
1455 {
+
1456 Throw<std::runtime_error>(
+
1457 "Configured [network_id] section is invalid: must be a number "
+
1458 "or one of the strings 'main', 'testnet' or 'devnet'.");
+
1459 }
+
1460
+
1461 return setup;
+
1462}
-
1462
- -
- -
1465 Application& app,
-
1466 Overlay::Setup const& setup,
-
1467 ServerHandler& serverHandler,
-
1468 Resource::Manager& resourceManager,
-
1469 Resolver& resolver,
-
1470 boost::asio::io_context& io_context,
-
1471 BasicConfig const& config,
-
1472 beast::insight::Collector::ptr const& collector)
-
1473{
- -
1475 app, setup, serverHandler, resourceManager, resolver, io_context, config, collector);
-
1476}
+
1463
+ +
+ +
1466 Application& app,
+
1467 Overlay::Setup const& setup,
+
1468 ServerHandler& serverHandler,
+
1469 Resource::Manager& resourceManager,
+
1470 Resolver& resolver,
+
1471 boost::asio::io_context& io_context,
+
1472 BasicConfig const& config,
+
1473 beast::insight::Collector::ptr const& collector)
+
1474{
+ +
1476 app, setup, serverHandler, resourceManager, resolver, io_context, config, collector);
+
1477}
-
1477
-
1478} // namespace xrpl
+
1478
+
1479} // namespace xrpl
@@ -1677,9 +1678,9 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
T bind(T... args)
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
-
Value removeMember(char const *key)
Remove and return the named member.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value & append(Value const &value)
Append value to array at the end.
+
Value removeMember(char const *key)
Remove and return the named member.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A version-independent IP address and port combination.
Definition IPEndpoint.h:18
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:300
@@ -1716,84 +1717,84 @@ $(document).ready(function() { init_codefold(0); });
Child(OverlayImpl &overlay)
-
void deletePeer(Peer::id_t id)
Called when the peer is deleted.
- -
void remove(std::shared_ptr< PeerFinder::Slot > const &slot)
+
void deletePeer(Peer::id_t id)
Called when the peer is deleted.
+ +
void remove(std::shared_ptr< PeerFinder::Slot > const &slot)
std::weak_ptr< Timer > timer_
Definition OverlayImpl.h:89
boost::asio::io_context & io_context_
Definition OverlayImpl.h:84
-
bool processRequest(http_request_type const &req, Handoff &handoff)
Handles non-peer protocol requests.
-
OverlayImpl(Application &app, Setup const &setup, ServerHandler &serverHandler, Resource::Manager &resourceManager, Resolver &resolver, boost::asio::io_context &io_context, BasicConfig const &config, beast::insight::Collector::ptr const &collector)
+
bool processRequest(http_request_type const &req, Handoff &handoff)
Handles non-peer protocol requests.
+
OverlayImpl(Application &app, Setup const &setup, ServerHandler &serverHandler, Resource::Manager &resourceManager, Resolver &resolver, boost::asio::io_context &io_context, BasicConfig const &config, beast::insight::Collector::ptr const &collector)
boost::asio::ip::address address_type
Definition OverlayImpl.h:62
-
static bool isPeerUpgrade(http_request_type const &request)
+
static bool isPeerUpgrade(http_request_type const &request)
Resource::Manager & m_resourceManager
Definition OverlayImpl.h:94
boost::system::error_code error_code
Definition OverlayImpl.h:64
-
bool processCrawl(http_request_type const &req, Handoff &handoff)
Handles crawl requests.
+
bool processCrawl(http_request_type const &req, Handoff &handoff)
Handles crawl requests.
-
bool processHealth(http_request_type const &req, Handoff &handoff)
Handles health requests.
-
Json::Value getServerCounts()
Returns information about the local server's performance counters.
-
void activate(std::shared_ptr< PeerImp > const &peer)
Called when a peer has connected successfully This is called after the peer handshake has been comple...
-
Handoff onHandoff(std::unique_ptr< stream_type > &&bundle, http_request_type &&request, endpoint_type remote_endpoint) override
Conditionally accept an incoming HTTP request.
+
bool processHealth(http_request_type const &req, Handoff &handoff)
Handles health requests.
+
Json::Value getServerCounts()
Returns information about the local server's performance counters.
+
void activate(std::shared_ptr< PeerImp > const &peer)
Called when a peer has connected successfully This is called after the peer handshake has been comple...
+
Handoff onHandoff(std::unique_ptr< stream_type > &&bundle, http_request_type &&request, endpoint_type remote_endpoint) override
Conditionally accept an incoming HTTP request.
std::optional< boost::asio::executor_work_guard< boost::asio::io_context::executor_type > > work_
Definition OverlayImpl.h:85
-
void reportOutboundTraffic(TrafficCount::category cat, int bytes)
- -
void for_each(UnaryFunc &&f) const
-
void stop() override
-
void connect(beast::IP::Endpoint const &remote_endpoint) override
Establish a peer connection to the specified endpoint.
-
std::size_t size() const override
The number of active peers on the network Active peers are only those peers that have completed the h...
+
void reportOutboundTraffic(TrafficCount::category cat, int bytes)
+ +
void for_each(UnaryFunc &&f) const
+
void stop() override
+
void connect(beast::IP::Endpoint const &remote_endpoint) override
Establish a peer connection to the specified endpoint.
+
std::size_t size() const override
The number of active peers on the network Active peers are only those peers that have completed the h...
ServerHandler & serverHandler_
Definition OverlayImpl.h:93
-
void onManifests(std::shared_ptr< protocol::TMManifests > const &m, std::shared_ptr< PeerImp > const &from)
-
void broadcast(protocol::TMProposeSet &m) override
Broadcast a proposal.
-
std::shared_ptr< Writer > makeErrorResponse(std::shared_ptr< PeerFinder::Slot > const &slot, http_request_type const &request, address_type remote_address, std::string msg)
+
void onManifests(std::shared_ptr< protocol::TMManifests > const &m, std::shared_ptr< PeerImp > const &from)
+
void broadcast(protocol::TMProposeSet &m) override
Broadcast a proposal.
+
std::shared_ptr< Writer > makeErrorResponse(std::shared_ptr< PeerFinder::Slot > const &slot, http_request_type const &request, address_type remote_address, std::string msg)
reduce_relay::Slots< UptimeClock > slots_
hash_map< Peer::id_t, std::weak_ptr< PeerImp > > ids_
Definition OverlayImpl.h:98
-
void deleteIdlePeers()
Check if peers stopped relaying messages and if slots stopped receiving messages from the validator.
+
void deleteIdlePeers()
Check if peers stopped relaying messages and if slots stopped receiving messages from the validator.
TrafficCount m_traffic
Definition OverlayImpl.h:96
-
void squelch(PublicKey const &validator, Peer::id_t const id, std::uint32_t squelchDuration) const override
Squelch handler.
-
void sendTxQueue()
Send once a second transactions' hashes aggregated by peers.
+
void squelch(PublicKey const &validator, Peer::id_t const id, std::uint32_t squelchDuration) const override
Squelch handler.
+
void sendTxQueue()
Send once a second transactions' hashes aggregated by peers.
std::shared_ptr< Message > manifestMessage_
std::unique_ptr< PeerFinder::Manager > m_peerFinder
Definition OverlayImpl.h:95
std::optional< std::uint32_t > manifestListSeq_
- -
void onWrite(beast::PropertyStream::Map &stream) override
Subclass override.
-
void add_active(std::shared_ptr< PeerImp > const &peer)
- + +
void onWrite(beast::PropertyStream::Map &stream) override
Subclass override.
+
void add_active(std::shared_ptr< PeerImp > const &peer)
+
PeerFinder::Manager & peerFinder()
Application & app_
Definition OverlayImpl.h:83
std::recursive_mutex mutex_
Definition OverlayImpl.h:87
Resource::Manager & resourceManager()
beast::Journal const journal_
Definition OverlayImpl.h:92
boost::asio::ip::tcp::endpoint endpoint_type
Definition OverlayImpl.h:63
-
void onPeerDeactivate(Peer::id_t id)
+
void onPeerDeactivate(Peer::id_t id)
std::mutex manifestLock_
boost::asio::strand< boost::asio::io_context::executor_type > strand_
Definition OverlayImpl.h:86
-
Json::Value json() override
Return diagnostics on the status of all peers.
-
static std::string makePrefix(std::uint32_t id)
+
Json::Value json() override
Return diagnostics on the status of all peers.
+
static std::string makePrefix(std::uint32_t id)
Setup const & setup() const
- -
std::set< Peer::id_t > relay(protocol::TMProposeSet &m, uint256 const &uid, PublicKey const &validator) override
Relay a proposal.
-
static bool is_upgrade(boost::beast::http::header< true, Fields > const &req)
+ +
std::set< Peer::id_t > relay(protocol::TMProposeSet &m, uint256 const &uid, PublicKey const &validator) override
Relay a proposal.
+
static bool is_upgrade(boost::beast::http::header< true, Fields > const &req)
metrics::TxMetrics txMetrics_
boost::container::flat_map< Child *, std::weak_ptr< Child > > list_
Definition OverlayImpl.h:90
-
int limit() override
Returns the maximum number of peers we are configured to allow.
+
int limit() override
Returns the maximum number of peers we are configured to allow.
std::condition_variable_any cond_
Definition OverlayImpl.h:88
hash_map< std::shared_ptr< PeerFinder::Slot >, std::weak_ptr< PeerImp > > m_peers
Definition OverlayImpl.h:97
-
std::shared_ptr< Message > getManifestsMessage()
-
Json::Value getUnlInfo()
Returns information about the local server's UNL.
-
std::shared_ptr< Peer > findPeerByPublicKey(PublicKey const &pubKey) override
Returns the peer with the matching public key, or null.
-
std::shared_ptr< Writer > makeRedirectResponse(std::shared_ptr< PeerFinder::Slot > const &slot, http_request_type const &request, address_type remote_address)
+
std::shared_ptr< Message > getManifestsMessage()
+
Json::Value getUnlInfo()
Returns information about the local server's UNL.
+
std::shared_ptr< Peer > findPeerByPublicKey(PublicKey const &pubKey) override
Returns the peer with the matching public key, or null.
+
std::shared_ptr< Writer > makeRedirectResponse(std::shared_ptr< PeerFinder::Slot > const &slot, http_request_type const &request, address_type remote_address)
std::atomic< Peer::id_t > next_id_
-
void reportInboundTraffic(TrafficCount::category cat, int bytes)
-
bool processValidatorList(http_request_type const &req, Handoff &handoff)
Handles validator list requests.
-
void checkTracking(std::uint32_t) override
Calls the checkTracking function on each peer.
+
void reportInboundTraffic(TrafficCount::category cat, int bytes)
+
bool processValidatorList(http_request_type const &req, Handoff &handoff)
Handles validator list requests.
+
void checkTracking(std::uint32_t) override
Calls the checkTracking function on each peer.
Resolver & m_resolver
Definition OverlayImpl.h:99
-
Json::Value getServerInfo()
Returns information about the local server.
-
void updateSlotAndSquelch(uint256 const &key, PublicKey const &validator, std::set< Peer::id_t > &&peers, protocol::MessageType type)
Updates message count for validator/peer.
-
std::shared_ptr< Peer > findPeerByShortID(Peer::id_t const &id) const override
Returns the peer with the matching short id, or null.
-
void start() override
-
PeerSequence getActivePeers() const override
Returns a sequence representing the current list of peers.
-
Json::Value getOverlayInfo()
Returns information about peers on the overlay network.
-
void unsquelch(PublicKey const &validator, Peer::id_t id) const override
Unsquelch handler.
+
Json::Value getServerInfo()
Returns information about the local server.
+
void updateSlotAndSquelch(uint256 const &key, PublicKey const &validator, std::set< Peer::id_t > &&peers, protocol::MessageType type)
Updates message count for validator/peer.
+
std::shared_ptr< Peer > findPeerByShortID(Peer::id_t const &id) const override
Returns the peer with the matching short id, or null.
+
void start() override
+
PeerSequence getActivePeers() const override
Returns a sequence representing the current list of peers.
+
Json::Value getOverlayInfo()
Returns information about peers on the overlay network.
+
void unsquelch(PublicKey const &validator, Peer::id_t id) const override
Unsquelch handler.
Manages the set of connected peers.
Definition Overlay.h:29
virtual std::pair< std::shared_ptr< Slot >, Result > new_outbound_slot(beast::IP::Endpoint const &remote_endpoint)=0
Create a new outbound slot with the specified remote endpoint.
bool contains(PublicKey const &nodeId)
@@ -1823,7 +1824,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value getJson() const
Return a JSON representation of the state of the validator list.
-
std::optional< Json::Value > getAvailable(std::string_view pubKey, std::optional< std::uint32_t > forceVersion={})
Returns the current valid list for the given publisher key, if available, as a Json object.
+
std::optional< Json::Value > getAvailable(std::string_view pubKey, std::optional< std::uint32_t > forceVersion={})
Returns the current valid list for the given publisher key, if available, as a Json object.
bool listed(PublicKey const &identity) const
Returns true if public key is included on any lists.
Json::Value getJson() const
Return JSON representation of configured validator sites.
@@ -1875,12 +1876,12 @@ $(document).ready(function() { init_codefold(0); });
beast::xor_shift_engine & default_prng()
Return the default random engine.
@ manifest
Manifest.
constexpr Number squelch(Number const &x, Number const &limit) noexcept
Definition Number.h:737
-
std::shared_ptr< Message > makeSquelchMessage(PublicKey const &validator, bool squelch, uint32_t squelchDuration)
-
Overlay::Setup setup_Overlay(BasicConfig const &config)
+
std::shared_ptr< Message > makeSquelchMessage(PublicKey const &validator, bool squelch, uint32_t squelchDuration)
+
Overlay::Setup setup_Overlay(BasicConfig const &config)
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:213
PublicKey verifyHandshake(boost::beast::http::fields const &headers, xrpl::uint256 const &sharedValue, std::optional< std::uint32_t > networkID, beast::IP::Address public_ip, beast::IP::Address remote, Application &app)
Validate header fields necessary for upgrading the link to the peer protocol.
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
Definition STTx.cpp:776
-
std::unique_ptr< Overlay > make_Overlay(Application &app, Overlay::Setup const &setup, ServerHandler &serverHandler, Resource::Manager &resourceManager, Resolver &resolver, boost::asio::io_context &io_context, BasicConfig const &config, beast::insight::Collector::ptr const &collector)
Creates the implementation of Overlay.
+
std::unique_ptr< Overlay > make_Overlay(Application &app, Overlay::Setup const &setup, ServerHandler &serverHandler, Resource::Manager &resourceManager, Resolver &resolver, boost::asio::io_context &io_context, BasicConfig const &config, beast::insight::Collector::ptr const &collector)
Creates the implementation of Overlay.
@ accepted
Manifest is valid.
@@ -1905,10 +1906,10 @@ $(document).ready(function() { init_codefold(0); });
bool keep_alive
Definition Handoff.h:24
-
void on_timer(error_code ec)
+
void on_timer(error_code ec)
Timer(OverlayImpl &overlay)
- +
std::uint32_t crawlOptions
Definition Overlay.h:51
std::optional< std::uint32_t > networkID
Definition Overlay.h:52
diff --git a/OverlayImpl_8h_source.html b/OverlayImpl_8h_source.html index 7c0b72ac7f..8928d05456 100644 --- a/OverlayImpl_8h_source.html +++ b/OverlayImpl_8h_source.html @@ -276,381 +276,382 @@ $(document).ready(function() { init_codefold(0); });
191 std::size_t& disabled,
192 std::size_t& enabledInSkip) const;
193
-
194 void checkTracking(std::uint32_t) override;
-
195
- -
197 findPeerByShortID(Peer::id_t const& id) const override;
-
198
- -
200 findPeerByPublicKey(PublicKey const& pubKey) override;
-
201
-
202 void
-
203 broadcast(protocol::TMProposeSet& m) override;
-
204
-
205 void
-
206 broadcast(protocol::TMValidation& m) override;
-
207
- -
209 relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey const& validator) override;
-
210
- -
212 relay(protocol::TMValidation& m, uint256 const& uid, PublicKey const& validator) override;
-
213
-
214 void
-
215 relay(
-
216 uint256 const&,
- -
218 std::set<Peer::id_t> const& skip) override;
-
219
- - -
222
-
223 //--------------------------------------------------------------------------
-
224 //
-
225 // OverlayImpl
-
226 //
-
227
-
228 void
- -
230
-
231 void
- -
233
-
239 void
- -
241
-
242 // Called when an active peer is destroyed.
-
243 void
- -
245
-
246 // UnaryFunc will be called as
-
247 // void(std::shared_ptr<PeerImp>&&)
-
248 //
-
249 template <class UnaryFunc>
-
250 void
-
-
251 for_each(UnaryFunc&& f) const
-
252 {
- -
254 {
- -
256
-
257 // Iterate over a copy of the peer list because peer
-
258 // destruction can invalidate iterators.
-
259 wp.reserve(ids_.size());
-
260
-
261 for (auto& x : ids_)
-
262 wp.push_back(x.second);
-
263 }
-
264
-
265 for (auto& w : wp)
-
266 {
-
267 if (auto p = w.lock())
-
268 f(std::move(p));
-
269 }
-
270 }
+
194 void
+ +
196
+ +
198 findPeerByShortID(Peer::id_t const& id) const override;
+
199
+ +
201 findPeerByPublicKey(PublicKey const& pubKey) override;
+
202
+
203 void
+
204 broadcast(protocol::TMProposeSet& m) override;
+
205
+
206 void
+
207 broadcast(protocol::TMValidation& m) override;
+
208
+ +
210 relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey const& validator) override;
+
211
+ +
213 relay(protocol::TMValidation& m, uint256 const& uid, PublicKey const& validator) override;
+
214
+
215 void
+
216 relay(
+
217 uint256 const&,
+ +
219 std::set<Peer::id_t> const& skip) override;
+
220
+ + +
223
+
224 //--------------------------------------------------------------------------
+
225 //
+
226 // OverlayImpl
+
227 //
+
228
+
229 void
+ +
231
+
232 void
+ +
234
+
240 void
+ +
242
+
243 // Called when an active peer is destroyed.
+
244 void
+ +
246
+
247 // UnaryFunc will be called as
+
248 // void(std::shared_ptr<PeerImp>&&)
+
249 //
+
250 template <class UnaryFunc>
+
251 void
+
+
252 for_each(UnaryFunc&& f) const
+
253 {
+ +
255 {
+ +
257
+
258 // Iterate over a copy of the peer list because peer
+
259 // destruction can invalidate iterators.
+
260 wp.reserve(ids_.size());
+
261
+
262 for (auto& x : ids_)
+
263 wp.push_back(x.second);
+
264 }
+
265
+
266 for (auto& w : wp)
+
267 {
+
268 if (auto p = w.lock())
+
269 f(std::move(p));
+
270 }
+
271 }
-
271
-
272 // Called when TMManifests is received from a peer
-
273 void
- -
275
-
276 static bool
-
277 isPeerUpgrade(http_request_type const& request);
-
278
-
279 template <class Body>
-
280 static bool
-
-
281 isPeerUpgrade(boost::beast::http::response<Body> const& response)
-
282 {
-
283 if (!is_upgrade(response))
-
284 return false;
-
285 return response.result() == boost::beast::http::status::switching_protocols;
-
286 }
+
272
+
273 // Called when TMManifests is received from a peer
+
274 void
+ +
276
+
277 static bool
+
278 isPeerUpgrade(http_request_type const& request);
+
279
+
280 template <class Body>
+
281 static bool
+
+
282 isPeerUpgrade(boost::beast::http::response<Body> const& response)
+
283 {
+
284 if (!is_upgrade(response))
+
285 return false;
+
286 return response.result() == boost::beast::http::status::switching_protocols;
+
287 }
-
287
-
288 template <class Fields>
-
289 static bool
-
-
290 is_upgrade(boost::beast::http::header<true, Fields> const& req)
-
291 {
-
292 if (req.version() < 11)
-
293 return false;
-
294 if (req.method() != boost::beast::http::verb::get)
-
295 return false;
-
296 if (!boost::beast::http::token_list{req["Connection"]}.exists("upgrade"))
-
297 return false;
-
298 return true;
-
299 }
+
288
+
289 template <class Fields>
+
290 static bool
+
+
291 is_upgrade(boost::beast::http::header<true, Fields> const& req)
+
292 {
+
293 if (req.version() < 11)
+
294 return false;
+
295 if (req.method() != boost::beast::http::verb::get)
+
296 return false;
+
297 if (!boost::beast::http::token_list{req["Connection"]}.exists("upgrade"))
+
298 return false;
+
299 return true;
+
300 }
-
300
-
301 template <class Fields>
-
302 static bool
-
-
303 is_upgrade(boost::beast::http::header<false, Fields> const& req)
-
304 {
-
305 if (req.version() < 11)
-
306 return false;
-
307 if (!boost::beast::http::token_list{req["Connection"]}.exists("upgrade"))
-
308 return false;
-
309 return true;
-
310 }
+
301
+
302 template <class Fields>
+
303 static bool
+
+
304 is_upgrade(boost::beast::http::header<false, Fields> const& req)
+
305 {
+
306 if (req.version() < 11)
+
307 return false;
+
308 if (!boost::beast::http::token_list{req["Connection"]}.exists("upgrade"))
+
309 return false;
+
310 return true;
+
311 }
-
311
-
312 static std::string
- -
314
-
315 void
- -
317
-
318 void
- -
320
-
321 void
-
- -
323 {
- -
325 }
+
312
+
313 static std::string
+ +
315
+
316 void
+ +
318
+
319 void
+ +
321
+
322 void
+
+ +
324 {
+ +
326 }
-
326
- -
-
328 getJqTransOverflow() const override
-
329 {
-
330 return jqTransOverflow_;
-
331 }
+
327
+ +
+
329 getJqTransOverflow() const override
+
330 {
+
331 return jqTransOverflow_;
+
332 }
-
332
-
333 void
-
- -
335 {
- -
337 }
+
333
+
334 void
+
+ +
336 {
+ +
338 }
-
338
- -
-
340 getPeerDisconnect() const override
-
341 {
-
342 return peerDisconnects_;
-
343 }
+
339
+ +
+
341 getPeerDisconnect() const override
+
342 {
+
343 return peerDisconnects_;
+
344 }
-
344
-
345 void
-
- -
347 {
- -
349 }
+
345
+
346 void
+
+ +
348 {
+ +
350 }
-
350
- -
- -
353 {
- -
355 }
+
351
+ +
+ +
354 {
+ +
356 }
-
356
- -
-
358 networkID() const override
-
359 {
-
360 return setup_.networkID;
-
361 }
+
357
+ +
+
359 networkID() const override
+
360 {
+
361 return setup_.networkID;
+
362 }
-
362
-
372 void
- -
374 uint256 const& key,
-
375 PublicKey const& validator,
-
376 std::set<Peer::id_t>&& peers,
-
377 protocol::MessageType type);
-
378
-
381 void
-
382 updateSlotAndSquelch(uint256 const& key, PublicKey const& validator, Peer::id_t peer, protocol::MessageType type);
-
383
-
389 void
- -
391
- -
-
393 txMetrics() const override
-
394 {
-
395 return txMetrics_.json();
-
396 }
+
363
+
373 void
+ +
375 uint256 const& key,
+
376 PublicKey const& validator,
+
377 std::set<Peer::id_t>&& peers,
+
378 protocol::MessageType type);
+
379
+
382 void
+
383 updateSlotAndSquelch(uint256 const& key, PublicKey const& validator, Peer::id_t peer, protocol::MessageType type);
+
384
+
390 void
+ +
392
+ +
+
394 txMetrics() const override
+
395 {
+
396 return txMetrics_.json();
+
397 }
-
397
-
399 template <typename... Args>
-
400 void
-
-
401 addTxMetrics(Args... args)
-
402 {
-
403 if (!strand_.running_in_this_thread())
-
404 return post(strand_, std::bind(&OverlayImpl::addTxMetrics<Args...>, this, args...));
-
405
-
406 txMetrics_.addMetrics(args...);
-
407 }
+
398
+
400 template <typename... Args>
+
401 void
+
+
402 addTxMetrics(Args... args)
+
403 {
+
404 if (!strand_.running_in_this_thread())
+
405 return post(strand_, std::bind(&OverlayImpl::addTxMetrics<Args...>, this, args...));
+
406
+
407 txMetrics_.addMetrics(args...);
+
408 }
-
408
-
409private:
-
410 void
-
411 squelch(PublicKey const& validator, Peer::id_t const id, std::uint32_t squelchDuration) const override;
-
412
-
413 void
-
414 unsquelch(PublicKey const& validator, Peer::id_t id) const override;
-
415
- - - -
419 http_request_type const& request,
-
420 address_type remote_address);
-
421
- - - -
425 http_request_type const& request,
-
426 address_type remote_address,
-
427 std::string msg);
-
428
-
434 bool
-
435 processCrawl(http_request_type const& req, Handoff& handoff);
-
436
-
444 bool
-
445 processValidatorList(http_request_type const& req, Handoff& handoff);
-
446
-
452 bool
-
453 processHealth(http_request_type const& req, Handoff& handoff);
-
454
-
459 bool
-
460 processRequest(http_request_type const& req, Handoff& handoff);
-
461
- - -
468
- - -
475
- - -
482
- -
488 getUnlInfo();
-
489
-
490 //--------------------------------------------------------------------------
-
491
-
492 //
-
493 // PropertyStream
-
494 //
-
495
-
496 void
-
497 onWrite(beast::PropertyStream::Map& stream) override;
-
498
-
499 //--------------------------------------------------------------------------
-
500
-
501 void
-
502 remove(Child& child);
-
503
-
504 void
-
505 stopChildren();
-
506
-
507 void
-
508 autoConnect();
-
509
-
510 void
- -
512
-
514 void
-
515 sendTxQueue();
-
516
-
519 void
- -
521
-
522private:
-
- -
524 {
-
- -
526 : name(name)
-
527 , bytesIn(collector->make_gauge(name, "Bytes_In"))
-
528 , bytesOut(collector->make_gauge(name, "Bytes_Out"))
-
529 , messagesIn(collector->make_gauge(name, "Messages_In"))
-
530 , messagesOut(collector->make_gauge(name, "Messages_Out"))
-
531 {
-
532 }
+
409
+
410private:
+
411 void
+
412 squelch(PublicKey const& validator, Peer::id_t const id, std::uint32_t squelchDuration) const override;
+
413
+
414 void
+
415 unsquelch(PublicKey const& validator, Peer::id_t id) const override;
+
416
+ + + +
420 http_request_type const& request,
+
421 address_type remote_address);
+
422
+ + + +
426 http_request_type const& request,
+
427 address_type remote_address,
+
428 std::string msg);
+
429
+
435 bool
+
436 processCrawl(http_request_type const& req, Handoff& handoff);
+
437
+
445 bool
+
446 processValidatorList(http_request_type const& req, Handoff& handoff);
+
447
+
453 bool
+
454 processHealth(http_request_type const& req, Handoff& handoff);
+
455
+
460 bool
+
461 processRequest(http_request_type const& req, Handoff& handoff);
+
462
+ + +
469
+ + +
476
+ + +
483
+ +
489 getUnlInfo();
+
490
+
491 //--------------------------------------------------------------------------
+
492
+
493 //
+
494 // PropertyStream
+
495 //
+
496
+
497 void
+
498 onWrite(beast::PropertyStream::Map& stream) override;
+
499
+
500 //--------------------------------------------------------------------------
+
501
+
502 void
+
503 remove(Child& child);
+
504
+
505 void
+
506 stopChildren();
+
507
+
508 void
+
509 autoConnect();
+
510
+
511 void
+ +
513
+
515 void
+
516 sendTxQueue();
+
517
+
520 void
+ +
522
+
523private:
+
+ +
525 {
+
+ +
527 : name(name)
+
528 , bytesIn(collector->make_gauge(name, "Bytes_In"))
+
529 , bytesOut(collector->make_gauge(name, "Bytes_Out"))
+
530 , messagesIn(collector->make_gauge(name, "Messages_In"))
+
531 , messagesOut(collector->make_gauge(name, "Messages_Out"))
+
532 {
+
533 }
- - - - - -
538 };
+ + + + + +
539 };
-
539
-
-
540 struct Stats
-
541 {
-
542 template <class Handler>
-
- -
544 Handler const& handler,
-
545 beast::insight::Collector::ptr const& collector,
- -
547 : peerDisconnects(collector->make_gauge("Overlay", "Peer_Disconnects"))
-
548 , trafficGauges(std::move(trafficGauges_))
-
549 , hook(collector->make_hook(handler))
-
550 {
-
551 }
+
540
+
+
541 struct Stats
+
542 {
+
543 template <class Handler>
+
+ +
545 Handler const& handler,
+
546 beast::insight::Collector::ptr const& collector,
+ +
548 : peerDisconnects(collector->make_gauge("Overlay", "Peer_Disconnects"))
+
549 , trafficGauges(std::move(trafficGauges_))
+
550 , hook(collector->make_hook(handler))
+
551 {
+
552 }
-
552
- - - -
556 };
+
553
+ + + +
557 };
-
557
- - -
560
-
561private:
-
562 void
-
- -
564 {
-
565 auto counts = m_traffic.getCounts();
- -
567 XRPL_ASSERT(
-
568 counts.size() == m_stats.trafficGauges.size(), "xrpl::OverlayImpl::collect_metrics : counts size do match");
-
569
-
570 for (auto const& [key, value] : counts)
-
571 {
-
572 auto it = m_stats.trafficGauges.find(key);
-
573 if (it == m_stats.trafficGauges.end())
-
574 continue;
-
575
-
576 auto& gauge = it->second;
-
577
-
578 XRPL_ASSERT(
-
579 gauge.name == value.name,
-
580 "xrpl::OverlayImpl::collect_metrics : gauge and counter "
-
581 "match");
-
582
-
583 gauge.bytesIn = value.bytesIn;
-
584 gauge.bytesOut = value.bytesOut;
-
585 gauge.messagesIn = value.messagesIn;
-
586 gauge.messagesOut = value.messagesOut;
-
587 }
-
588
- -
590 }
+
558
+ + +
561
+
562private:
+
563 void
+
+ +
565 {
+
566 auto counts = m_traffic.getCounts();
+ +
568 XRPL_ASSERT(
+
569 counts.size() == m_stats.trafficGauges.size(), "xrpl::OverlayImpl::collect_metrics : counts size do match");
+
570
+
571 for (auto const& [key, value] : counts)
+
572 {
+
573 auto it = m_stats.trafficGauges.find(key);
+
574 if (it == m_stats.trafficGauges.end())
+
575 continue;
+
576
+
577 auto& gauge = it->second;
+
578
+
579 XRPL_ASSERT(
+
580 gauge.name == value.name,
+
581 "xrpl::OverlayImpl::collect_metrics : gauge and counter "
+
582 "match");
+
583
+
584 gauge.bytesIn = value.bytesIn;
+
585 gauge.bytesOut = value.bytesOut;
+
586 gauge.messagesIn = value.messagesIn;
+
587 gauge.messagesOut = value.messagesOut;
+
588 }
+
589
+ +
591 }
-
591};
+
592};
-
592
-
593} // namespace xrpl
+
593
+
594} // namespace xrpl
T bind(T... args)
@@ -668,101 +669,101 @@ $(document).ready(function() { init_codefold(0); });
virtual void stop()=0
-
void deletePeer(Peer::id_t id)
Called when the peer is deleted.
- -
void remove(std::shared_ptr< PeerFinder::Slot > const &slot)
+
void deletePeer(Peer::id_t id)
Called when the peer is deleted.
+ +
void remove(std::shared_ptr< PeerFinder::Slot > const &slot)
std::weak_ptr< Timer > timer_
Definition OverlayImpl.h:89
boost::asio::io_context & io_context_
Definition OverlayImpl.h:84
-
bool processRequest(http_request_type const &req, Handoff &handoff)
Handles non-peer protocol requests.
+
bool processRequest(http_request_type const &req, Handoff &handoff)
Handles non-peer protocol requests.
boost::asio::ip::address address_type
Definition OverlayImpl.h:62
-
static bool isPeerUpgrade(http_request_type const &request)
+
static bool isPeerUpgrade(http_request_type const &request)
Resource::Manager & m_resourceManager
Definition OverlayImpl.h:94
boost::system::error_code error_code
Definition OverlayImpl.h:64
-
bool processCrawl(http_request_type const &req, Handoff &handoff)
Handles crawl requests.
+
bool processCrawl(http_request_type const &req, Handoff &handoff)
Handles crawl requests.
OverlayImpl(OverlayImpl const &)=delete
-
void addTxMetrics(Args... args)
Add tx reduce-relay metrics.
-
bool processHealth(http_request_type const &req, Handoff &handoff)
Handles health requests.
-
Json::Value getServerCounts()
Returns information about the local server's performance counters.
-
std::mutex m_statsMutex
-
void incPeerDisconnectCharges() override
-
void activate(std::shared_ptr< PeerImp > const &peer)
Called when a peer has connected successfully This is called after the peer handshake has been comple...
-
Handoff onHandoff(std::unique_ptr< stream_type > &&bundle, http_request_type &&request, endpoint_type remote_endpoint) override
Conditionally accept an incoming HTTP request.
+
void addTxMetrics(Args... args)
Add tx reduce-relay metrics.
+
bool processHealth(http_request_type const &req, Handoff &handoff)
Handles health requests.
+
Json::Value getServerCounts()
Returns information about the local server's performance counters.
+
std::mutex m_statsMutex
+
void incPeerDisconnectCharges() override
+
void activate(std::shared_ptr< PeerImp > const &peer)
Called when a peer has connected successfully This is called after the peer handshake has been comple...
+
Handoff onHandoff(std::unique_ptr< stream_type > &&bundle, http_request_type &&request, endpoint_type remote_endpoint) override
Conditionally accept an incoming HTTP request.
std::atomic< uint64_t > peerDisconnectsCharges_
boost::asio::ip::tcp::socket socket_type
Definition OverlayImpl.h:61
std::optional< boost::asio::executor_work_guard< boost::asio::io_context::executor_type > > work_
Definition OverlayImpl.h:85
-
void reportOutboundTraffic(TrafficCount::category cat, int bytes)
- -
static bool is_upgrade(boost::beast::http::header< false, Fields > const &req)
-
void for_each(UnaryFunc &&f) const
-
void stop() override
-
void connect(beast::IP::Endpoint const &remote_endpoint) override
Establish a peer connection to the specified endpoint.
+
void reportOutboundTraffic(TrafficCount::category cat, int bytes)
+ +
static bool is_upgrade(boost::beast::http::header< false, Fields > const &req)
+
void for_each(UnaryFunc &&f) const
+
void stop() override
+
void connect(beast::IP::Endpoint const &remote_endpoint) override
Establish a peer connection to the specified endpoint.
OverlayImpl & operator=(OverlayImpl const &)=delete
-
std::size_t size() const override
The number of active peers on the network Active peers are only those peers that have completed the h...
+
std::size_t size() const override
The number of active peers on the network Active peers are only those peers that have completed the h...
ServerHandler & serverHandler_
Definition OverlayImpl.h:93
-
void onManifests(std::shared_ptr< protocol::TMManifests > const &m, std::shared_ptr< PeerImp > const &from)
-
std::uint64_t getPeerDisconnectCharges() const override
-
void broadcast(protocol::TMProposeSet &m) override
Broadcast a proposal.
-
std::shared_ptr< Writer > makeErrorResponse(std::shared_ptr< PeerFinder::Slot > const &slot, http_request_type const &request, address_type remote_address, std::string msg)
+
void onManifests(std::shared_ptr< protocol::TMManifests > const &m, std::shared_ptr< PeerImp > const &from)
+
std::uint64_t getPeerDisconnectCharges() const override
+
void broadcast(protocol::TMProposeSet &m) override
Broadcast a proposal.
+
std::shared_ptr< Writer > makeErrorResponse(std::shared_ptr< PeerFinder::Slot > const &slot, http_request_type const &request, address_type remote_address, std::string msg)
reduce_relay::Slots< UptimeClock > slots_
hash_map< Peer::id_t, std::weak_ptr< PeerImp > > ids_
Definition OverlayImpl.h:98
-
void deleteIdlePeers()
Check if peers stopped relaying messages and if slots stopped receiving messages from the validator.
+
void deleteIdlePeers()
Check if peers stopped relaying messages and if slots stopped receiving messages from the validator.
TrafficCount m_traffic
Definition OverlayImpl.h:96
-
void squelch(PublicKey const &validator, Peer::id_t const id, std::uint32_t squelchDuration) const override
Squelch handler.
-
void sendTxQueue()
Send once a second transactions' hashes aggregated by peers.
+
void squelch(PublicKey const &validator, Peer::id_t const id, std::uint32_t squelchDuration) const override
Squelch handler.
+
void sendTxQueue()
Send once a second transactions' hashes aggregated by peers.
std::shared_ptr< Message > manifestMessage_
std::unique_ptr< PeerFinder::Manager > m_peerFinder
Definition OverlayImpl.h:95
-
std::uint64_t getPeerDisconnect() const override
+
std::uint64_t getPeerDisconnect() const override
std::optional< std::uint32_t > manifestListSeq_
-
Json::Value txMetrics() const override
Returns tx reduce-relay metrics.
- -
void onWrite(beast::PropertyStream::Map &stream) override
Subclass override.
-
void add_active(std::shared_ptr< PeerImp > const &peer)
- +
Json::Value txMetrics() const override
Returns tx reduce-relay metrics.
+ +
void onWrite(beast::PropertyStream::Map &stream) override
Subclass override.
+
void add_active(std::shared_ptr< PeerImp > const &peer)
+
PeerFinder::Manager & peerFinder()
Application & app_
Definition OverlayImpl.h:83
std::recursive_mutex mutex_
Definition OverlayImpl.h:87
Resource::Manager & resourceManager()
-
void incPeerDisconnect() override
Increment and retrieve counters for total peer disconnects, and disconnects we initiate for excessive...
+
void incPeerDisconnect() override
Increment and retrieve counters for total peer disconnects, and disconnects we initiate for excessive...
beast::Journal const journal_
Definition OverlayImpl.h:92
boost::asio::ip::tcp::endpoint endpoint_type
Definition OverlayImpl.h:63
-
void onPeerDeactivate(Peer::id_t id)
+
void onPeerDeactivate(Peer::id_t id)
std::mutex manifestLock_
boost::asio::strand< boost::asio::io_context::executor_type > strand_
Definition OverlayImpl.h:86
-
Json::Value json() override
Return diagnostics on the status of all peers.
-
static std::string makePrefix(std::uint32_t id)
+
Json::Value json() override
Return diagnostics on the status of all peers.
+
static std::string makePrefix(std::uint32_t id)
Setup const & setup() const
std::atomic< uint64_t > peerDisconnects_
- -
std::set< Peer::id_t > relay(protocol::TMProposeSet &m, uint256 const &uid, PublicKey const &validator) override
Relay a proposal.
-
static bool is_upgrade(boost::beast::http::header< true, Fields > const &req)
+ +
std::set< Peer::id_t > relay(protocol::TMProposeSet &m, uint256 const &uid, PublicKey const &validator) override
Relay a proposal.
+
static bool is_upgrade(boost::beast::http::header< true, Fields > const &req)
metrics::TxMetrics txMetrics_
boost::container::flat_map< Child *, std::weak_ptr< Child > > list_
Definition OverlayImpl.h:90
-
int limit() override
Returns the maximum number of peers we are configured to allow.
+
int limit() override
Returns the maximum number of peers we are configured to allow.
std::condition_variable_any cond_
Definition OverlayImpl.h:88
hash_map< std::shared_ptr< PeerFinder::Slot >, std::weak_ptr< PeerImp > > m_peers
Definition OverlayImpl.h:97
-
std::shared_ptr< Message > getManifestsMessage()
-
Json::Value getUnlInfo()
Returns information about the local server's UNL.
-
std::shared_ptr< Peer > findPeerByPublicKey(PublicKey const &pubKey) override
Returns the peer with the matching public key, or null.
+
std::shared_ptr< Message > getManifestsMessage()
+
Json::Value getUnlInfo()
Returns information about the local server's UNL.
+
std::shared_ptr< Peer > findPeerByPublicKey(PublicKey const &pubKey) override
Returns the peer with the matching public key, or null.
std::atomic< uint64_t > jqTransOverflow_
-
std::shared_ptr< Writer > makeRedirectResponse(std::shared_ptr< PeerFinder::Slot > const &slot, http_request_type const &request, address_type remote_address)
-
std::optional< std::uint32_t > networkID() const override
Returns the ID of the network this server is configured for, if any.
+
std::shared_ptr< Writer > makeRedirectResponse(std::shared_ptr< PeerFinder::Slot > const &slot, http_request_type const &request, address_type remote_address)
+
std::optional< std::uint32_t > networkID() const override
Returns the ID of the network this server is configured for, if any.
std::atomic< Peer::id_t > next_id_
-
void reportInboundTraffic(TrafficCount::category cat, int bytes)
-
bool processValidatorList(http_request_type const &req, Handoff &handoff)
Handles validator list requests.
-
void checkTracking(std::uint32_t) override
Calls the checkTracking function on each peer.
+
void reportInboundTraffic(TrafficCount::category cat, int bytes)
+
bool processValidatorList(http_request_type const &req, Handoff &handoff)
Handles validator list requests.
+
void checkTracking(std::uint32_t) override
Calls the checkTracking function on each peer.
Resolver & m_resolver
Definition OverlayImpl.h:99
-
Json::Value getServerInfo()
Returns information about the local server.
-
void updateSlotAndSquelch(uint256 const &key, PublicKey const &validator, std::set< Peer::id_t > &&peers, protocol::MessageType type)
Updates message count for validator/peer.
-
std::uint64_t getJqTransOverflow() const override
-
static bool isPeerUpgrade(boost::beast::http::response< Body > const &response)
-
std::shared_ptr< Peer > findPeerByShortID(Peer::id_t const &id) const override
Returns the peer with the matching short id, or null.
-
void start() override
-
PeerSequence getActivePeers() const override
Returns a sequence representing the current list of peers.
-
Json::Value getOverlayInfo()
Returns information about peers on the overlay network.
-
void unsquelch(PublicKey const &validator, Peer::id_t id) const override
Unsquelch handler.
-
void incJqTransOverflow() override
Increment and retrieve counter for transaction job queue overflows.
+
Json::Value getServerInfo()
Returns information about the local server.
+
void updateSlotAndSquelch(uint256 const &key, PublicKey const &validator, std::set< Peer::id_t > &&peers, protocol::MessageType type)
Updates message count for validator/peer.
+
std::uint64_t getJqTransOverflow() const override
+
static bool isPeerUpgrade(boost::beast::http::response< Body > const &response)
+
std::shared_ptr< Peer > findPeerByShortID(Peer::id_t const &id) const override
Returns the peer with the matching short id, or null.
+
void start() override
+
PeerSequence getActivePeers() const override
Returns a sequence representing the current list of peers.
+
Json::Value getOverlayInfo()
Returns information about peers on the overlay network.
+
void unsquelch(PublicKey const &validator, Peer::id_t id) const override
Unsquelch handler.
+
void incJqTransOverflow() override
Increment and retrieve counter for transaction job queue overflows.
Manages the set of connected peers.
Definition Overlay.h:29
std::vector< std::shared_ptr< Peer > > PeerSequence
Definition Overlay.h:56
Maintains a set of IP addresses used for getting into the network.
@@ -774,7 +775,7 @@ $(document).ready(function() { init_codefold(0); });
auto const & getCounts() const
An up-to-date copy of all the counters.
-
Slots is a container for validator's Slot and handles Slot update when a message is received from a v...
+
Slots is a container for validator's Slot and handles Slot update when a message is received from a v...
@@ -797,24 +798,24 @@ $(document).ready(function() { init_codefold(0); });
Used to indicate the result of a server connection handoff.
Definition Handoff.h:18
- -
beast::insight::Gauge peerDisconnects
-
std::unordered_map< TrafficCount::category, TrafficGauges > trafficGauges
-
Stats(Handler const &handler, beast::insight::Collector::ptr const &collector, std::unordered_map< TrafficCount::category, TrafficGauges > &&trafficGauges_)
-
beast::insight::Hook hook
+ +
beast::insight::Gauge peerDisconnects
+
std::unordered_map< TrafficCount::category, TrafficGauges > trafficGauges
+
Stats(Handler const &handler, beast::insight::Collector::ptr const &collector, std::unordered_map< TrafficCount::category, TrafficGauges > &&trafficGauges_)
+
beast::insight::Hook hook
-
void on_timer(error_code ec)
+
void on_timer(error_code ec)
boost::asio::basic_waitable_timer< clock_type > timer_
Definition OverlayImpl.h:68
- -
beast::insight::Gauge messagesOut
- -
beast::insight::Gauge bytesOut
-
TrafficGauges(std::string const &name, beast::insight::Collector::ptr const &collector)
-
beast::insight::Gauge messagesIn
-
beast::insight::Gauge bytesIn
+ +
beast::insight::Gauge messagesOut
+ +
beast::insight::Gauge bytesOut
+
TrafficGauges(std::string const &name, beast::insight::Collector::ptr const &collector)
+
beast::insight::Gauge messagesIn
+
beast::insight::Gauge bytesIn
std::optional< std::uint32_t > networkID
Definition Overlay.h:52
Run transaction reduce-relay feature related metrics.
Definition TxMetrics.h:69
diff --git a/OversizeMeta__test_8cpp_source.html b/OversizeMeta__test_8cpp_source.html index 1d641b5da3..7c7f155960 100644 --- a/OversizeMeta__test_8cpp_source.html +++ b/OversizeMeta__test_8cpp_source.html @@ -308,7 +308,7 @@ $(document).ready(function() { init_codefold(0); });
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:284
Converts to IOU Issue or STAmount.
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/OwnerInfo_8cpp_source.html b/OwnerInfo_8cpp_source.html index 49461a9b2c..235f995ada 100644 --- a/OwnerInfo_8cpp_source.html +++ b/OwnerInfo_8cpp_source.html @@ -125,7 +125,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
std::shared_ptr< Ledger const > getClosedLedger()
std::shared_ptr< ReadView const > getCurrentLedger()
virtual Json::Value getOwnerInfo(std::shared_ptr< ReadView const > lpLedger, AccountID const &account)=0
diff --git a/OwnerInfo__test_8cpp_source.html b/OwnerInfo__test_8cpp_source.html index 13da1d97ed..3ac2215adc 100644 --- a/OwnerInfo__test_8cpp_source.html +++ b/OwnerInfo__test_8cpp_source.html @@ -255,9 +255,9 @@ $(document).ready(function() { init_codefold(0); }); -
STAmount const & value() const noexcept
Definition STAmount.h:560
+
STAmount const & value() const noexcept
Definition STAmount.h:562
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
owner_count< ltOFFER > offers
Match the number of offers in the account's owner directory.
Definition owners.h:66
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
diff --git a/PathFind_8cpp_source.html b/PathFind_8cpp_source.html index d52f6a83a9..d8cd2a33ef 100644 --- a/PathFind_8cpp_source.html +++ b/PathFind_8cpp_source.html @@ -148,9 +148,9 @@ $(document).ready(function() { init_codefold(0); });
63
64} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isString() const
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
int PATH_SEARCH_MAX
Definition Config.h:180
void clearRequest()
Definition InfoSub.cpp:101
diff --git a/PathRequest_8cpp_source.html b/PathRequest_8cpp_source.html index 17bcdfaf3c..e2ca1937ac 100644 --- a/PathRequest_8cpp_source.html +++ b/PathRequest_8cpp_source.html @@ -823,11 +823,11 @@ $(document).ready(function() { init_codefold(0); });
T clamp(T... args)
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
Value & append(Value const &value)
Append value to array at the end.
+
bool isArray() const
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:300
Stream info() const
Definition Journal.h:306
diff --git a/Path__test_8cpp_source.html b/Path__test_8cpp_source.html index 4d93038cd4..5e7389aa11 100644 --- a/Path__test_8cpp_source.html +++ b/Path__test_8cpp_source.html @@ -1916,9 +1916,9 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
const_iterator begin() const
-
const_iterator end() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
const_iterator begin() const
+
const_iterator end() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
A consumption charge.
Definition Charge.h:10
@@ -2007,7 +2007,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
bool same(STPathSet const &st1, Args const &... args)
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
uint256 setupDomain(jtx::Env &env, std::vector< jtx::Account > const &accounts, jtx::Account const &domainOwner, std::string const &credType)
STPathElement IPE(Issue const &iss)
diff --git a/PayChan_8cpp_source.html b/PayChan_8cpp_source.html index acc91de3b3..dd4dfcafb4 100644 --- a/PayChan_8cpp_source.html +++ b/PayChan_8cpp_source.html @@ -701,7 +701,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfRenew
Definition TxFlags.h:114
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Definition View.cpp:1020
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3436
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:955
@@ -712,7 +712,7 @@ $(document).ready(function() { init_codefold(0); });
@ temBAD_AMOUNT
Definition TER.h:69
@ temBAD_SIGNATURE
Definition TER.h:85
@ temBAD_SIGNER
Definition TER.h:95
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecDIR_FULL
Definition TER.h:268
@ tecUNFUNDED_PAYMENT
Definition TER.h:266
@ tecNO_ENTRY
Definition TER.h:287
diff --git a/PayChan__test_8cpp_source.html b/PayChan__test_8cpp_source.html index eac245fd3f..59b860dde3 100644 --- a/PayChan__test_8cpp_source.html +++ b/PayChan__test_8cpp_source.html @@ -2096,7 +2096,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount, NetClock::duration const &settleDelay, PublicKey const &pk, std::optional< NetClock::time_point > const &cancelAfter, std::optional< std::uint32_t > const &dstTag)
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
void fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition AMMTest.cpp:18
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
FeatureBitset testable_amendments()
Definition Env.h:76
void sign(Json::Value &jv, Account const &account, Json::Value &sigObject)
Sign automatically into a specific Json field of the jv object.
Definition utility.cpp:27
@@ -2113,7 +2113,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:65
void serializePayChanAuthorization(Serializer &msg, uint256 const &key, XRPAmount const &amt)
constexpr std::uint32_t tfRenew
Definition TxFlags.h:114
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void forAllApiVersions(Fn const &fn, Args &&... args)
Definition ApiVersion.h:144
static std::string sliceToHex(Slice const &slice)
Definition PublicKey.cpp:75
@@ -2125,7 +2125,7 @@ $(document).ready(function() { init_codefold(0); });
@ temBAD_AMOUNT
Definition TER.h:69
@ temBAD_SIGNATURE
Definition TER.h:85
@ temBAD_SIGNER
Definition TER.h:95
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
constexpr std::uint32_t asfDisallowXRP
Definition TxFlags.h:59
@ tecUNFUNDED_PAYMENT
Definition TER.h:266
@ tecNO_ENTRY
Definition TER.h:287
diff --git a/PaySteps_8cpp_source.html b/PaySteps_8cpp_source.html index 6b473861fc..3d5482d650 100644 --- a/PaySteps_8cpp_source.html +++ b/PaySteps_8cpp_source.html @@ -677,8 +677,8 @@ $(document).ready(function() { init_codefold(0); });
Stream warn() const
Definition Journal.h:312
Maintains AMM info per overall payment engine execution and individual iteration.
Definition AMMContext.h:16
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
-
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:161
-
exponent_type exponent() const noexcept
Definition IOUAmount.h:155
+
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:163
+
exponent_type exponent() const noexcept
Definition IOUAmount.h:157
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
AccountID account
Definition Issue.h:16
@@ -741,7 +741,7 @@ $(document).ready(function() { init_codefold(0); });
static bool isXRPAccount(STPathElement const &pe)
Definition PaySteps.cpp:41
OfferCrossing
Definition Steps.h:25
template bool isDirectXrpToXrp< IOUAmount, XRPAmount >(Strand const &strand)
-
bool isTemMalformed(TER x) noexcept
Definition TER.h:631
+
bool isTemMalformed(TER x) noexcept
Definition TER.h:625
@ tesSUCCESS
Definition TER.h:225
std::pair< TER, Strand > toStrand(ReadView const &view, AccountID const &src, AccountID const &dst, Issue const &deliver, std::optional< Quality > const &limitQuality, std::optional< Issue > const &sendMaxIssue, STPath const &path, bool ownerPaysTransferFee, OfferCrossing offerCrossing, AMMContext &ammContext, std::optional< uint256 > const &domainID, beast::Journal j)
Create a Strand for the specified path.
Definition PaySteps.cpp:100
diff --git a/PayStrand__test_8cpp_source.html b/PayStrand__test_8cpp_source.html index 3e786f77fe..967e5f154b 100644 --- a/PayStrand__test_8cpp_source.html +++ b/PayStrand__test_8cpp_source.html @@ -1407,7 +1407,7 @@ $(document).ready(function() { init_codefold(0); });
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:214
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
STPathElement allPathElements(AccountID const &a, Issue const &iss)
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
diff --git a/PaymentSandbox__test_8cpp_source.html b/PaymentSandbox__test_8cpp_source.html index 06ff53e431..c39a631e99 100644 --- a/PaymentSandbox__test_8cpp_source.html +++ b/PaymentSandbox__test_8cpp_source.html @@ -486,8 +486,8 @@ $(document).ready(function() { init_codefold(0); });
Set Paths, SendMax on a JTx.
Definition paths.h:15
Set the flags on a JTx.
Definition txflags.h:11
-
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:111
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:112
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
diff --git a/Payment_8cpp_source.html b/Payment_8cpp_source.html index 670d23399d..80dafe559d 100644 --- a/Payment_8cpp_source.html +++ b/Payment_8cpp_source.html @@ -773,13 +773,13 @@ $(document).ready(function() { init_codefold(0); });
@ telNO_DST_PARTIAL
Definition TER.h:38
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
@ terNO_DELEGATE_PERMISSION
Definition TER.h:210
-
bool isTerRetry(TER x) noexcept
Definition TER.h:643
+
bool isTerRetry(TER x) noexcept
Definition TER.h:637
bool isXRP(AccountID const &c)
Definition AccountID.h:70
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
STAmount getMaxSourceAmount(AccountID const &account, STAmount const &dstAmount, std::optional< STAmount > const &sendMax)
Definition Payment.cpp:31
constexpr std::uint32_t tfPaymentMask
Definition TxFlags.h:90
@ tefINTERNAL
Definition TER.h:153
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
Definition View.cpp:219
constexpr std::uint32_t tfLimitQuality
Definition TxFlags.h:89
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
@@ -804,7 +804,7 @@ $(document).ready(function() { init_codefold(0); });
@ temDISABLED
Definition TER.h:94
@ temBAD_AMOUNT
Definition TER.h:69
@ temREDUNDANT
Definition TER.h:92
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecLOCKED
Definition TER.h:339
@ tecPATH_PARTIAL
Definition TER.h:263
@ tecUNFUNDED_PAYMENT
Definition TER.h:266
diff --git a/PeerImp_8cpp_source.html b/PeerImp_8cpp_source.html index 1b660d27ff..67599572c6 100644 --- a/PeerImp_8cpp_source.html +++ b/PeerImp_8cpp_source.html @@ -655,2885 +655,2884 @@ $(document).ready(function() { init_codefold(0); });
533{
534 if (!strand_.running_in_this_thread())
-
535 return post(
-
536 strand_, std::bind((void(Peer::*)(std::string const&)) & PeerImp::fail, shared_from_this(), reason));
-
537
-
538 if (!socket_.is_open())
-
539 return;
-
540
-
541 // Call to name() locks, log only if the message will be outputted
- -
543 {
-
544 std::string const n = name();
-
545 JLOG(journal_.warn()) << n << " failed: " << reason;
-
546 }
-
547
-
548 shutdown();
-
549}
+
535 return post(strand_, std::bind((void (Peer::*)(std::string const&))&PeerImp::fail, shared_from_this(), reason));
+
536
+
537 if (!socket_.is_open())
+
538 return;
+
539
+
540 // Call to name() locks, log only if the message will be outputted
+ +
542 {
+
543 std::string const n = name();
+
544 JLOG(journal_.warn()) << n << " failed: " << reason;
+
545 }
+
546
+
547 shutdown();
+
548}
-
550
-
551void
-
- -
553{
-
554 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::tryAsyncShutdown : strand in this thread");
-
555
- -
557 return;
-
558
- -
560 return;
-
561
-
562 shutdownStarted_ = true;
-
563
-
564 setTimer(shutdownTimerInterval);
-
565
-
566 // gracefully shutdown the SSL socket, performing a shutdown handshake
-
567 stream_.async_shutdown(
-
568 bind_executor(strand_, std::bind(&PeerImp::onShutdown, shared_from_this(), std::placeholders::_1)));
-
569}
+
549
+
550void
+
+ +
552{
+
553 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::tryAsyncShutdown : strand in this thread");
+
554
+ +
556 return;
+
557
+ +
559 return;
+
560
+
561 shutdownStarted_ = true;
+
562
+
563 setTimer(shutdownTimerInterval);
+
564
+
565 // gracefully shutdown the SSL socket, performing a shutdown handshake
+
566 stream_.async_shutdown(
+
567 bind_executor(strand_, std::bind(&PeerImp::onShutdown, shared_from_this(), std::placeholders::_1)));
+
568}
-
570
-
571void
-
- -
573{
-
574 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::shutdown: strand in this thread");
-
575
-
576 if (!socket_.is_open() || shutdown_)
-
577 return;
-
578
-
579 shutdown_ = true;
-
580
-
581 boost::beast::get_lowest_layer(stream_).cancel();
-
582
- -
584}
+
569
+
570void
+
+ +
572{
+
573 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::shutdown: strand in this thread");
+
574
+
575 if (!socket_.is_open() || shutdown_)
+
576 return;
+
577
+
578 shutdown_ = true;
+
579
+
580 boost::beast::get_lowest_layer(stream_).cancel();
+
581
+ +
583}
-
585
-
586void
-
- -
588{
-
589 cancelTimer();
-
590 if (ec)
-
591 {
-
592 // - eof: the stream was cleanly closed
-
593 // - operation_aborted: an expired timer (slow shutdown)
-
594 // - stream_truncated: the tcp connection closed (no handshake) it could
-
595 // occur if a peer does not perform a graceful disconnect
-
596 // - broken_pipe: the peer is gone
-
597 bool shouldLog =
-
598 (ec != boost::asio::error::eof && ec != boost::asio::error::operation_aborted &&
-
599 ec.message().find("application data after close notify") == std::string::npos);
-
600
-
601 if (shouldLog)
-
602 {
-
603 JLOG(journal_.debug()) << "onShutdown: " << ec.message();
-
604 }
-
605 }
-
606
-
607 close();
-
608}
+
584
+
585void
+
+ +
587{
+
588 cancelTimer();
+
589 if (ec)
+
590 {
+
591 // - eof: the stream was cleanly closed
+
592 // - operation_aborted: an expired timer (slow shutdown)
+
593 // - stream_truncated: the tcp connection closed (no handshake) it could
+
594 // occur if a peer does not perform a graceful disconnect
+
595 // - broken_pipe: the peer is gone
+
596 bool shouldLog =
+
597 (ec != boost::asio::error::eof && ec != boost::asio::error::operation_aborted &&
+
598 ec.message().find("application data after close notify") == std::string::npos);
+
599
+
600 if (shouldLog)
+
601 {
+
602 JLOG(journal_.debug()) << "onShutdown: " << ec.message();
+
603 }
+
604 }
+
605
+
606 close();
+
607}
-
609
-
610void
-
- -
612{
-
613 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::close : strand in this thread");
-
614
-
615 if (!socket_.is_open())
-
616 return;
-
617
-
618 cancelTimer();
-
619
-
620 error_code ec;
-
621 socket_.close(ec);
-
622
- -
624
-
625 // The rationale for using different severity levels is that
-
626 // outbound connections are under our control and may be logged
-
627 // at a higher level, but inbound connections are more numerous and
-
628 // uncontrolled so to prevent log flooding the severity is reduced.
-
629 JLOG((inbound_ ? journal_.debug() : journal_.info())) << "close: Closed";
-
630}
+
608
+
609void
+
+ +
611{
+
612 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::close : strand in this thread");
+
613
+
614 if (!socket_.is_open())
+
615 return;
+
616
+
617 cancelTimer();
+
618
+
619 error_code ec;
+
620 socket_.close(ec);
+
621
+ +
623
+
624 // The rationale for using different severity levels is that
+
625 // outbound connections are under our control and may be logged
+
626 // at a higher level, but inbound connections are more numerous and
+
627 // uncontrolled so to prevent log flooding the severity is reduced.
+
628 JLOG((inbound_ ? journal_.debug() : journal_.info())) << "close: Closed";
+
629}
-
631
-
632//------------------------------------------------------------------------------
-
633
-
634void
-
- -
636{
-
637 try
-
638 {
-
639 timer_.expires_after(interval);
-
640 }
-
641 catch (std::exception const& ex)
-
642 {
-
643 JLOG(journal_.error()) << "setTimer: " << ex.what();
-
644 return shutdown();
-
645 }
-
646
-
647 timer_.async_wait(bind_executor(strand_, std::bind(&PeerImp::onTimer, shared_from_this(), std::placeholders::_1)));
-
648}
+
630
+
631//------------------------------------------------------------------------------
+
632
+
633void
+
+ +
635{
+
636 try
+
637 {
+
638 timer_.expires_after(interval);
+
639 }
+
640 catch (std::exception const& ex)
+
641 {
+
642 JLOG(journal_.error()) << "setTimer: " << ex.what();
+
643 return shutdown();
+
644 }
+
645
+
646 timer_.async_wait(bind_executor(strand_, std::bind(&PeerImp::onTimer, shared_from_this(), std::placeholders::_1)));
+
647}
-
649
-
650//------------------------------------------------------------------------------
-
651
- -
- -
654{
- -
656 ss << "[" << fingerprint << "] ";
-
657 return ss.str();
-
658}
+
648
+
649//------------------------------------------------------------------------------
+
650
+ +
+ +
653{
+ +
655 ss << "[" << fingerprint << "] ";
+
656 return ss.str();
+
657}
-
659
-
660void
-
- -
662{
-
663 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::onTimer : strand in this thread");
-
664
-
665 if (!socket_.is_open())
-
666 return;
-
667
-
668 if (ec)
-
669 {
-
670 // do not initiate shutdown, timers are frequently cancelled
-
671 if (ec == boost::asio::error::operation_aborted)
-
672 return;
-
673
-
674 // This should never happen
-
675 JLOG(journal_.error()) << "onTimer: " << ec.message();
-
676 return close();
-
677 }
-
678
-
679 // the timer expired before the shutdown completed
-
680 // force close the connection
-
681 if (shutdown_)
-
682 {
-
683 JLOG(journal_.debug()) << "onTimer: shutdown timer expired";
-
684 return close();
-
685 }
-
686
- -
688 return fail("Large send queue");
-
689
-
690 if (auto const t = tracking_.load(); !inbound_ && t != Tracking::converged)
-
691 {
-
692 clock_type::duration duration;
-
693
-
694 {
- -
696 duration = clock_type::now() - trackingTime_;
-
697 }
-
698
-
699 if ((t == Tracking::diverged && (duration > app_.config().MAX_DIVERGED_TIME)) ||
-
700 (t == Tracking::unknown && (duration > app_.config().MAX_UNKNOWN_TIME)))
-
701 {
- -
703 return fail("Not useful");
-
704 }
-
705 }
-
706
-
707 // Already waiting for PONG
-
708 if (lastPingSeq_)
-
709 return fail("Ping Timeout");
-
710
- -
712 lastPingSeq_ = rand_int<std::uint32_t>();
-
713
-
714 protocol::TMPing message;
-
715 message.set_type(protocol::TMPing::ptPING);
-
716 message.set_seq(*lastPingSeq_);
-
717
-
718 send(std::make_shared<Message>(message, protocol::mtPING));
-
719
-
720 setTimer(peerTimerInterval);
-
721}
+
658
+
659void
+
+ +
661{
+
662 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::onTimer : strand in this thread");
+
663
+
664 if (!socket_.is_open())
+
665 return;
+
666
+
667 if (ec)
+
668 {
+
669 // do not initiate shutdown, timers are frequently cancelled
+
670 if (ec == boost::asio::error::operation_aborted)
+
671 return;
+
672
+
673 // This should never happen
+
674 JLOG(journal_.error()) << "onTimer: " << ec.message();
+
675 return close();
+
676 }
+
677
+
678 // the timer expired before the shutdown completed
+
679 // force close the connection
+
680 if (shutdown_)
+
681 {
+
682 JLOG(journal_.debug()) << "onTimer: shutdown timer expired";
+
683 return close();
+
684 }
+
685
+ +
687 return fail("Large send queue");
+
688
+
689 if (auto const t = tracking_.load(); !inbound_ && t != Tracking::converged)
+
690 {
+
691 clock_type::duration duration;
+
692
+
693 {
+ +
695 duration = clock_type::now() - trackingTime_;
+
696 }
+
697
+
698 if ((t == Tracking::diverged && (duration > app_.config().MAX_DIVERGED_TIME)) ||
+
699 (t == Tracking::unknown && (duration > app_.config().MAX_UNKNOWN_TIME)))
+
700 {
+ +
702 return fail("Not useful");
+
703 }
+
704 }
+
705
+
706 // Already waiting for PONG
+
707 if (lastPingSeq_)
+
708 return fail("Ping Timeout");
+
709
+ +
711 lastPingSeq_ = rand_int<std::uint32_t>();
+
712
+
713 protocol::TMPing message;
+
714 message.set_type(protocol::TMPing::ptPING);
+
715 message.set_seq(*lastPingSeq_);
+
716
+
717 send(std::make_shared<Message>(message, protocol::mtPING));
+
718
+
719 setTimer(peerTimerInterval);
+
720}
-
722
-
723void
-
- -
725{
-
726 try
-
727 {
-
728 timer_.cancel();
-
729 }
-
730 catch (std::exception const& ex)
-
731 {
-
732 JLOG(journal_.error()) << "cancelTimer: " << ex.what();
-
733 }
-
734}
+
721
+
722void
+
+ +
724{
+
725 try
+
726 {
+
727 timer_.cancel();
+
728 }
+
729 catch (std::exception const& ex)
+
730 {
+
731 JLOG(journal_.error()) << "cancelTimer: " << ex.what();
+
732 }
+
733}
-
735
-
736//------------------------------------------------------------------------------
-
737void
-
- -
739{
-
740 XRPL_ASSERT(read_buffer_.size() == 0, "xrpl::PeerImp::doAccept : empty read buffer");
-
741
-
742 JLOG(journal_.debug()) << "doAccept";
-
743
-
744 // a shutdown was initiated before the handshake, there is nothing to do
-
745 if (shutdown_)
-
746 return tryAsyncShutdown();
-
747
-
748 auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
-
749
-
750 // This shouldn't fail since we already computed
-
751 // the shared value successfully in OverlayImpl
-
752 if (!sharedValue)
-
753 return fail("makeSharedValue: Unexpected failure");
-
754
-
755 JLOG(journal_.debug()) << "Protocol: " << to_string(protocol_);
-
756
-
757 if (auto member = app_.cluster().member(publicKey_))
-
758 {
-
759 {
- -
761 name_ = *member;
-
762 }
-
763 JLOG(journal_.info()) << "Cluster name: " << *member;
-
764 }
-
765
- -
767
-
768 // XXX Set timer: connection is in grace period to be useful.
-
769 // XXX Set timer: connection idle (idle may vary depending on connection
-
770 // type.)
-
771
- -
773
-
774 boost::beast::ostream(*write_buffer) << makeResponse(
- -
776 request_,
- - -
779 *sharedValue,
- -
781 protocol_,
-
782 app_);
-
783
-
784 // Write the whole buffer and only start protocol when that's done.
-
785 boost::asio::async_write(
-
786 stream_,
-
787 write_buffer->data(),
-
788 boost::asio::transfer_all(),
-
789 bind_executor(
-
790 strand_, [this, write_buffer, self = shared_from_this()](error_code ec, std::size_t bytes_transferred) {
-
791 if (!socket_.is_open())
-
792 return;
-
793 if (ec == boost::asio::error::operation_aborted)
-
794 return tryAsyncShutdown();
-
795 if (ec)
-
796 return fail("onWriteResponse", ec);
-
797 if (write_buffer->size() == bytes_transferred)
-
798 return doProtocolStart();
-
799 return fail("Failed to write header");
-
800 }));
-
801}
+
734
+
735//------------------------------------------------------------------------------
+
736void
+
+ +
738{
+
739 XRPL_ASSERT(read_buffer_.size() == 0, "xrpl::PeerImp::doAccept : empty read buffer");
+
740
+
741 JLOG(journal_.debug()) << "doAccept";
+
742
+
743 // a shutdown was initiated before the handshake, there is nothing to do
+
744 if (shutdown_)
+
745 return tryAsyncShutdown();
+
746
+
747 auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
+
748
+
749 // This shouldn't fail since we already computed
+
750 // the shared value successfully in OverlayImpl
+
751 if (!sharedValue)
+
752 return fail("makeSharedValue: Unexpected failure");
+
753
+
754 JLOG(journal_.debug()) << "Protocol: " << to_string(protocol_);
+
755
+
756 if (auto member = app_.cluster().member(publicKey_))
+
757 {
+
758 {
+ +
760 name_ = *member;
+
761 }
+
762 JLOG(journal_.info()) << "Cluster name: " << *member;
+
763 }
+
764
+ +
766
+
767 // XXX Set timer: connection is in grace period to be useful.
+
768 // XXX Set timer: connection idle (idle may vary depending on connection
+
769 // type.)
+
770
+ +
772
+
773 boost::beast::ostream(*write_buffer) << makeResponse(
+ +
775 request_,
+ + +
778 *sharedValue,
+ +
780 protocol_,
+
781 app_);
+
782
+
783 // Write the whole buffer and only start protocol when that's done.
+
784 boost::asio::async_write(
+
785 stream_,
+
786 write_buffer->data(),
+
787 boost::asio::transfer_all(),
+
788 bind_executor(
+
789 strand_, [this, write_buffer, self = shared_from_this()](error_code ec, std::size_t bytes_transferred) {
+
790 if (!socket_.is_open())
+
791 return;
+
792 if (ec == boost::asio::error::operation_aborted)
+
793 return tryAsyncShutdown();
+
794 if (ec)
+
795 return fail("onWriteResponse", ec);
+
796 if (write_buffer->size() == bytes_transferred)
+
797 return doProtocolStart();
+
798 return fail("Failed to write header");
+
799 }));
+
800}
-
802
- -
- -
805{
-
806 std::shared_lock read_lock{nameMutex_};
-
807 return name_;
-
808}
+
801
+ +
+ +
804{
+
805 std::shared_lock read_lock{nameMutex_};
+
806 return name_;
+
807}
-
809
- -
- -
812{
-
813 return headers_["Server-Domain"];
-
814}
+
808
+ +
+ +
811{
+
812 return headers_["Server-Domain"];
+
813}
-
815
-
816//------------------------------------------------------------------------------
-
817
-
818// Protocol logic
-
819
-
820void
-
- -
822{
-
823 // a shutdown was initiated before the handshare, there is nothing to do
-
824 if (shutdown_)
-
825 return tryAsyncShutdown();
-
826
- -
828
-
829 // Send all the validator lists that have been loaded
- -
831 {
- -
833 std::uint32_t version,
- -
835 PublicKey const& pubKey,
-
836 std::size_t maxSequence,
-
837 uint256 const& hash) {
- -
839 *this, 0, pubKey, maxSequence, version, manifest, blobInfos, app_.getHashRouter(), p_journal_);
-
840
-
841 // Don't send it next time.
- -
843 });
-
844 }
-
845
-
846 if (auto m = overlay_.getManifestsMessage())
-
847 send(m);
-
848
-
849 setTimer(peerTimerInterval);
-
850}
+
814
+
815//------------------------------------------------------------------------------
+
816
+
817// Protocol logic
+
818
+
819void
+
+ +
821{
+
822 // a shutdown was initiated before the handshare, there is nothing to do
+
823 if (shutdown_)
+
824 return tryAsyncShutdown();
+
825
+ +
827
+
828 // Send all the validator lists that have been loaded
+ +
830 {
+ +
832 std::uint32_t version,
+ +
834 PublicKey const& pubKey,
+
835 std::size_t maxSequence,
+
836 uint256 const& hash) {
+ +
838 *this, 0, pubKey, maxSequence, version, manifest, blobInfos, app_.getHashRouter(), p_journal_);
+
839
+
840 // Don't send it next time.
+ +
842 });
+
843 }
+
844
+
845 if (auto m = overlay_.getManifestsMessage())
+
846 send(m);
+
847
+
848 setTimer(peerTimerInterval);
+
849}
-
851
-
852// Called repeatedly with protocol message data
-
853void
-
- -
855{
-
856 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::onReadMessage : strand in this thread");
-
857
-
858 readPending_ = false;
-
859
-
860 if (!socket_.is_open())
-
861 return;
-
862
-
863 if (ec)
-
864 {
-
865 if (ec == boost::asio::error::eof)
-
866 {
-
867 JLOG(journal_.debug()) << "EOF";
-
868 return shutdown();
-
869 }
-
870
-
871 if (ec == boost::asio::error::operation_aborted)
-
872 return tryAsyncShutdown();
-
873
-
874 return fail("onReadMessage", ec);
-
875 }
-
876 // we started shutdown, no reason to process further data
-
877 if (shutdown_)
-
878 return tryAsyncShutdown();
-
879
-
880 if (auto stream = journal_.trace())
-
881 {
-
882 stream << "onReadMessage: " << (bytes_transferred > 0 ? to_string(bytes_transferred) + " bytes" : "");
-
883 }
-
884
-
885 metrics_.recv.add_message(bytes_transferred);
-
886
-
887 read_buffer_.commit(bytes_transferred);
-
888
-
889 auto hint = Tuning::readBufferBytes;
-
890
-
891 while (read_buffer_.size() > 0)
-
892 {
-
893 std::size_t bytes_consumed;
-
894
-
895 using namespace std::chrono_literals;
-
896 std::tie(bytes_consumed, ec) = perf::measureDurationAndLog(
-
897 [&]() { return invokeProtocolMessage(read_buffer_.data(), *this, hint); },
-
898 "invokeProtocolMessage",
-
899 350ms,
-
900 journal_);
-
901
-
902 if (!socket_.is_open())
-
903 return;
-
904
-
905 // the error_code is produced by invokeProtocolMessage
-
906 // it could be due to a bad message
-
907 if (ec)
-
908 return fail("onReadMessage", ec);
-
909
-
910 if (bytes_consumed == 0)
-
911 break;
-
912
-
913 read_buffer_.consume(bytes_consumed);
-
914 }
-
915
-
916 // check if a shutdown was initiated while processing messages
-
917 if (shutdown_)
-
918 return tryAsyncShutdown();
-
919
-
920 readPending_ = true;
-
921
-
922 XRPL_ASSERT(!shutdownStarted_, "xrpl::PeerImp::onReadMessage : shutdown started");
-
923
-
924 // Timeout on writes only
-
925 stream_.async_read_some(
- -
927 bind_executor(
-
928 strand_,
-
929 std::bind(&PeerImp::onReadMessage, shared_from_this(), std::placeholders::_1, std::placeholders::_2)));
-
930}
+
850
+
851// Called repeatedly with protocol message data
+
852void
+
+ +
854{
+
855 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::onReadMessage : strand in this thread");
+
856
+
857 readPending_ = false;
+
858
+
859 if (!socket_.is_open())
+
860 return;
+
861
+
862 if (ec)
+
863 {
+
864 if (ec == boost::asio::error::eof)
+
865 {
+
866 JLOG(journal_.debug()) << "EOF";
+
867 return shutdown();
+
868 }
+
869
+
870 if (ec == boost::asio::error::operation_aborted)
+
871 return tryAsyncShutdown();
+
872
+
873 return fail("onReadMessage", ec);
+
874 }
+
875 // we started shutdown, no reason to process further data
+
876 if (shutdown_)
+
877 return tryAsyncShutdown();
+
878
+
879 if (auto stream = journal_.trace())
+
880 {
+
881 stream << "onReadMessage: " << (bytes_transferred > 0 ? to_string(bytes_transferred) + " bytes" : "");
+
882 }
+
883
+
884 metrics_.recv.add_message(bytes_transferred);
+
885
+
886 read_buffer_.commit(bytes_transferred);
+
887
+
888 auto hint = Tuning::readBufferBytes;
+
889
+
890 while (read_buffer_.size() > 0)
+
891 {
+
892 std::size_t bytes_consumed;
+
893
+
894 using namespace std::chrono_literals;
+
895 std::tie(bytes_consumed, ec) = perf::measureDurationAndLog(
+
896 [&]() { return invokeProtocolMessage(read_buffer_.data(), *this, hint); },
+
897 "invokeProtocolMessage",
+
898 350ms,
+
899 journal_);
+
900
+
901 if (!socket_.is_open())
+
902 return;
+
903
+
904 // the error_code is produced by invokeProtocolMessage
+
905 // it could be due to a bad message
+
906 if (ec)
+
907 return fail("onReadMessage", ec);
+
908
+
909 if (bytes_consumed == 0)
+
910 break;
+
911
+
912 read_buffer_.consume(bytes_consumed);
+
913 }
+
914
+
915 // check if a shutdown was initiated while processing messages
+
916 if (shutdown_)
+
917 return tryAsyncShutdown();
+
918
+
919 readPending_ = true;
+
920
+
921 XRPL_ASSERT(!shutdownStarted_, "xrpl::PeerImp::onReadMessage : shutdown started");
+
922
+
923 // Timeout on writes only
+
924 stream_.async_read_some(
+ +
926 bind_executor(
+
927 strand_,
+
928 std::bind(&PeerImp::onReadMessage, shared_from_this(), std::placeholders::_1, std::placeholders::_2)));
+
929}
-
931
-
932void
-
- -
934{
-
935 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::onWriteMessage : strand in this thread");
-
936
-
937 writePending_ = false;
-
938
-
939 if (!socket_.is_open())
-
940 return;
-
941
-
942 if (ec)
-
943 {
-
944 if (ec == boost::asio::error::operation_aborted)
-
945 return tryAsyncShutdown();
-
946
-
947 return fail("onWriteMessage", ec);
-
948 }
-
949
-
950 if (auto stream = journal_.trace())
-
951 {
-
952 stream << "onWriteMessage: " << (bytes_transferred > 0 ? to_string(bytes_transferred) + " bytes" : "");
-
953 }
-
954
-
955 metrics_.sent.add_message(bytes_transferred);
-
956
-
957 XRPL_ASSERT(!send_queue_.empty(), "xrpl::PeerImp::onWriteMessage : non-empty send buffer");
-
958 send_queue_.pop();
-
959
-
960 if (shutdown_)
-
961 return tryAsyncShutdown();
-
962
-
963 if (!send_queue_.empty())
-
964 {
-
965 writePending_ = true;
-
966 XRPL_ASSERT(!shutdownStarted_, "xrpl::PeerImp::onWriteMessage : shutdown started");
-
967
-
968 // Timeout on writes only
-
969 return boost::asio::async_write(
-
970 stream_,
-
971 boost::asio::buffer(send_queue_.front()->getBuffer(compressionEnabled_)),
-
972 bind_executor(
-
973 strand_,
-
974 std::bind(&PeerImp::onWriteMessage, shared_from_this(), std::placeholders::_1, std::placeholders::_2)));
-
975 }
-
976}
+
930
+
931void
+
+ +
933{
+
934 XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::onWriteMessage : strand in this thread");
+
935
+
936 writePending_ = false;
+
937
+
938 if (!socket_.is_open())
+
939 return;
+
940
+
941 if (ec)
+
942 {
+
943 if (ec == boost::asio::error::operation_aborted)
+
944 return tryAsyncShutdown();
+
945
+
946 return fail("onWriteMessage", ec);
+
947 }
+
948
+
949 if (auto stream = journal_.trace())
+
950 {
+
951 stream << "onWriteMessage: " << (bytes_transferred > 0 ? to_string(bytes_transferred) + " bytes" : "");
+
952 }
+
953
+
954 metrics_.sent.add_message(bytes_transferred);
+
955
+
956 XRPL_ASSERT(!send_queue_.empty(), "xrpl::PeerImp::onWriteMessage : non-empty send buffer");
+
957 send_queue_.pop();
+
958
+
959 if (shutdown_)
+
960 return tryAsyncShutdown();
+
961
+
962 if (!send_queue_.empty())
+
963 {
+
964 writePending_ = true;
+
965 XRPL_ASSERT(!shutdownStarted_, "xrpl::PeerImp::onWriteMessage : shutdown started");
+
966
+
967 // Timeout on writes only
+
968 return boost::asio::async_write(
+
969 stream_,
+
970 boost::asio::buffer(send_queue_.front()->getBuffer(compressionEnabled_)),
+
971 bind_executor(
+
972 strand_,
+
973 std::bind(&PeerImp::onWriteMessage, shared_from_this(), std::placeholders::_1, std::placeholders::_2)));
+
974 }
+
975}
-
977
-
978//------------------------------------------------------------------------------
-
979//
-
980// ProtocolHandler
-
981//
-
982//------------------------------------------------------------------------------
-
983
-
984void
-
- -
986{
-
987 // TODO
-
988}
+
976
+
977//------------------------------------------------------------------------------
+
978//
+
979// ProtocolHandler
+
980//
+
981//------------------------------------------------------------------------------
+
982
+
983void
+
+ +
985{
+
986 // TODO
+
987}
-
989
-
990void
-
- -
992 std::uint16_t type,
- -
994 std::size_t size,
-
995 std::size_t uncompressed_size,
-
996 bool isCompressed)
-
997{
-
998 auto const name = protocolMessageName(type);
- - -
1001
-
1002 auto const category = TrafficCount::categorize(*m, static_cast<protocol::MessageType>(type), true);
-
1003
-
1004 // report total incoming traffic
- -
1006
-
1007 // increase the traffic received for a specific category
-
1008 overlay_.reportInboundTraffic(category, static_cast<int>(size));
-
1009
-
1010 using namespace protocol;
-
1011 if ((type == MessageType::mtTRANSACTION || type == MessageType::mtHAVE_TRANSACTIONS ||
-
1012 type == MessageType::mtTRANSACTIONS ||
-
1013 // GET_OBJECTS
- -
1015 // GET_LEDGER
- -
1017 // LEDGER_DATA
- - -
1020 {
-
1021 overlay_.addTxMetrics(static_cast<MessageType>(type), static_cast<std::uint64_t>(size));
-
1022 }
-
1023 JLOG(journal_.trace()) << "onMessageBegin: " << type << " " << size << " " << uncompressed_size << " "
-
1024 << isCompressed;
-
1025}
+
988
+
989void
+
+ +
991 std::uint16_t type,
+ +
993 std::size_t size,
+
994 std::size_t uncompressed_size,
+
995 bool isCompressed)
+
996{
+
997 auto const name = protocolMessageName(type);
+ + +
1000
+
1001 auto const category = TrafficCount::categorize(*m, static_cast<protocol::MessageType>(type), true);
+
1002
+
1003 // report total incoming traffic
+ +
1005
+
1006 // increase the traffic received for a specific category
+
1007 overlay_.reportInboundTraffic(category, static_cast<int>(size));
+
1008
+
1009 using namespace protocol;
+
1010 if ((type == MessageType::mtTRANSACTION || type == MessageType::mtHAVE_TRANSACTIONS ||
+
1011 type == MessageType::mtTRANSACTIONS ||
+
1012 // GET_OBJECTS
+ +
1014 // GET_LEDGER
+ +
1016 // LEDGER_DATA
+ + +
1019 {
+
1020 overlay_.addTxMetrics(static_cast<MessageType>(type), static_cast<std::uint64_t>(size));
+
1021 }
+
1022 JLOG(journal_.trace()) << "onMessageBegin: " << type << " " << size << " " << uncompressed_size << " "
+
1023 << isCompressed;
+
1024}
-
1026
-
1027void
-
- -
1029{
-
1030 load_event_.reset();
- -
1032}
+
1025
+
1026void
+ -
1033
-
1034void
-
- -
1036{
-
1037 auto const s = m->list_size();
-
1038
-
1039 if (s == 0)
-
1040 {
- -
1042 return;
-
1043 }
-
1044
-
1045 if (s > 100)
- -
1047
- -
1049 jtMANIFEST, "RcvManifests", [this, that = shared_from_this(), m]() { overlay_.onManifests(m, that); });
-
1050}
+
1032
+
1033void
+
+ +
1035{
+
1036 auto const s = m->list_size();
+
1037
+
1038 if (s == 0)
+
1039 {
+ +
1041 return;
+
1042 }
+
1043
+
1044 if (s > 100)
+ +
1046
+ +
1048 jtMANIFEST, "RcvManifests", [this, that = shared_from_this(), m]() { overlay_.onManifests(m, that); });
+
1049}
-
1051
-
1052void
-
- -
1054{
-
1055 if (m->type() == protocol::TMPing::ptPING)
-
1056 {
-
1057 // We have received a ping request, reply with a pong
- -
1059 m->set_type(protocol::TMPing::ptPONG);
-
1060 send(std::make_shared<Message>(*m, protocol::mtPING));
-
1061 return;
-
1062 }
-
1063
-
1064 if (m->type() == protocol::TMPing::ptPONG && m->has_seq())
-
1065 {
-
1066 // Only reset the ping sequence if we actually received a
-
1067 // PONG with the correct cookie. That way, any peers which
-
1068 // respond with incorrect cookies will eventually time out.
-
1069 if (m->seq() == lastPingSeq_)
-
1070 {
- -
1072
-
1073 // Update latency estimate
-
1074 auto const rtt = std::chrono::round<std::chrono::milliseconds>(clock_type::now() - lastPingTime_);
-
1075
- -
1077
-
1078 if (latency_)
-
1079 latency_ = (*latency_ * 7 + rtt) / 8;
-
1080 else
-
1081 latency_ = rtt;
-
1082 }
-
1083
-
1084 return;
-
1085 }
-
1086}
+
1050
+
1051void
+
+ +
1053{
+
1054 if (m->type() == protocol::TMPing::ptPING)
+
1055 {
+
1056 // We have received a ping request, reply with a pong
+ +
1058 m->set_type(protocol::TMPing::ptPONG);
+
1059 send(std::make_shared<Message>(*m, protocol::mtPING));
+
1060 return;
+
1061 }
+
1062
+
1063 if (m->type() == protocol::TMPing::ptPONG && m->has_seq())
+
1064 {
+
1065 // Only reset the ping sequence if we actually received a
+
1066 // PONG with the correct cookie. That way, any peers which
+
1067 // respond with incorrect cookies will eventually time out.
+
1068 if (m->seq() == lastPingSeq_)
+
1069 {
+ +
1071
+
1072 // Update latency estimate
+
1073 auto const rtt = std::chrono::round<std::chrono::milliseconds>(clock_type::now() - lastPingTime_);
+
1074
+ +
1076
+
1077 if (latency_)
+
1078 latency_ = (*latency_ * 7 + rtt) / 8;
+
1079 else
+
1080 latency_ = rtt;
+
1081 }
+
1082
+
1083 return;
+
1084 }
+
1085}
-
1087
-
1088void
-
- -
1090{
-
1091 // VFALCO NOTE I think we should drop the peer immediately
-
1092 if (!cluster())
-
1093 {
-
1094 fee_.update(Resource::feeUselessData, "unknown cluster");
-
1095 return;
-
1096 }
-
1097
-
1098 for (int i = 0; i < m->clusternodes().size(); ++i)
-
1099 {
-
1100 protocol::TMClusterNode const& node = m->clusternodes(i);
-
1101
- -
1103 if (node.has_nodename())
-
1104 name = node.nodename();
-
1105
-
1106 auto const publicKey = parseBase58<PublicKey>(TokenType::NodePublic, node.publickey());
-
1107
-
1108 // NIKB NOTE We should drop the peer immediately if
-
1109 // they send us a public key we can't parse
-
1110 if (publicKey)
-
1111 {
-
1112 auto const reportTime = NetClock::time_point{NetClock::duration{node.reporttime()}};
-
1113
-
1114 app_.cluster().update(*publicKey, name, node.nodeload(), reportTime);
-
1115 }
-
1116 }
-
1117
-
1118 int loadSources = m->loadsources().size();
-
1119 if (loadSources != 0)
-
1120 {
-
1121 Resource::Gossip gossip;
-
1122 gossip.items.reserve(loadSources);
-
1123 for (int i = 0; i < m->loadsources().size(); ++i)
-
1124 {
-
1125 protocol::TMLoadSource const& node = m->loadsources(i);
- -
1127 item.address = beast::IP::Endpoint::from_string(node.name());
-
1128 item.balance = node.cost();
-
1129 if (item.address != beast::IP::Endpoint())
-
1130 gossip.items.push_back(item);
-
1131 }
- -
1133 }
-
1134
-
1135 // Calculate the cluster fee:
-
1136 auto const thresh = app_.timeKeeper().now() - 90s;
-
1137 std::uint32_t clusterFee = 0;
-
1138
- -
1140 fees.reserve(app_.cluster().size());
-
1141
-
1142 app_.cluster().for_each([&fees, thresh](ClusterNode const& status) {
-
1143 if (status.getReportTime() >= thresh)
-
1144 fees.push_back(status.getLoadFee());
-
1145 });
-
1146
-
1147 if (!fees.empty())
-
1148 {
-
1149 auto const index = fees.size() / 2;
-
1150 std::nth_element(fees.begin(), fees.begin() + index, fees.end());
-
1151 clusterFee = fees[index];
-
1152 }
-
1153
-
1154 app_.getFeeTrack().setClusterFee(clusterFee);
-
1155}
+
1086
+
1087void
+
+ +
1089{
+
1090 // VFALCO NOTE I think we should drop the peer immediately
+
1091 if (!cluster())
+
1092 {
+
1093 fee_.update(Resource::feeUselessData, "unknown cluster");
+
1094 return;
+
1095 }
+
1096
+
1097 for (int i = 0; i < m->clusternodes().size(); ++i)
+
1098 {
+
1099 protocol::TMClusterNode const& node = m->clusternodes(i);
+
1100
+ +
1102 if (node.has_nodename())
+
1103 name = node.nodename();
+
1104
+
1105 auto const publicKey = parseBase58<PublicKey>(TokenType::NodePublic, node.publickey());
+
1106
+
1107 // NIKB NOTE We should drop the peer immediately if
+
1108 // they send us a public key we can't parse
+
1109 if (publicKey)
+
1110 {
+
1111 auto const reportTime = NetClock::time_point{NetClock::duration{node.reporttime()}};
+
1112
+
1113 app_.cluster().update(*publicKey, name, node.nodeload(), reportTime);
+
1114 }
+
1115 }
+
1116
+
1117 int loadSources = m->loadsources().size();
+
1118 if (loadSources != 0)
+
1119 {
+
1120 Resource::Gossip gossip;
+
1121 gossip.items.reserve(loadSources);
+
1122 for (int i = 0; i < m->loadsources().size(); ++i)
+
1123 {
+
1124 protocol::TMLoadSource const& node = m->loadsources(i);
+ +
1126 item.address = beast::IP::Endpoint::from_string(node.name());
+
1127 item.balance = node.cost();
+
1128 if (item.address != beast::IP::Endpoint())
+
1129 gossip.items.push_back(item);
+
1130 }
+ +
1132 }
+
1133
+
1134 // Calculate the cluster fee:
+
1135 auto const thresh = app_.timeKeeper().now() - 90s;
+
1136 std::uint32_t clusterFee = 0;
+
1137
+ +
1139 fees.reserve(app_.cluster().size());
+
1140
+
1141 app_.cluster().for_each([&fees, thresh](ClusterNode const& status) {
+
1142 if (status.getReportTime() >= thresh)
+
1143 fees.push_back(status.getLoadFee());
+
1144 });
+
1145
+
1146 if (!fees.empty())
+
1147 {
+
1148 auto const index = fees.size() / 2;
+
1149 std::nth_element(fees.begin(), fees.begin() + index, fees.end());
+
1150 clusterFee = fees[index];
+
1151 }
+
1152
+
1153 app_.getFeeTrack().setClusterFee(clusterFee);
+
1154}
-
1156
-
1157void
-
- -
1159{
-
1160 // Don't allow endpoints from peers that are not known tracking or are
-
1161 // not using a version of the message that we support:
-
1162 if (tracking_.load() != Tracking::converged || m->version() != 2)
-
1163 return;
-
1164
-
1165 // The number is arbitrary and doesn't have any real significance or
-
1166 // implication for the protocol.
-
1167 if (m->endpoints_v2().size() >= 1024)
-
1168 {
-
1169 fee_.update(Resource::feeUselessData, "endpoints too large");
-
1170 return;
-
1171 }
-
1172
- -
1174 endpoints.reserve(m->endpoints_v2().size());
-
1175
-
1176 auto malformed = 0;
-
1177 for (auto const& tm : m->endpoints_v2())
-
1178 {
-
1179 auto result = beast::IP::Endpoint::from_string_checked(tm.endpoint());
-
1180
-
1181 if (!result)
-
1182 {
-
1183 JLOG(p_journal_.error()) << "failed to parse incoming endpoint: {" << tm.endpoint() << "}";
-
1184 malformed++;
-
1185 continue;
-
1186 }
-
1187
-
1188 // If hops == 0, this Endpoint describes the peer we are connected
-
1189 // to -- in that case, we take the remote address seen on the
-
1190 // socket and store that in the IP::Endpoint. If this is the first
-
1191 // time, then we'll verify that their listener can receive incoming
-
1192 // by performing a connectivity test. if hops > 0, then we just
-
1193 // take the address/port we were given
-
1194 if (tm.hops() == 0)
-
1195 result = remote_address_.at_port(result->port());
-
1196
-
1197 endpoints.emplace_back(*result, tm.hops());
-
1198 }
-
1199
-
1200 // Charge the peer for each malformed endpoint. As there still may be
-
1201 // multiple valid endpoints we don't return early.
-
1202 if (malformed > 0)
-
1203 {
-
1204 fee_.update(Resource::feeInvalidData * malformed, std::to_string(malformed) + " malformed endpoints");
-
1205 }
-
1206
-
1207 if (!endpoints.empty())
-
1208 overlay_.peerFinder().on_endpoints(slot_, endpoints);
-
1209}
+
1155
+
1156void
+
+ +
1158{
+
1159 // Don't allow endpoints from peers that are not known tracking or are
+
1160 // not using a version of the message that we support:
+
1161 if (tracking_.load() != Tracking::converged || m->version() != 2)
+
1162 return;
+
1163
+
1164 // The number is arbitrary and doesn't have any real significance or
+
1165 // implication for the protocol.
+
1166 if (m->endpoints_v2().size() >= 1024)
+
1167 {
+
1168 fee_.update(Resource::feeUselessData, "endpoints too large");
+
1169 return;
+
1170 }
+
1171
+ +
1173 endpoints.reserve(m->endpoints_v2().size());
+
1174
+
1175 auto malformed = 0;
+
1176 for (auto const& tm : m->endpoints_v2())
+
1177 {
+
1178 auto result = beast::IP::Endpoint::from_string_checked(tm.endpoint());
+
1179
+
1180 if (!result)
+
1181 {
+
1182 JLOG(p_journal_.error()) << "failed to parse incoming endpoint: {" << tm.endpoint() << "}";
+
1183 malformed++;
+
1184 continue;
+
1185 }
+
1186
+
1187 // If hops == 0, this Endpoint describes the peer we are connected
+
1188 // to -- in that case, we take the remote address seen on the
+
1189 // socket and store that in the IP::Endpoint. If this is the first
+
1190 // time, then we'll verify that their listener can receive incoming
+
1191 // by performing a connectivity test. if hops > 0, then we just
+
1192 // take the address/port we were given
+
1193 if (tm.hops() == 0)
+
1194 result = remote_address_.at_port(result->port());
+
1195
+
1196 endpoints.emplace_back(*result, tm.hops());
+
1197 }
+
1198
+
1199 // Charge the peer for each malformed endpoint. As there still may be
+
1200 // multiple valid endpoints we don't return early.
+
1201 if (malformed > 0)
+
1202 {
+
1203 fee_.update(Resource::feeInvalidData * malformed, std::to_string(malformed) + " malformed endpoints");
+
1204 }
+
1205
+
1206 if (!endpoints.empty())
+
1207 overlay_.peerFinder().on_endpoints(slot_, endpoints);
+
1208}
-
1210
-
1211void
-
- -
1213{
-
1214 handleTransaction(m, true, false);
-
1215}
+
1209
+
1210void
+
+ +
1212{
+
1213 handleTransaction(m, true, false);
+
1214}
-
1216
-
1217void
-
- -
1219{
-
1220 XRPL_ASSERT(eraseTxQueue != batch, ("xrpl::PeerImp::handleTransaction : valid inputs"));
- -
1222 return;
-
1223
- -
1225 {
-
1226 // If we've never been in synch, there's nothing we can do
-
1227 // with a transaction
-
1228 JLOG(p_journal_.debug()) << "Ignoring incoming transaction: Need network ledger";
-
1229 return;
-
1230 }
-
1231
-
1232 SerialIter sit(makeSlice(m->rawtransaction()));
-
1233
-
1234 try
-
1235 {
-
1236 auto stx = std::make_shared<STTx const>(sit);
-
1237 uint256 txID = stx->getTransactionID();
-
1238
-
1239 // Charge strongly for attempting to relay a txn with tfInnerBatchTxn
-
1240 // LCOV_EXCL_START
-
1241 /*
-
1242 There is no need to check whether the featureBatch amendment is
-
1243 enabled.
-
1244
-
1245 * If the `tfInnerBatchTxn` flag is set, and the amendment is
-
1246 enabled, then it's an invalid transaction because inner batch
-
1247 transactions should not be relayed.
-
1248 * If the `tfInnerBatchTxn` flag is set, and the amendment is *not*
-
1249 enabled, then the transaction is malformed because it's using an
-
1250 "unknown" flag. There's no need to waste the resources to send it
-
1251 to the transaction engine.
-
1252
-
1253 We don't normally check transaction validity at this level, but
-
1254 since we _need_ to check it when the amendment is enabled, we may as
-
1255 well drop it if the flag is set regardless.
-
1256 */
-
1257 if (stx->isFlag(tfInnerBatchTxn))
-
1258 {
-
1259 JLOG(p_journal_.warn()) << "Ignoring Network relayed Tx containing "
-
1260 "tfInnerBatchTxn (handleTransaction).";
-
1261 fee_.update(Resource::feeModerateBurdenPeer, "inner batch txn");
-
1262 return;
-
1263 }
-
1264 // LCOV_EXCL_STOP
-
1265
-
1266 HashRouterFlags flags;
-
1267 constexpr std::chrono::seconds tx_interval = 10s;
-
1268
-
1269 if (!app_.getHashRouter().shouldProcess(txID, id_, flags, tx_interval))
-
1270 {
-
1271 // we have seen this transaction recently
-
1272 if (any(flags & HashRouterFlags::BAD))
-
1273 {
-
1274 fee_.update(Resource::feeUselessData, "known bad");
-
1275 JLOG(p_journal_.debug()) << "Ignoring known bad tx " << txID;
-
1276 }
-
1277
-
1278 // Erase only if the server has seen this tx. If the server has not
-
1279 // seen this tx then the tx could not has been queued for this peer.
-
1280 else if (eraseTxQueue && txReduceRelayEnabled())
-
1281 removeTxQueue(txID);
-
1282
- -
1284
-
1285 return;
-
1286 }
-
1287
-
1288 JLOG(p_journal_.debug()) << "Got tx " << txID;
-
1289
-
1290 bool checkSignature = true;
-
1291 if (cluster())
-
1292 {
-
1293 if (!m->has_deferred() || !m->deferred())
-
1294 {
-
1295 // Skip local checks if a server we trust
-
1296 // put the transaction in its open ledger
-
1297 flags |= HashRouterFlags::TRUSTED;
-
1298 }
-
1299
-
1300 // for non-validator nodes only -- localPublicKey is set for
-
1301 // validators only
- -
1303 {
-
1304 // For now, be paranoid and have each validator
-
1305 // check each transaction, regardless of source
-
1306 checkSignature = false;
-
1307 }
-
1308 }
-
1309
- -
1311 {
-
1312 JLOG(p_journal_.trace()) << "No new transactions until synchronized";
-
1313 }
- -
1315 {
- -
1317 JLOG(p_journal_.info()) << "Transaction queue is full";
-
1318 }
-
1319 else
-
1320 {
- - -
1323 "RcvCheckTx",
-
1324 [weak = std::weak_ptr<PeerImp>(shared_from_this()), flags, checkSignature, batch, stx]() {
-
1325 if (auto peer = weak.lock())
-
1326 peer->checkTransaction(flags, checkSignature, stx, batch);
-
1327 });
-
1328 }
-
1329 }
-
1330 catch (std::exception const& ex)
-
1331 {
-
1332 JLOG(p_journal_.warn()) << "Transaction invalid: " << strHex(m->rawtransaction())
-
1333 << ". Exception: " << ex.what();
-
1334 }
-
1335}
+
1215
+
1216void
+
+ +
1218{
+
1219 XRPL_ASSERT(eraseTxQueue != batch, ("xrpl::PeerImp::handleTransaction : valid inputs"));
+ +
1221 return;
+
1222
+ +
1224 {
+
1225 // If we've never been in synch, there's nothing we can do
+
1226 // with a transaction
+
1227 JLOG(p_journal_.debug()) << "Ignoring incoming transaction: Need network ledger";
+
1228 return;
+
1229 }
+
1230
+
1231 SerialIter sit(makeSlice(m->rawtransaction()));
+
1232
+
1233 try
+
1234 {
+
1235 auto stx = std::make_shared<STTx const>(sit);
+
1236 uint256 txID = stx->getTransactionID();
+
1237
+
1238 // Charge strongly for attempting to relay a txn with tfInnerBatchTxn
+
1239 // LCOV_EXCL_START
+
1240 /*
+
1241 There is no need to check whether the featureBatch amendment is
+
1242 enabled.
+
1243
+
1244 * If the `tfInnerBatchTxn` flag is set, and the amendment is
+
1245 enabled, then it's an invalid transaction because inner batch
+
1246 transactions should not be relayed.
+
1247 * If the `tfInnerBatchTxn` flag is set, and the amendment is *not*
+
1248 enabled, then the transaction is malformed because it's using an
+
1249 "unknown" flag. There's no need to waste the resources to send it
+
1250 to the transaction engine.
+
1251
+
1252 We don't normally check transaction validity at this level, but
+
1253 since we _need_ to check it when the amendment is enabled, we may as
+
1254 well drop it if the flag is set regardless.
+
1255 */
+
1256 if (stx->isFlag(tfInnerBatchTxn))
+
1257 {
+
1258 JLOG(p_journal_.warn()) << "Ignoring Network relayed Tx containing "
+
1259 "tfInnerBatchTxn (handleTransaction).";
+
1260 fee_.update(Resource::feeModerateBurdenPeer, "inner batch txn");
+
1261 return;
+
1262 }
+
1263 // LCOV_EXCL_STOP
+
1264
+
1265 HashRouterFlags flags;
+
1266 constexpr std::chrono::seconds tx_interval = 10s;
+
1267
+
1268 if (!app_.getHashRouter().shouldProcess(txID, id_, flags, tx_interval))
+
1269 {
+
1270 // we have seen this transaction recently
+
1271 if (any(flags & HashRouterFlags::BAD))
+
1272 {
+
1273 fee_.update(Resource::feeUselessData, "known bad");
+
1274 JLOG(p_journal_.debug()) << "Ignoring known bad tx " << txID;
+
1275 }
+
1276
+
1277 // Erase only if the server has seen this tx. If the server has not
+
1278 // seen this tx then the tx could not has been queued for this peer.
+
1279 else if (eraseTxQueue && txReduceRelayEnabled())
+
1280 removeTxQueue(txID);
+
1281
+ +
1283
+
1284 return;
+
1285 }
+
1286
+
1287 JLOG(p_journal_.debug()) << "Got tx " << txID;
+
1288
+
1289 bool checkSignature = true;
+
1290 if (cluster())
+
1291 {
+
1292 if (!m->has_deferred() || !m->deferred())
+
1293 {
+
1294 // Skip local checks if a server we trust
+
1295 // put the transaction in its open ledger
+
1296 flags |= HashRouterFlags::TRUSTED;
+
1297 }
+
1298
+
1299 // for non-validator nodes only -- localPublicKey is set for
+
1300 // validators only
+ +
1302 {
+
1303 // For now, be paranoid and have each validator
+
1304 // check each transaction, regardless of source
+
1305 checkSignature = false;
+
1306 }
+
1307 }
+
1308
+ +
1310 {
+
1311 JLOG(p_journal_.trace()) << "No new transactions until synchronized";
+
1312 }
+ +
1314 {
+ +
1316 JLOG(p_journal_.info()) << "Transaction queue is full";
+
1317 }
+
1318 else
+
1319 {
+ + +
1322 "RcvCheckTx",
+
1323 [weak = std::weak_ptr<PeerImp>(shared_from_this()), flags, checkSignature, batch, stx]() {
+
1324 if (auto peer = weak.lock())
+
1325 peer->checkTransaction(flags, checkSignature, stx, batch);
+
1326 });
+
1327 }
+
1328 }
+
1329 catch (std::exception const& ex)
+
1330 {
+
1331 JLOG(p_journal_.warn()) << "Transaction invalid: " << strHex(m->rawtransaction())
+
1332 << ". Exception: " << ex.what();
+
1333 }
+
1334}
-
1336
-
1337void
-
- -
1339{
-
1340 auto badData = [&](std::string const& msg) {
-
1341 fee_.update(Resource::feeInvalidData, "get_ledger " + msg);
-
1342 JLOG(p_journal_.warn()) << "TMGetLedger: " << msg;
-
1343 };
-
1344 auto const itype{m->itype()};
-
1345
-
1346 // Verify ledger info type
-
1347 if (itype < protocol::liBASE || itype > protocol::liTS_CANDIDATE)
-
1348 return badData("Invalid ledger info type");
-
1349
-
1350 auto const ltype = [&m]() -> std::optional<::protocol::TMLedgerType> {
-
1351 if (m->has_ltype())
-
1352 return m->ltype();
-
1353 return std::nullopt;
-
1354 }();
-
1355
-
1356 if (itype == protocol::liTS_CANDIDATE)
-
1357 {
-
1358 if (!m->has_ledgerhash())
-
1359 return badData("Invalid TX candidate set, missing TX set hash");
-
1360 }
-
1361 else if (!m->has_ledgerhash() && !m->has_ledgerseq() && !(ltype && *ltype == protocol::ltCLOSED))
-
1362 {
-
1363 return badData("Invalid request");
-
1364 }
-
1365
-
1366 // Verify ledger type
-
1367 if (ltype && (*ltype < protocol::ltACCEPTED || *ltype > protocol::ltCLOSED))
-
1368 return badData("Invalid ledger type");
-
1369
-
1370 // Verify ledger hash
-
1371 if (m->has_ledgerhash() && !stringIsUint256Sized(m->ledgerhash()))
-
1372 return badData("Invalid ledger hash");
-
1373
-
1374 // Verify ledger sequence
-
1375 if (m->has_ledgerseq())
-
1376 {
-
1377 auto const ledgerSeq{m->ledgerseq()};
-
1378
-
1379 // Check if within a reasonable range
-
1380 using namespace std::chrono_literals;
- -
1382 ledgerSeq > app_.getLedgerMaster().getValidLedgerIndex() + 10)
-
1383 {
-
1384 return badData("Invalid ledger sequence " + std::to_string(ledgerSeq));
-
1385 }
-
1386 }
-
1387
-
1388 // Verify ledger node IDs
-
1389 if (itype != protocol::liBASE)
-
1390 {
-
1391 if (m->nodeids_size() <= 0)
-
1392 return badData("Invalid ledger node IDs");
-
1393
-
1394 for (auto const& nodeId : m->nodeids())
-
1395 {
- -
1397 return badData("Invalid SHAMap node ID");
-
1398 }
-
1399 }
-
1400
-
1401 // Verify query type
-
1402 if (m->has_querytype() && m->querytype() != protocol::qtINDIRECT)
-
1403 return badData("Invalid query type");
-
1404
-
1405 // Verify query depth
-
1406 if (m->has_querydepth())
-
1407 {
-
1408 if (m->querydepth() > Tuning::maxQueryDepth || itype == protocol::liBASE)
-
1409 {
-
1410 return badData("Invalid query depth");
-
1411 }
-
1412 }
-
1413
-
1414 // Queue a job to process the request
- -
1416 app_.getJobQueue().addJob(jtLEDGER_REQ, "RcvGetLedger", [weak, m]() {
-
1417 if (auto peer = weak.lock())
-
1418 peer->processLedgerRequest(m);
-
1419 });
-
1420}
+
1335
+
1336void
+
+ +
1338{
+
1339 auto badData = [&](std::string const& msg) {
+
1340 fee_.update(Resource::feeInvalidData, "get_ledger " + msg);
+
1341 JLOG(p_journal_.warn()) << "TMGetLedger: " << msg;
+
1342 };
+
1343 auto const itype{m->itype()};
+
1344
+
1345 // Verify ledger info type
+
1346 if (itype < protocol::liBASE || itype > protocol::liTS_CANDIDATE)
+
1347 return badData("Invalid ledger info type");
+
1348
+
1349 auto const ltype = [&m]() -> std::optional<::protocol::TMLedgerType> {
+
1350 if (m->has_ltype())
+
1351 return m->ltype();
+
1352 return std::nullopt;
+
1353 }();
+
1354
+
1355 if (itype == protocol::liTS_CANDIDATE)
+
1356 {
+
1357 if (!m->has_ledgerhash())
+
1358 return badData("Invalid TX candidate set, missing TX set hash");
+
1359 }
+
1360 else if (!m->has_ledgerhash() && !m->has_ledgerseq() && !(ltype && *ltype == protocol::ltCLOSED))
+
1361 {
+
1362 return badData("Invalid request");
+
1363 }
+
1364
+
1365 // Verify ledger type
+
1366 if (ltype && (*ltype < protocol::ltACCEPTED || *ltype > protocol::ltCLOSED))
+
1367 return badData("Invalid ledger type");
+
1368
+
1369 // Verify ledger hash
+
1370 if (m->has_ledgerhash() && !stringIsUint256Sized(m->ledgerhash()))
+
1371 return badData("Invalid ledger hash");
+
1372
+
1373 // Verify ledger sequence
+
1374 if (m->has_ledgerseq())
+
1375 {
+
1376 auto const ledgerSeq{m->ledgerseq()};
+
1377
+
1378 // Check if within a reasonable range
+
1379 using namespace std::chrono_literals;
+ +
1381 ledgerSeq > app_.getLedgerMaster().getValidLedgerIndex() + 10)
+
1382 {
+
1383 return badData("Invalid ledger sequence " + std::to_string(ledgerSeq));
+
1384 }
+
1385 }
+
1386
+
1387 // Verify ledger node IDs
+
1388 if (itype != protocol::liBASE)
+
1389 {
+
1390 if (m->nodeids_size() <= 0)
+
1391 return badData("Invalid ledger node IDs");
+
1392
+
1393 for (auto const& nodeId : m->nodeids())
+
1394 {
+ +
1396 return badData("Invalid SHAMap node ID");
+
1397 }
+
1398 }
+
1399
+
1400 // Verify query type
+
1401 if (m->has_querytype() && m->querytype() != protocol::qtINDIRECT)
+
1402 return badData("Invalid query type");
+
1403
+
1404 // Verify query depth
+
1405 if (m->has_querydepth())
+
1406 {
+
1407 if (m->querydepth() > Tuning::maxQueryDepth || itype == protocol::liBASE)
+
1408 {
+
1409 return badData("Invalid query depth");
+
1410 }
+
1411 }
+
1412
+
1413 // Queue a job to process the request
+ +
1415 app_.getJobQueue().addJob(jtLEDGER_REQ, "RcvGetLedger", [weak, m]() {
+
1416 if (auto peer = weak.lock())
+
1417 peer->processLedgerRequest(m);
+
1418 });
+
1419}
-
1421
-
1422void
-
- -
1424{
-
1425 JLOG(p_journal_.trace()) << "onMessage, TMProofPathRequest";
- -
1427 {
-
1428 fee_.update(Resource::feeMalformedRequest, "proof_path_request disabled");
-
1429 return;
-
1430 }
-
1431
-
1432 fee_.update(Resource::feeModerateBurdenPeer, "received a proof path request");
- -
1434 app_.getJobQueue().addJob(jtREPLAY_REQ, "RcvProofPReq", [weak, m]() {
-
1435 if (auto peer = weak.lock())
-
1436 {
-
1437 auto reply = peer->ledgerReplayMsgHandler_.processProofPathRequest(m);
-
1438 if (reply.has_error())
-
1439 {
-
1440 if (reply.error() == protocol::TMReplyError::reBAD_REQUEST)
-
1441 peer->charge(Resource::feeMalformedRequest, "proof_path_request");
-
1442 else
-
1443 peer->charge(Resource::feeRequestNoReply, "proof_path_request");
-
1444 }
-
1445 else
-
1446 {
-
1447 peer->send(std::make_shared<Message>(reply, protocol::mtPROOF_PATH_RESPONSE));
-
1448 }
-
1449 }
-
1450 });
-
1451}
+
1420
+
1421void
+
+ +
1423{
+
1424 JLOG(p_journal_.trace()) << "onMessage, TMProofPathRequest";
+ +
1426 {
+
1427 fee_.update(Resource::feeMalformedRequest, "proof_path_request disabled");
+
1428 return;
+
1429 }
+
1430
+
1431 fee_.update(Resource::feeModerateBurdenPeer, "received a proof path request");
+ +
1433 app_.getJobQueue().addJob(jtREPLAY_REQ, "RcvProofPReq", [weak, m]() {
+
1434 if (auto peer = weak.lock())
+
1435 {
+
1436 auto reply = peer->ledgerReplayMsgHandler_.processProofPathRequest(m);
+
1437 if (reply.has_error())
+
1438 {
+
1439 if (reply.error() == protocol::TMReplyError::reBAD_REQUEST)
+
1440 peer->charge(Resource::feeMalformedRequest, "proof_path_request");
+
1441 else
+
1442 peer->charge(Resource::feeRequestNoReply, "proof_path_request");
+
1443 }
+
1444 else
+
1445 {
+
1446 peer->send(std::make_shared<Message>(reply, protocol::mtPROOF_PATH_RESPONSE));
+
1447 }
+
1448 }
+
1449 });
+
1450}
-
1452
-
1453void
-
- -
1455{
-
1456 if (!ledgerReplayEnabled_)
-
1457 {
-
1458 fee_.update(Resource::feeMalformedRequest, "proof_path_response disabled");
-
1459 return;
-
1460 }
-
1461
-
1462 if (!ledgerReplayMsgHandler_.processProofPathResponse(m))
-
1463 {
-
1464 fee_.update(Resource::feeInvalidData, "proof_path_response");
-
1465 }
-
1466}
+
1451
+
1452void
+
+ +
1454{
+
1455 if (!ledgerReplayEnabled_)
+
1456 {
+
1457 fee_.update(Resource::feeMalformedRequest, "proof_path_response disabled");
+
1458 return;
+
1459 }
+
1460
+
1461 if (!ledgerReplayMsgHandler_.processProofPathResponse(m))
+
1462 {
+
1463 fee_.update(Resource::feeInvalidData, "proof_path_response");
+
1464 }
+
1465}
-
1467
-
1468void
-
- -
1470{
-
1471 JLOG(p_journal_.trace()) << "onMessage, TMReplayDeltaRequest";
-
1472 if (!ledgerReplayEnabled_)
-
1473 {
-
1474 fee_.update(Resource::feeMalformedRequest, "replay_delta_request disabled");
-
1475 return;
-
1476 }
-
1477
-
1478 fee_.fee = Resource::feeModerateBurdenPeer;
-
1479 std::weak_ptr<PeerImp> weak = shared_from_this();
-
1480 app_.getJobQueue().addJob(jtREPLAY_REQ, "RcvReplDReq", [weak, m]() {
-
1481 if (auto peer = weak.lock())
-
1482 {
-
1483 auto reply = peer->ledgerReplayMsgHandler_.processReplayDeltaRequest(m);
-
1484 if (reply.has_error())
-
1485 {
-
1486 if (reply.error() == protocol::TMReplyError::reBAD_REQUEST)
-
1487 peer->charge(Resource::feeMalformedRequest, "replay_delta_request");
-
1488 else
-
1489 peer->charge(Resource::feeRequestNoReply, "replay_delta_request");
-
1490 }
-
1491 else
-
1492 {
-
1493 peer->send(std::make_shared<Message>(reply, protocol::mtREPLAY_DELTA_RESPONSE));
-
1494 }
-
1495 }
-
1496 });
-
1497}
+
1466
+
1467void
+
+ +
1469{
+
1470 JLOG(p_journal_.trace()) << "onMessage, TMReplayDeltaRequest";
+
1471 if (!ledgerReplayEnabled_)
+
1472 {
+
1473 fee_.update(Resource::feeMalformedRequest, "replay_delta_request disabled");
+
1474 return;
+
1475 }
+
1476
+
1477 fee_.fee = Resource::feeModerateBurdenPeer;
+
1478 std::weak_ptr<PeerImp> weak = shared_from_this();
+
1479 app_.getJobQueue().addJob(jtREPLAY_REQ, "RcvReplDReq", [weak, m]() {
+
1480 if (auto peer = weak.lock())
+
1481 {
+
1482 auto reply = peer->ledgerReplayMsgHandler_.processReplayDeltaRequest(m);
+
1483 if (reply.has_error())
+
1484 {
+
1485 if (reply.error() == protocol::TMReplyError::reBAD_REQUEST)
+
1486 peer->charge(Resource::feeMalformedRequest, "replay_delta_request");
+
1487 else
+
1488 peer->charge(Resource::feeRequestNoReply, "replay_delta_request");
+
1489 }
+
1490 else
+
1491 {
+
1492 peer->send(std::make_shared<Message>(reply, protocol::mtREPLAY_DELTA_RESPONSE));
+
1493 }
+
1494 }
+
1495 });
+
1496}
-
1498
-
1499void
-
- -
1501{
-
1502 if (!ledgerReplayEnabled_)
-
1503 {
-
1504 fee_.update(Resource::feeMalformedRequest, "replay_delta_response disabled");
-
1505 return;
-
1506 }
-
1507
-
1508 if (!ledgerReplayMsgHandler_.processReplayDeltaResponse(m))
-
1509 {
-
1510 fee_.update(Resource::feeInvalidData, "replay_delta_response");
-
1511 }
-
1512}
+
1497
+
1498void
+
+ +
1500{
+
1501 if (!ledgerReplayEnabled_)
+
1502 {
+
1503 fee_.update(Resource::feeMalformedRequest, "replay_delta_response disabled");
+
1504 return;
+
1505 }
+
1506
+
1507 if (!ledgerReplayMsgHandler_.processReplayDeltaResponse(m))
+
1508 {
+
1509 fee_.update(Resource::feeInvalidData, "replay_delta_response");
+
1510 }
+
1511}
-
1513
-
1514void
-
- -
1516{
-
1517 auto badData = [&](std::string const& msg) {
-
1518 fee_.update(Resource::feeInvalidData, msg);
-
1519 JLOG(p_journal_.warn()) << "TMLedgerData: " << msg;
-
1520 };
-
1521
-
1522 // Verify ledger hash
-
1523 if (!stringIsUint256Sized(m->ledgerhash()))
-
1524 return badData("Invalid ledger hash");
-
1525
-
1526 // Verify ledger sequence
-
1527 {
-
1528 auto const ledgerSeq{m->ledgerseq()};
-
1529 if (m->type() == protocol::liTS_CANDIDATE)
-
1530 {
-
1531 if (ledgerSeq != 0)
-
1532 {
-
1533 return badData("Invalid ledger sequence " + std::to_string(ledgerSeq));
-
1534 }
-
1535 }
-
1536 else
-
1537 {
-
1538 // Check if within a reasonable range
-
1539 using namespace std::chrono_literals;
-
1540 if (app_.getLedgerMaster().getValidatedLedgerAge() <= 10s &&
-
1541 ledgerSeq > app_.getLedgerMaster().getValidLedgerIndex() + 10)
-
1542 {
-
1543 return badData("Invalid ledger sequence " + std::to_string(ledgerSeq));
-
1544 }
-
1545 }
-
1546 }
-
1547
-
1548 // Verify ledger info type
-
1549 if (m->type() < protocol::liBASE || m->type() > protocol::liTS_CANDIDATE)
-
1550 return badData("Invalid ledger info type");
-
1551
-
1552 // Verify reply error
-
1553 if (m->has_error() && (m->error() < protocol::reNO_LEDGER || m->error() > protocol::reBAD_REQUEST))
-
1554 {
-
1555 return badData("Invalid reply error");
-
1556 }
-
1557
-
1558 // Verify ledger nodes.
-
1559 if (m->nodes_size() <= 0 || m->nodes_size() > Tuning::hardMaxReplyNodes)
-
1560 {
-
1561 return badData("Invalid Ledger/TXset nodes " + std::to_string(m->nodes_size()));
-
1562 }
-
1563
-
1564 // If there is a request cookie, attempt to relay the message
-
1565 if (m->has_requestcookie())
-
1566 {
-
1567 if (auto peer = overlay_.findPeerByShortID(m->requestcookie()))
-
1568 {
-
1569 m->clear_requestcookie();
-
1570 peer->send(std::make_shared<Message>(*m, protocol::mtLEDGER_DATA));
-
1571 }
-
1572 else
-
1573 {
-
1574 JLOG(p_journal_.info()) << "Unable to route TX/ledger data reply";
-
1575 }
-
1576 return;
-
1577 }
-
1578
-
1579 uint256 const ledgerHash{m->ledgerhash()};
-
1580
-
1581 // Otherwise check if received data for a candidate transaction set
-
1582 if (m->type() == protocol::liTS_CANDIDATE)
-
1583 {
-
1584 std::weak_ptr<PeerImp> weak{shared_from_this()};
-
1585 app_.getJobQueue().addJob(jtTXN_DATA, "RcvPeerData", [weak, ledgerHash, m]() {
-
1586 if (auto peer = weak.lock())
-
1587 {
-
1588 peer->app_.getInboundTransactions().gotData(ledgerHash, peer, m);
-
1589 }
-
1590 });
-
1591 return;
-
1592 }
-
1593
-
1594 // Consume the message
-
1595 app_.getInboundLedgers().gotLedgerData(ledgerHash, shared_from_this(), m);
-
1596}
+
1512
+
1513void
+
+ +
1515{
+
1516 auto badData = [&](std::string const& msg) {
+
1517 fee_.update(Resource::feeInvalidData, msg);
+
1518 JLOG(p_journal_.warn()) << "TMLedgerData: " << msg;
+
1519 };
+
1520
+
1521 // Verify ledger hash
+
1522 if (!stringIsUint256Sized(m->ledgerhash()))
+
1523 return badData("Invalid ledger hash");
+
1524
+
1525 // Verify ledger sequence
+
1526 {
+
1527 auto const ledgerSeq{m->ledgerseq()};
+
1528 if (m->type() == protocol::liTS_CANDIDATE)
+
1529 {
+
1530 if (ledgerSeq != 0)
+
1531 {
+
1532 return badData("Invalid ledger sequence " + std::to_string(ledgerSeq));
+
1533 }
+
1534 }
+
1535 else
+
1536 {
+
1537 // Check if within a reasonable range
+
1538 using namespace std::chrono_literals;
+
1539 if (app_.getLedgerMaster().getValidatedLedgerAge() <= 10s &&
+
1540 ledgerSeq > app_.getLedgerMaster().getValidLedgerIndex() + 10)
+
1541 {
+
1542 return badData("Invalid ledger sequence " + std::to_string(ledgerSeq));
+
1543 }
+
1544 }
+
1545 }
+
1546
+
1547 // Verify ledger info type
+
1548 if (m->type() < protocol::liBASE || m->type() > protocol::liTS_CANDIDATE)
+
1549 return badData("Invalid ledger info type");
+
1550
+
1551 // Verify reply error
+
1552 if (m->has_error() && (m->error() < protocol::reNO_LEDGER || m->error() > protocol::reBAD_REQUEST))
+
1553 {
+
1554 return badData("Invalid reply error");
+
1555 }
+
1556
+
1557 // Verify ledger nodes.
+
1558 if (m->nodes_size() <= 0 || m->nodes_size() > Tuning::hardMaxReplyNodes)
+
1559 {
+
1560 return badData("Invalid Ledger/TXset nodes " + std::to_string(m->nodes_size()));
+
1561 }
+
1562
+
1563 // If there is a request cookie, attempt to relay the message
+
1564 if (m->has_requestcookie())
+
1565 {
+
1566 if (auto peer = overlay_.findPeerByShortID(m->requestcookie()))
+
1567 {
+
1568 m->clear_requestcookie();
+
1569 peer->send(std::make_shared<Message>(*m, protocol::mtLEDGER_DATA));
+
1570 }
+
1571 else
+
1572 {
+
1573 JLOG(p_journal_.info()) << "Unable to route TX/ledger data reply";
+
1574 }
+
1575 return;
+
1576 }
+
1577
+
1578 uint256 const ledgerHash{m->ledgerhash()};
+
1579
+
1580 // Otherwise check if received data for a candidate transaction set
+
1581 if (m->type() == protocol::liTS_CANDIDATE)
+
1582 {
+
1583 std::weak_ptr<PeerImp> weak{shared_from_this()};
+
1584 app_.getJobQueue().addJob(jtTXN_DATA, "RcvPeerData", [weak, ledgerHash, m]() {
+
1585 if (auto peer = weak.lock())
+
1586 {
+
1587 peer->app_.getInboundTransactions().gotData(ledgerHash, peer, m);
+
1588 }
+
1589 });
+
1590 return;
+
1591 }
+
1592
+
1593 // Consume the message
+
1594 app_.getInboundLedgers().gotLedgerData(ledgerHash, shared_from_this(), m);
+
1595}
-
1597
-
1598void
-
- -
1600{
-
1601 protocol::TMProposeSet& set = *m;
-
1602
-
1603 auto const sig = makeSlice(set.signature());
-
1604
-
1605 // Preliminary check for the validity of the signature: A DER encoded
-
1606 // signature can't be longer than 72 bytes.
-
1607 if ((std::clamp<std::size_t>(sig.size(), 64, 72) != sig.size()) ||
-
1608 (publicKeyType(makeSlice(set.nodepubkey())) != KeyType::secp256k1))
-
1609 {
-
1610 JLOG(p_journal_.warn()) << "Proposal: malformed";
-
1611 fee_.update(Resource::feeInvalidSignature, " signature can't be longer than 72 bytes");
-
1612 return;
-
1613 }
-
1614
-
1615 if (!stringIsUint256Sized(set.currenttxhash()) || !stringIsUint256Sized(set.previousledger()))
-
1616 {
-
1617 JLOG(p_journal_.warn()) << "Proposal: malformed";
-
1618 fee_.update(Resource::feeMalformedRequest, "bad hashes");
-
1619 return;
-
1620 }
-
1621
-
1622 // RH TODO: when isTrusted = false we should probably also cache a key
-
1623 // suppression for 30 seconds to avoid doing a relatively expensive lookup
-
1624 // every time a spam packet is received
-
1625 PublicKey const publicKey{makeSlice(set.nodepubkey())};
-
1626 auto const isTrusted = app_.validators().trusted(publicKey);
-
1627
-
1628 // If the operator has specified that untrusted proposals be dropped then
-
1629 // this happens here I.e. before further wasting CPU verifying the signature
-
1630 // of an untrusted key
-
1631 if (!isTrusted)
-
1632 {
-
1633 // report untrusted proposal messages
-
1634 overlay_.reportInboundTraffic(TrafficCount::category::proposal_untrusted, Message::messageSize(*m));
-
1635
-
1636 if (app_.config().RELAY_UNTRUSTED_PROPOSALS == -1)
-
1637 return;
-
1638 }
-
1639
-
1640 uint256 const proposeHash{set.currenttxhash()};
-
1641 uint256 const prevLedger{set.previousledger()};
-
1642
-
1643 NetClock::time_point const closeTime{NetClock::duration{set.closetime()}};
-
1644
-
1645 uint256 const suppression =
-
1646 proposalUniqueId(proposeHash, prevLedger, set.proposeseq(), closeTime, publicKey.slice(), sig);
-
1647
-
1648 if (auto [added, relayed] = app_.getHashRouter().addSuppressionPeerWithStatus(suppression, id_); !added)
-
1649 {
-
1650 // Count unique messages (Slots has it's own 'HashRouter'), which a peer
-
1651 // receives within IDLED seconds since the message has been relayed.
-
1652 if (relayed && (stopwatch().now() - *relayed) < reduce_relay::IDLED)
-
1653 overlay_.updateSlotAndSquelch(suppression, publicKey, id_, protocol::mtPROPOSE_LEDGER);
-
1654
-
1655 // report duplicate proposal messages
-
1656 overlay_.reportInboundTraffic(TrafficCount::category::proposal_duplicate, Message::messageSize(*m));
-
1657
-
1658 JLOG(p_journal_.trace()) << "Proposal: duplicate";
-
1659
-
1660 return;
-
1661 }
-
1662
-
1663 if (!isTrusted)
-
1664 {
-
1665 if (tracking_.load() == Tracking::diverged)
-
1666 {
-
1667 JLOG(p_journal_.debug()) << "Proposal: Dropping untrusted (peer divergence)";
-
1668 return;
-
1669 }
-
1670
-
1671 if (!cluster() && app_.getFeeTrack().isLoadedLocal())
-
1672 {
-
1673 JLOG(p_journal_.debug()) << "Proposal: Dropping untrusted (load)";
-
1674 return;
-
1675 }
-
1676 }
-
1677
-
1678 JLOG(p_journal_.trace()) << "Proposal: " << (isTrusted ? "trusted" : "untrusted");
-
1679
-
1680 auto proposal = RCLCxPeerPos(
-
1681 publicKey,
-
1682 sig,
-
1683 suppression,
- -
1685 prevLedger,
-
1686 set.proposeseq(),
-
1687 proposeHash,
-
1688 closeTime,
-
1689 app_.timeKeeper().closeTime(),
-
1690 calcNodeID(app_.validatorManifests().getMasterKey(publicKey))});
-
1691
-
1692 std::weak_ptr<PeerImp> weak = shared_from_this();
-
1693 app_.getJobQueue().addJob(
-
1694 isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, "checkPropose", [weak, isTrusted, m, proposal]() {
-
1695 if (auto peer = weak.lock())
-
1696 peer->checkPropose(isTrusted, m, proposal);
-
1697 });
-
1698}
+
1596
+
1597void
+
+ +
1599{
+
1600 protocol::TMProposeSet& set = *m;
+
1601
+
1602 auto const sig = makeSlice(set.signature());
+
1603
+
1604 // Preliminary check for the validity of the signature: A DER encoded
+
1605 // signature can't be longer than 72 bytes.
+
1606 if ((std::clamp<std::size_t>(sig.size(), 64, 72) != sig.size()) ||
+
1607 (publicKeyType(makeSlice(set.nodepubkey())) != KeyType::secp256k1))
+
1608 {
+
1609 JLOG(p_journal_.warn()) << "Proposal: malformed";
+
1610 fee_.update(Resource::feeInvalidSignature, " signature can't be longer than 72 bytes");
+
1611 return;
+
1612 }
+
1613
+
1614 if (!stringIsUint256Sized(set.currenttxhash()) || !stringIsUint256Sized(set.previousledger()))
+
1615 {
+
1616 JLOG(p_journal_.warn()) << "Proposal: malformed";
+
1617 fee_.update(Resource::feeMalformedRequest, "bad hashes");
+
1618 return;
+
1619 }
+
1620
+
1621 // RH TODO: when isTrusted = false we should probably also cache a key
+
1622 // suppression for 30 seconds to avoid doing a relatively expensive lookup
+
1623 // every time a spam packet is received
+
1624 PublicKey const publicKey{makeSlice(set.nodepubkey())};
+
1625 auto const isTrusted = app_.validators().trusted(publicKey);
+
1626
+
1627 // If the operator has specified that untrusted proposals be dropped then
+
1628 // this happens here I.e. before further wasting CPU verifying the signature
+
1629 // of an untrusted key
+
1630 if (!isTrusted)
+
1631 {
+
1632 // report untrusted proposal messages
+
1633 overlay_.reportInboundTraffic(TrafficCount::category::proposal_untrusted, Message::messageSize(*m));
+
1634
+
1635 if (app_.config().RELAY_UNTRUSTED_PROPOSALS == -1)
+
1636 return;
+
1637 }
+
1638
+
1639 uint256 const proposeHash{set.currenttxhash()};
+
1640 uint256 const prevLedger{set.previousledger()};
+
1641
+
1642 NetClock::time_point const closeTime{NetClock::duration{set.closetime()}};
+
1643
+
1644 uint256 const suppression =
+
1645 proposalUniqueId(proposeHash, prevLedger, set.proposeseq(), closeTime, publicKey.slice(), sig);
+
1646
+
1647 if (auto [added, relayed] = app_.getHashRouter().addSuppressionPeerWithStatus(suppression, id_); !added)
+
1648 {
+
1649 // Count unique messages (Slots has it's own 'HashRouter'), which a peer
+
1650 // receives within IDLED seconds since the message has been relayed.
+
1651 if (relayed && (stopwatch().now() - *relayed) < reduce_relay::IDLED)
+
1652 overlay_.updateSlotAndSquelch(suppression, publicKey, id_, protocol::mtPROPOSE_LEDGER);
+
1653
+
1654 // report duplicate proposal messages
+
1655 overlay_.reportInboundTraffic(TrafficCount::category::proposal_duplicate, Message::messageSize(*m));
+
1656
+
1657 JLOG(p_journal_.trace()) << "Proposal: duplicate";
+
1658
+
1659 return;
+
1660 }
+
1661
+
1662 if (!isTrusted)
+
1663 {
+
1664 if (tracking_.load() == Tracking::diverged)
+
1665 {
+
1666 JLOG(p_journal_.debug()) << "Proposal: Dropping untrusted (peer divergence)";
+
1667 return;
+
1668 }
+
1669
+
1670 if (!cluster() && app_.getFeeTrack().isLoadedLocal())
+
1671 {
+
1672 JLOG(p_journal_.debug()) << "Proposal: Dropping untrusted (load)";
+
1673 return;
+
1674 }
+
1675 }
+
1676
+
1677 JLOG(p_journal_.trace()) << "Proposal: " << (isTrusted ? "trusted" : "untrusted");
+
1678
+
1679 auto proposal = RCLCxPeerPos(
+
1680 publicKey,
+
1681 sig,
+
1682 suppression,
+ +
1684 prevLedger,
+
1685 set.proposeseq(),
+
1686 proposeHash,
+
1687 closeTime,
+
1688 app_.timeKeeper().closeTime(),
+
1689 calcNodeID(app_.validatorManifests().getMasterKey(publicKey))});
+
1690
+
1691 std::weak_ptr<PeerImp> weak = shared_from_this();
+
1692 app_.getJobQueue().addJob(
+
1693 isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, "checkPropose", [weak, isTrusted, m, proposal]() {
+
1694 if (auto peer = weak.lock())
+
1695 peer->checkPropose(isTrusted, m, proposal);
+
1696 });
+
1697}
-
1699
-
1700void
-
- -
1702{
-
1703 JLOG(p_journal_.trace()) << "Status: Change";
-
1704
-
1705 if (!m->has_networktime())
-
1706 m->set_networktime(app_.timeKeeper().now().time_since_epoch().count());
-
1707
-
1708 {
-
1709 std::lock_guard sl(recentLock_);
-
1710 if (!last_status_.has_newstatus() || m->has_newstatus())
-
1711 last_status_ = *m;
-
1712 else
-
1713 {
-
1714 // preserve old status
-
1715 protocol::NodeStatus status = last_status_.newstatus();
-
1716 last_status_ = *m;
-
1717 m->set_newstatus(status);
-
1718 }
-
1719 }
-
1720
-
1721 if (m->newevent() == protocol::neLOST_SYNC)
-
1722 {
-
1723 bool outOfSync{false};
-
1724 {
-
1725 // Operations on closedLedgerHash_ and previousLedgerHash_ must be
-
1726 // guarded by recentLock_.
-
1727 std::lock_guard sl(recentLock_);
-
1728 if (!closedLedgerHash_.isZero())
-
1729 {
-
1730 outOfSync = true;
-
1731 closedLedgerHash_.zero();
-
1732 }
-
1733 previousLedgerHash_.zero();
-
1734 }
-
1735 if (outOfSync)
-
1736 {
-
1737 JLOG(p_journal_.debug()) << "Status: Out of sync";
-
1738 }
-
1739 return;
-
1740 }
-
1741
-
1742 {
-
1743 uint256 closedLedgerHash{};
-
1744 bool const peerChangedLedgers{m->has_ledgerhash() && stringIsUint256Sized(m->ledgerhash())};
-
1745
-
1746 {
-
1747 // Operations on closedLedgerHash_ and previousLedgerHash_ must be
-
1748 // guarded by recentLock_.
-
1749 std::lock_guard sl(recentLock_);
-
1750 if (peerChangedLedgers)
-
1751 {
-
1752 closedLedgerHash_ = m->ledgerhash();
-
1753 closedLedgerHash = closedLedgerHash_;
-
1754 addLedger(closedLedgerHash, sl);
-
1755 }
-
1756 else
-
1757 {
-
1758 closedLedgerHash_.zero();
-
1759 }
-
1760
-
1761 if (m->has_ledgerhashprevious() && stringIsUint256Sized(m->ledgerhashprevious()))
-
1762 {
-
1763 previousLedgerHash_ = m->ledgerhashprevious();
-
1764 addLedger(previousLedgerHash_, sl);
-
1765 }
-
1766 else
-
1767 {
-
1768 previousLedgerHash_.zero();
-
1769 }
-
1770 }
-
1771 if (peerChangedLedgers)
-
1772 {
-
1773 JLOG(p_journal_.debug()) << "LCL is " << closedLedgerHash;
-
1774 }
-
1775 else
-
1776 {
-
1777 JLOG(p_journal_.debug()) << "Status: No ledger";
-
1778 }
-
1779 }
-
1780
-
1781 if (m->has_firstseq() && m->has_lastseq())
-
1782 {
-
1783 std::lock_guard sl(recentLock_);
-
1784
-
1785 minLedger_ = m->firstseq();
-
1786 maxLedger_ = m->lastseq();
-
1787
-
1788 if ((maxLedger_ < minLedger_) || (minLedger_ == 0) || (maxLedger_ == 0))
-
1789 minLedger_ = maxLedger_ = 0;
-
1790 }
-
1791
-
1792 if (m->has_ledgerseq() && app_.getLedgerMaster().getValidatedLedgerAge() < 2min)
-
1793 {
-
1794 checkTracking(m->ledgerseq(), app_.getLedgerMaster().getValidLedgerIndex());
-
1795 }
-
1796
-
1797 app_.getOPs().pubPeerStatus([=, this]() -> Json::Value {
- -
1799
-
1800 if (m->has_newstatus())
-
1801 {
-
1802 switch (m->newstatus())
-
1803 {
-
1804 case protocol::nsCONNECTING:
-
1805 j[jss::status] = "CONNECTING";
-
1806 break;
-
1807 case protocol::nsCONNECTED:
-
1808 j[jss::status] = "CONNECTED";
-
1809 break;
-
1810 case protocol::nsMONITORING:
-
1811 j[jss::status] = "MONITORING";
-
1812 break;
-
1813 case protocol::nsVALIDATING:
-
1814 j[jss::status] = "VALIDATING";
-
1815 break;
-
1816 case protocol::nsSHUTTING:
-
1817 j[jss::status] = "SHUTTING";
-
1818 break;
-
1819 }
-
1820 }
-
1821
-
1822 if (m->has_newevent())
-
1823 {
-
1824 switch (m->newevent())
-
1825 {
-
1826 case protocol::neCLOSING_LEDGER:
-
1827 j[jss::action] = "CLOSING_LEDGER";
-
1828 break;
-
1829 case protocol::neACCEPTED_LEDGER:
-
1830 j[jss::action] = "ACCEPTED_LEDGER";
-
1831 break;
-
1832 case protocol::neSWITCHED_LEDGER:
-
1833 j[jss::action] = "SWITCHED_LEDGER";
-
1834 break;
-
1835 case protocol::neLOST_SYNC:
-
1836 j[jss::action] = "LOST_SYNC";
-
1837 break;
-
1838 }
-
1839 }
-
1840
-
1841 if (m->has_ledgerseq())
-
1842 {
-
1843 j[jss::ledger_index] = m->ledgerseq();
-
1844 }
-
1845
-
1846 if (m->has_ledgerhash())
-
1847 {
-
1848 uint256 closedLedgerHash{};
-
1849 {
-
1850 std::lock_guard sl(recentLock_);
-
1851 closedLedgerHash = closedLedgerHash_;
-
1852 }
-
1853 j[jss::ledger_hash] = to_string(closedLedgerHash);
-
1854 }
-
1855
-
1856 if (m->has_networktime())
-
1857 {
-
1858 j[jss::date] = Json::UInt(m->networktime());
-
1859 }
-
1860
-
1861 if (m->has_firstseq() && m->has_lastseq())
-
1862 {
-
1863 j[jss::ledger_index_min] = Json::UInt(m->firstseq());
-
1864 j[jss::ledger_index_max] = Json::UInt(m->lastseq());
-
1865 }
-
1866
-
1867 return j;
-
1868 });
-
1869}
+
1698
+
1699void
+
+ +
1701{
+
1702 JLOG(p_journal_.trace()) << "Status: Change";
+
1703
+
1704 if (!m->has_networktime())
+
1705 m->set_networktime(app_.timeKeeper().now().time_since_epoch().count());
+
1706
+
1707 {
+
1708 std::lock_guard sl(recentLock_);
+
1709 if (!last_status_.has_newstatus() || m->has_newstatus())
+
1710 last_status_ = *m;
+
1711 else
+
1712 {
+
1713 // preserve old status
+
1714 protocol::NodeStatus status = last_status_.newstatus();
+
1715 last_status_ = *m;
+
1716 m->set_newstatus(status);
+
1717 }
+
1718 }
+
1719
+
1720 if (m->newevent() == protocol::neLOST_SYNC)
+
1721 {
+
1722 bool outOfSync{false};
+
1723 {
+
1724 // Operations on closedLedgerHash_ and previousLedgerHash_ must be
+
1725 // guarded by recentLock_.
+
1726 std::lock_guard sl(recentLock_);
+
1727 if (!closedLedgerHash_.isZero())
+
1728 {
+
1729 outOfSync = true;
+
1730 closedLedgerHash_.zero();
+
1731 }
+
1732 previousLedgerHash_.zero();
+
1733 }
+
1734 if (outOfSync)
+
1735 {
+
1736 JLOG(p_journal_.debug()) << "Status: Out of sync";
+
1737 }
+
1738 return;
+
1739 }
+
1740
+
1741 {
+
1742 uint256 closedLedgerHash{};
+
1743 bool const peerChangedLedgers{m->has_ledgerhash() && stringIsUint256Sized(m->ledgerhash())};
+
1744
+
1745 {
+
1746 // Operations on closedLedgerHash_ and previousLedgerHash_ must be
+
1747 // guarded by recentLock_.
+
1748 std::lock_guard sl(recentLock_);
+
1749 if (peerChangedLedgers)
+
1750 {
+
1751 closedLedgerHash_ = m->ledgerhash();
+
1752 closedLedgerHash = closedLedgerHash_;
+
1753 addLedger(closedLedgerHash, sl);
+
1754 }
+
1755 else
+
1756 {
+
1757 closedLedgerHash_.zero();
+
1758 }
+
1759
+
1760 if (m->has_ledgerhashprevious() && stringIsUint256Sized(m->ledgerhashprevious()))
+
1761 {
+
1762 previousLedgerHash_ = m->ledgerhashprevious();
+
1763 addLedger(previousLedgerHash_, sl);
+
1764 }
+
1765 else
+
1766 {
+
1767 previousLedgerHash_.zero();
+
1768 }
+
1769 }
+
1770 if (peerChangedLedgers)
+
1771 {
+
1772 JLOG(p_journal_.debug()) << "LCL is " << closedLedgerHash;
+
1773 }
+
1774 else
+
1775 {
+
1776 JLOG(p_journal_.debug()) << "Status: No ledger";
+
1777 }
+
1778 }
+
1779
+
1780 if (m->has_firstseq() && m->has_lastseq())
+
1781 {
+
1782 std::lock_guard sl(recentLock_);
+
1783
+
1784 minLedger_ = m->firstseq();
+
1785 maxLedger_ = m->lastseq();
+
1786
+
1787 if ((maxLedger_ < minLedger_) || (minLedger_ == 0) || (maxLedger_ == 0))
+
1788 minLedger_ = maxLedger_ = 0;
+
1789 }
+
1790
+
1791 if (m->has_ledgerseq() && app_.getLedgerMaster().getValidatedLedgerAge() < 2min)
+
1792 {
+
1793 checkTracking(m->ledgerseq(), app_.getLedgerMaster().getValidLedgerIndex());
+
1794 }
+
1795
+
1796 app_.getOPs().pubPeerStatus([=, this]() -> Json::Value {
+ +
1798
+
1799 if (m->has_newstatus())
+
1800 {
+
1801 switch (m->newstatus())
+
1802 {
+
1803 case protocol::nsCONNECTING:
+
1804 j[jss::status] = "CONNECTING";
+
1805 break;
+
1806 case protocol::nsCONNECTED:
+
1807 j[jss::status] = "CONNECTED";
+
1808 break;
+
1809 case protocol::nsMONITORING:
+
1810 j[jss::status] = "MONITORING";
+
1811 break;
+
1812 case protocol::nsVALIDATING:
+
1813 j[jss::status] = "VALIDATING";
+
1814 break;
+
1815 case protocol::nsSHUTTING:
+
1816 j[jss::status] = "SHUTTING";
+
1817 break;
+
1818 }
+
1819 }
+
1820
+
1821 if (m->has_newevent())
+
1822 {
+
1823 switch (m->newevent())
+
1824 {
+
1825 case protocol::neCLOSING_LEDGER:
+
1826 j[jss::action] = "CLOSING_LEDGER";
+
1827 break;
+
1828 case protocol::neACCEPTED_LEDGER:
+
1829 j[jss::action] = "ACCEPTED_LEDGER";
+
1830 break;
+
1831 case protocol::neSWITCHED_LEDGER:
+
1832 j[jss::action] = "SWITCHED_LEDGER";
+
1833 break;
+
1834 case protocol::neLOST_SYNC:
+
1835 j[jss::action] = "LOST_SYNC";
+
1836 break;
+
1837 }
+
1838 }
+
1839
+
1840 if (m->has_ledgerseq())
+
1841 {
+
1842 j[jss::ledger_index] = m->ledgerseq();
+
1843 }
+
1844
+
1845 if (m->has_ledgerhash())
+
1846 {
+
1847 uint256 closedLedgerHash{};
+
1848 {
+
1849 std::lock_guard sl(recentLock_);
+
1850 closedLedgerHash = closedLedgerHash_;
+
1851 }
+
1852 j[jss::ledger_hash] = to_string(closedLedgerHash);
+
1853 }
+
1854
+
1855 if (m->has_networktime())
+
1856 {
+
1857 j[jss::date] = Json::UInt(m->networktime());
+
1858 }
+
1859
+
1860 if (m->has_firstseq() && m->has_lastseq())
+
1861 {
+
1862 j[jss::ledger_index_min] = Json::UInt(m->firstseq());
+
1863 j[jss::ledger_index_max] = Json::UInt(m->lastseq());
+
1864 }
+
1865
+
1866 return j;
+
1867 });
+
1868}
-
1870
-
1871void
-
-
1872PeerImp::checkTracking(std::uint32_t validationSeq)
-
1873{
-
1874 std::uint32_t serverSeq;
-
1875 {
-
1876 // Extract the sequence number of the highest
-
1877 // ledger this peer has
-
1878 std::lock_guard sl(recentLock_);
-
1879
-
1880 serverSeq = maxLedger_;
-
1881 }
-
1882 if (serverSeq != 0)
-
1883 {
-
1884 // Compare the peer's ledger sequence to the
-
1885 // sequence of a recently-validated ledger
-
1886 checkTracking(serverSeq, validationSeq);
-
1887 }
-
1888}
+
1869
+
1870void
+
+
1871PeerImp::checkTracking(std::uint32_t validationSeq)
+
1872{
+
1873 std::uint32_t serverSeq;
+
1874 {
+
1875 // Extract the sequence number of the highest
+
1876 // ledger this peer has
+
1877 std::lock_guard sl(recentLock_);
+
1878
+
1879 serverSeq = maxLedger_;
+
1880 }
+
1881 if (serverSeq != 0)
+
1882 {
+
1883 // Compare the peer's ledger sequence to the
+
1884 // sequence of a recently-validated ledger
+
1885 checkTracking(serverSeq, validationSeq);
+
1886 }
+
1887}
-
1889
-
1890void
-
-
1891PeerImp::checkTracking(std::uint32_t seq1, std::uint32_t seq2)
-
1892{
-
1893 int diff = std::max(seq1, seq2) - std::min(seq1, seq2);
-
1894
-
1895 if (diff < Tuning::convergedLedgerLimit)
-
1896 {
-
1897 // The peer's ledger sequence is close to the validation's
-
1898 tracking_ = Tracking::converged;
-
1899 }
-
1900
-
1901 if ((diff > Tuning::divergedLedgerLimit) && (tracking_.load() != Tracking::diverged))
-
1902 {
-
1903 // The peer's ledger sequence is way off the validation's
-
1904 std::lock_guard sl(recentLock_);
-
1905
-
1906 tracking_ = Tracking::diverged;
-
1907 trackingTime_ = clock_type::now();
-
1908 }
-
1909}
+
1888
+
1889void
+
+
1890PeerImp::checkTracking(std::uint32_t seq1, std::uint32_t seq2)
+
1891{
+
1892 int diff = std::max(seq1, seq2) - std::min(seq1, seq2);
+
1893
+
1894 if (diff < Tuning::convergedLedgerLimit)
+
1895 {
+
1896 // The peer's ledger sequence is close to the validation's
+
1897 tracking_ = Tracking::converged;
+
1898 }
+
1899
+
1900 if ((diff > Tuning::divergedLedgerLimit) && (tracking_.load() != Tracking::diverged))
+
1901 {
+
1902 // The peer's ledger sequence is way off the validation's
+
1903 std::lock_guard sl(recentLock_);
+
1904
+
1905 tracking_ = Tracking::diverged;
+
1906 trackingTime_ = clock_type::now();
+
1907 }
+
1908}
-
1910
-
1911void
-
- -
1913{
-
1914 if (!stringIsUint256Sized(m->hash()))
-
1915 {
-
1916 fee_.update(Resource::feeMalformedRequest, "bad hash");
-
1917 return;
-
1918 }
-
1919
-
1920 uint256 const hash{m->hash()};
-
1921
-
1922 if (m->status() == protocol::tsHAVE)
-
1923 {
-
1924 std::lock_guard sl(recentLock_);
-
1925
-
1926 if (std::find(recentTxSets_.begin(), recentTxSets_.end(), hash) != recentTxSets_.end())
-
1927 {
-
1928 fee_.update(Resource::feeUselessData, "duplicate (tsHAVE)");
-
1929 return;
-
1930 }
-
1931
-
1932 recentTxSets_.push_back(hash);
-
1933 }
-
1934}
+
1909
+
1910void
+
+ +
1912{
+
1913 if (!stringIsUint256Sized(m->hash()))
+
1914 {
+
1915 fee_.update(Resource::feeMalformedRequest, "bad hash");
+
1916 return;
+
1917 }
+
1918
+
1919 uint256 const hash{m->hash()};
+
1920
+
1921 if (m->status() == protocol::tsHAVE)
+
1922 {
+
1923 std::lock_guard sl(recentLock_);
+
1924
+
1925 if (std::find(recentTxSets_.begin(), recentTxSets_.end(), hash) != recentTxSets_.end())
+
1926 {
+
1927 fee_.update(Resource::feeUselessData, "duplicate (tsHAVE)");
+
1928 return;
+
1929 }
+
1930
+
1931 recentTxSets_.push_back(hash);
+
1932 }
+
1933}
-
1935
-
1936void
-
-
1937PeerImp::onValidatorListMessage(
-
1938 std::string const& messageType,
-
1939 std::string const& manifest,
-
1940 std::uint32_t version,
-
1941 std::vector<ValidatorBlobInfo> const& blobs)
-
1942{
-
1943 // If there are no blobs, the message is malformed (possibly because of
-
1944 // ValidatorList class rules), so charge accordingly and skip processing.
-
1945 if (blobs.empty())
-
1946 {
-
1947 JLOG(p_journal_.warn()) << "Ignored malformed " << messageType;
-
1948 // This shouldn't ever happen with a well-behaved peer
-
1949 fee_.update(Resource::feeHeavyBurdenPeer, "no blobs");
-
1950 return;
-
1951 }
-
1952
-
1953 auto const hash = sha512Half(manifest, blobs, version);
-
1954
-
1955 JLOG(p_journal_.debug()) << "Received " << messageType;
-
1956
-
1957 if (!app_.getHashRouter().addSuppressionPeer(hash, id_))
-
1958 {
-
1959 JLOG(p_journal_.debug()) << messageType << ": received duplicate " << messageType;
-
1960 // Charging this fee here won't hurt the peer in the normal
-
1961 // course of operation (ie. refresh every 5 minutes), but
-
1962 // will add up if the peer is misbehaving.
-
1963 fee_.update(Resource::feeUselessData, "duplicate");
-
1964 return;
-
1965 }
-
1966
-
1967 auto const applyResult = app_.validators().applyListsAndBroadcast(
-
1968 manifest,
-
1969 version,
-
1970 blobs,
-
1971 remote_address_.to_string(),
-
1972 hash,
-
1973 app_.overlay(),
-
1974 app_.getHashRouter(),
-
1975 app_.getOPs());
-
1976
-
1977 JLOG(p_journal_.debug()) << "Processed " << messageType << " version " << version << " from "
-
1978 << (applyResult.publisherKey ? strHex(*applyResult.publisherKey)
-
1979 : "unknown or invalid publisher")
-
1980 << " with best result " << to_string(applyResult.bestDisposition());
-
1981
-
1982 // Act based on the best result
-
1983 switch (applyResult.bestDisposition())
-
1984 {
-
1985 // New list
-
1986 case ListDisposition::accepted:
-
1987 // Newest list is expired, and that needs to be broadcast, too
-
1988 case ListDisposition::expired:
-
1989 // Future list
-
1990 case ListDisposition::pending: {
-
1991 std::lock_guard<std::mutex> sl(recentLock_);
-
1992
-
1993 XRPL_ASSERT(
-
1994 applyResult.publisherKey,
-
1995 "xrpl::PeerImp::onValidatorListMessage : publisher key is "
-
1996 "set");
-
1997 auto const& pubKey = *applyResult.publisherKey;
-
1998#ifndef NDEBUG
-
1999 if (auto const iter = publisherListSequences_.find(pubKey); iter != publisherListSequences_.end())
-
2000 {
-
2001 XRPL_ASSERT(
-
2002 iter->second < applyResult.sequence, "xrpl::PeerImp::onValidatorListMessage : lower sequence");
-
2003 }
-
2004#endif
-
2005 publisherListSequences_[pubKey] = applyResult.sequence;
-
2006 }
-
2007 break;
-
2008 case ListDisposition::same_sequence:
-
2009 case ListDisposition::known_sequence:
-
2010#ifndef NDEBUG
-
2011 {
-
2012 std::lock_guard<std::mutex> sl(recentLock_);
-
2013 XRPL_ASSERT(
-
2014 applyResult.sequence && applyResult.publisherKey,
-
2015 "xrpl::PeerImp::onValidatorListMessage : nonzero sequence "
-
2016 "and set publisher key");
-
2017 XRPL_ASSERT(
-
2018 publisherListSequences_[*applyResult.publisherKey] <= applyResult.sequence,
-
2019 "xrpl::PeerImp::onValidatorListMessage : maximum sequence");
-
2020 }
-
2021#endif // !NDEBUG
-
2022
-
2023 break;
-
2024 case ListDisposition::stale:
-
2025 case ListDisposition::untrusted:
-
2026 case ListDisposition::invalid:
-
2027 case ListDisposition::unsupported_version:
-
2028 break;
-
2029 // LCOV_EXCL_START
-
2030 default:
-
2031 UNREACHABLE(
-
2032 "xrpl::PeerImp::onValidatorListMessage : invalid best list "
-
2033 "disposition");
-
2034 // LCOV_EXCL_STOP
-
2035 }
-
2036
-
2037 // Charge based on the worst result
-
2038 switch (applyResult.worstDisposition())
-
2039 {
-
2040 case ListDisposition::accepted:
-
2041 case ListDisposition::expired:
-
2042 case ListDisposition::pending:
-
2043 // No charges for good data
-
2044 break;
-
2045 case ListDisposition::same_sequence:
-
2046 case ListDisposition::known_sequence:
-
2047 // Charging this fee here won't hurt the peer in the normal
-
2048 // course of operation (ie. refresh every 5 minutes), but
-
2049 // will add up if the peer is misbehaving.
-
2050 fee_.update(Resource::feeUselessData, " duplicate (same_sequence or known_sequence)");
-
2051 break;
-
2052 case ListDisposition::stale:
-
2053 // There are very few good reasons for a peer to send an
-
2054 // old list, particularly more than once.
-
2055 fee_.update(Resource::feeInvalidData, "expired");
-
2056 break;
-
2057 case ListDisposition::untrusted:
-
2058 // Charging this fee here won't hurt the peer in the normal
-
2059 // course of operation (ie. refresh every 5 minutes), but
-
2060 // will add up if the peer is misbehaving.
-
2061 fee_.update(Resource::feeUselessData, "untrusted");
-
2062 break;
-
2063 case ListDisposition::invalid:
-
2064 // This shouldn't ever happen with a well-behaved peer
-
2065 fee_.update(Resource::feeInvalidSignature, "invalid list disposition");
-
2066 break;
-
2067 case ListDisposition::unsupported_version:
-
2068 // During a version transition, this may be legitimate.
-
2069 // If it happens frequently, that's probably bad.
-
2070 fee_.update(Resource::feeInvalidData, "version");
-
2071 break;
-
2072 // LCOV_EXCL_START
-
2073 default:
-
2074 UNREACHABLE(
-
2075 "xrpl::PeerImp::onValidatorListMessage : invalid worst list "
-
2076 "disposition");
-
2077 // LCOV_EXCL_STOP
-
2078 }
-
2079
-
2080 // Log based on all the results.
-
2081 for (auto const& [disp, count] : applyResult.dispositions)
-
2082 {
-
2083 switch (disp)
-
2084 {
-
2085 // New list
-
2086 case ListDisposition::accepted:
-
2087 JLOG(p_journal_.debug()) << "Applied " << count << " new " << messageType;
-
2088 break;
-
2089 // Newest list is expired, and that needs to be broadcast, too
-
2090 case ListDisposition::expired:
-
2091 JLOG(p_journal_.debug()) << "Applied " << count << " expired " << messageType;
-
2092 break;
-
2093 // Future list
-
2094 case ListDisposition::pending:
-
2095 JLOG(p_journal_.debug()) << "Processed " << count << " future " << messageType;
-
2096 break;
-
2097 case ListDisposition::same_sequence:
-
2098 JLOG(p_journal_.warn()) << "Ignored " << count << " " << messageType << "(s) with current sequence";
-
2099 break;
-
2100 case ListDisposition::known_sequence:
-
2101 JLOG(p_journal_.warn()) << "Ignored " << count << " " << messageType << "(s) with future sequence";
-
2102 break;
-
2103 case ListDisposition::stale:
-
2104 JLOG(p_journal_.warn()) << "Ignored " << count << "stale " << messageType;
-
2105 break;
-
2106 case ListDisposition::untrusted:
-
2107 JLOG(p_journal_.warn()) << "Ignored " << count << " untrusted " << messageType;
-
2108 break;
-
2109 case ListDisposition::unsupported_version:
-
2110 JLOG(p_journal_.warn()) << "Ignored " << count << "unsupported version " << messageType;
-
2111 break;
-
2112 case ListDisposition::invalid:
-
2113 JLOG(p_journal_.warn()) << "Ignored " << count << "invalid " << messageType;
-
2114 break;
-
2115 // LCOV_EXCL_START
-
2116 default:
-
2117 UNREACHABLE(
-
2118 "xrpl::PeerImp::onValidatorListMessage : invalid list "
-
2119 "disposition");
-
2120 // LCOV_EXCL_STOP
-
2121 }
-
2122 }
-
2123}
+
1934
+
1935void
+
+
1936PeerImp::onValidatorListMessage(
+
1937 std::string const& messageType,
+
1938 std::string const& manifest,
+
1939 std::uint32_t version,
+
1940 std::vector<ValidatorBlobInfo> const& blobs)
+
1941{
+
1942 // If there are no blobs, the message is malformed (possibly because of
+
1943 // ValidatorList class rules), so charge accordingly and skip processing.
+
1944 if (blobs.empty())
+
1945 {
+
1946 JLOG(p_journal_.warn()) << "Ignored malformed " << messageType;
+
1947 // This shouldn't ever happen with a well-behaved peer
+
1948 fee_.update(Resource::feeHeavyBurdenPeer, "no blobs");
+
1949 return;
+
1950 }
+
1951
+
1952 auto const hash = sha512Half(manifest, blobs, version);
+
1953
+
1954 JLOG(p_journal_.debug()) << "Received " << messageType;
+
1955
+
1956 if (!app_.getHashRouter().addSuppressionPeer(hash, id_))
+
1957 {
+
1958 JLOG(p_journal_.debug()) << messageType << ": received duplicate " << messageType;
+
1959 // Charging this fee here won't hurt the peer in the normal
+
1960 // course of operation (ie. refresh every 5 minutes), but
+
1961 // will add up if the peer is misbehaving.
+
1962 fee_.update(Resource::feeUselessData, "duplicate");
+
1963 return;
+
1964 }
+
1965
+
1966 auto const applyResult = app_.validators().applyListsAndBroadcast(
+
1967 manifest,
+
1968 version,
+
1969 blobs,
+
1970 remote_address_.to_string(),
+
1971 hash,
+
1972 app_.overlay(),
+
1973 app_.getHashRouter(),
+
1974 app_.getOPs());
+
1975
+
1976 JLOG(p_journal_.debug()) << "Processed " << messageType << " version " << version << " from "
+
1977 << (applyResult.publisherKey ? strHex(*applyResult.publisherKey)
+
1978 : "unknown or invalid publisher")
+
1979 << " with best result " << to_string(applyResult.bestDisposition());
+
1980
+
1981 // Act based on the best result
+
1982 switch (applyResult.bestDisposition())
+
1983 {
+
1984 // New list
+
1985 case ListDisposition::accepted:
+
1986 // Newest list is expired, and that needs to be broadcast, too
+
1987 case ListDisposition::expired:
+
1988 // Future list
+
1989 case ListDisposition::pending: {
+
1990 std::lock_guard<std::mutex> sl(recentLock_);
+
1991
+
1992 XRPL_ASSERT(
+
1993 applyResult.publisherKey,
+
1994 "xrpl::PeerImp::onValidatorListMessage : publisher key is "
+
1995 "set");
+
1996 auto const& pubKey = *applyResult.publisherKey;
+
1997#ifndef NDEBUG
+
1998 if (auto const iter = publisherListSequences_.find(pubKey); iter != publisherListSequences_.end())
+
1999 {
+
2000 XRPL_ASSERT(
+
2001 iter->second < applyResult.sequence, "xrpl::PeerImp::onValidatorListMessage : lower sequence");
+
2002 }
+
2003#endif
+
2004 publisherListSequences_[pubKey] = applyResult.sequence;
+
2005 }
+
2006 break;
+
2007 case ListDisposition::same_sequence:
+
2008 case ListDisposition::known_sequence:
+
2009#ifndef NDEBUG
+
2010 {
+
2011 std::lock_guard<std::mutex> sl(recentLock_);
+
2012 XRPL_ASSERT(
+
2013 applyResult.sequence && applyResult.publisherKey,
+
2014 "xrpl::PeerImp::onValidatorListMessage : nonzero sequence "
+
2015 "and set publisher key");
+
2016 XRPL_ASSERT(
+
2017 publisherListSequences_[*applyResult.publisherKey] <= applyResult.sequence,
+
2018 "xrpl::PeerImp::onValidatorListMessage : maximum sequence");
+
2019 }
+
2020#endif // !NDEBUG
+
2021
+
2022 break;
+
2023 case ListDisposition::stale:
+
2024 case ListDisposition::untrusted:
+
2025 case ListDisposition::invalid:
+
2026 case ListDisposition::unsupported_version:
+
2027 break;
+
2028 // LCOV_EXCL_START
+
2029 default:
+
2030 UNREACHABLE(
+
2031 "xrpl::PeerImp::onValidatorListMessage : invalid best list "
+
2032 "disposition");
+
2033 // LCOV_EXCL_STOP
+
2034 }
+
2035
+
2036 // Charge based on the worst result
+
2037 switch (applyResult.worstDisposition())
+
2038 {
+
2039 case ListDisposition::accepted:
+
2040 case ListDisposition::expired:
+
2041 case ListDisposition::pending:
+
2042 // No charges for good data
+
2043 break;
+
2044 case ListDisposition::same_sequence:
+
2045 case ListDisposition::known_sequence:
+
2046 // Charging this fee here won't hurt the peer in the normal
+
2047 // course of operation (ie. refresh every 5 minutes), but
+
2048 // will add up if the peer is misbehaving.
+
2049 fee_.update(Resource::feeUselessData, " duplicate (same_sequence or known_sequence)");
+
2050 break;
+
2051 case ListDisposition::stale:
+
2052 // There are very few good reasons for a peer to send an
+
2053 // old list, particularly more than once.
+
2054 fee_.update(Resource::feeInvalidData, "expired");
+
2055 break;
+
2056 case ListDisposition::untrusted:
+
2057 // Charging this fee here won't hurt the peer in the normal
+
2058 // course of operation (ie. refresh every 5 minutes), but
+
2059 // will add up if the peer is misbehaving.
+
2060 fee_.update(Resource::feeUselessData, "untrusted");
+
2061 break;
+
2062 case ListDisposition::invalid:
+
2063 // This shouldn't ever happen with a well-behaved peer
+
2064 fee_.update(Resource::feeInvalidSignature, "invalid list disposition");
+
2065 break;
+
2066 case ListDisposition::unsupported_version:
+
2067 // During a version transition, this may be legitimate.
+
2068 // If it happens frequently, that's probably bad.
+
2069 fee_.update(Resource::feeInvalidData, "version");
+
2070 break;
+
2071 // LCOV_EXCL_START
+
2072 default:
+
2073 UNREACHABLE(
+
2074 "xrpl::PeerImp::onValidatorListMessage : invalid worst list "
+
2075 "disposition");
+
2076 // LCOV_EXCL_STOP
+
2077 }
+
2078
+
2079 // Log based on all the results.
+
2080 for (auto const& [disp, count] : applyResult.dispositions)
+
2081 {
+
2082 switch (disp)
+
2083 {
+
2084 // New list
+
2085 case ListDisposition::accepted:
+
2086 JLOG(p_journal_.debug()) << "Applied " << count << " new " << messageType;
+
2087 break;
+
2088 // Newest list is expired, and that needs to be broadcast, too
+
2089 case ListDisposition::expired:
+
2090 JLOG(p_journal_.debug()) << "Applied " << count << " expired " << messageType;
+
2091 break;
+
2092 // Future list
+
2093 case ListDisposition::pending:
+
2094 JLOG(p_journal_.debug()) << "Processed " << count << " future " << messageType;
+
2095 break;
+
2096 case ListDisposition::same_sequence:
+
2097 JLOG(p_journal_.warn()) << "Ignored " << count << " " << messageType << "(s) with current sequence";
+
2098 break;
+
2099 case ListDisposition::known_sequence:
+
2100 JLOG(p_journal_.warn()) << "Ignored " << count << " " << messageType << "(s) with future sequence";
+
2101 break;
+
2102 case ListDisposition::stale:
+
2103 JLOG(p_journal_.warn()) << "Ignored " << count << "stale " << messageType;
+
2104 break;
+
2105 case ListDisposition::untrusted:
+
2106 JLOG(p_journal_.warn()) << "Ignored " << count << " untrusted " << messageType;
+
2107 break;
+
2108 case ListDisposition::unsupported_version:
+
2109 JLOG(p_journal_.warn()) << "Ignored " << count << "unsupported version " << messageType;
+
2110 break;
+
2111 case ListDisposition::invalid:
+
2112 JLOG(p_journal_.warn()) << "Ignored " << count << "invalid " << messageType;
+
2113 break;
+
2114 // LCOV_EXCL_START
+
2115 default:
+
2116 UNREACHABLE(
+
2117 "xrpl::PeerImp::onValidatorListMessage : invalid list "
+
2118 "disposition");
+
2119 // LCOV_EXCL_STOP
+
2120 }
+
2121 }
+
2122}
-
2124
-
2125void
-
- -
2127{
-
2128 try
-
2129 {
-
2130 if (!supportsFeature(ProtocolFeature::ValidatorListPropagation))
-
2131 {
-
2132 JLOG(p_journal_.debug()) << "ValidatorList: received validator list from peer using "
-
2133 << "protocol version " << to_string(protocol_)
-
2134 << " which shouldn't support this feature.";
-
2135 fee_.update(Resource::feeUselessData, "unsupported peer");
-
2136 return;
-
2137 }
-
2138 onValidatorListMessage("ValidatorList", m->manifest(), m->version(), ValidatorList::parseBlobs(*m));
-
2139 }
-
2140 catch (std::exception const& e)
-
2141 {
-
2142 JLOG(p_journal_.warn()) << "ValidatorList: Exception, " << e.what();
-
2143 using namespace std::string_literals;
-
2144 fee_.update(Resource::feeInvalidData, e.what());
-
2145 }
-
2146}
+
2123
+
2124void
+
+ +
2126{
+
2127 try
+
2128 {
+
2129 if (!supportsFeature(ProtocolFeature::ValidatorListPropagation))
+
2130 {
+
2131 JLOG(p_journal_.debug()) << "ValidatorList: received validator list from peer using "
+
2132 << "protocol version " << to_string(protocol_)
+
2133 << " which shouldn't support this feature.";
+
2134 fee_.update(Resource::feeUselessData, "unsupported peer");
+
2135 return;
+
2136 }
+
2137 onValidatorListMessage("ValidatorList", m->manifest(), m->version(), ValidatorList::parseBlobs(*m));
+
2138 }
+
2139 catch (std::exception const& e)
+
2140 {
+
2141 JLOG(p_journal_.warn()) << "ValidatorList: Exception, " << e.what();
+
2142 using namespace std::string_literals;
+
2143 fee_.update(Resource::feeInvalidData, e.what());
+
2144 }
+
2145}
-
2147
-
2148void
-
- -
2150{
-
2151 try
-
2152 {
-
2153 if (!supportsFeature(ProtocolFeature::ValidatorList2Propagation))
-
2154 {
-
2155 JLOG(p_journal_.debug()) << "ValidatorListCollection: received validator list from peer "
-
2156 << "using protocol version " << to_string(protocol_)
-
2157 << " which shouldn't support this feature.";
-
2158 fee_.update(Resource::feeUselessData, "unsupported peer");
-
2159 return;
-
2160 }
-
2161 else if (m->version() < 2)
-
2162 {
-
2163 JLOG(p_journal_.debug()) << "ValidatorListCollection: received invalid validator list "
-
2164 "version "
-
2165 << m->version() << " from peer using protocol version " << to_string(protocol_);
-
2166 fee_.update(Resource::feeInvalidData, "wrong version");
-
2167 return;
-
2168 }
-
2169 onValidatorListMessage("ValidatorListCollection", m->manifest(), m->version(), ValidatorList::parseBlobs(*m));
-
2170 }
-
2171 catch (std::exception const& e)
-
2172 {
-
2173 JLOG(p_journal_.warn()) << "ValidatorListCollection: Exception, " << e.what();
-
2174 using namespace std::string_literals;
-
2175 fee_.update(Resource::feeInvalidData, e.what());
-
2176 }
-
2177}
+
2146
+
2147void
+
+ +
2149{
+
2150 try
+
2151 {
+
2152 if (!supportsFeature(ProtocolFeature::ValidatorList2Propagation))
+
2153 {
+
2154 JLOG(p_journal_.debug()) << "ValidatorListCollection: received validator list from peer "
+
2155 << "using protocol version " << to_string(protocol_)
+
2156 << " which shouldn't support this feature.";
+
2157 fee_.update(Resource::feeUselessData, "unsupported peer");
+
2158 return;
+
2159 }
+
2160 else if (m->version() < 2)
+
2161 {
+
2162 JLOG(p_journal_.debug()) << "ValidatorListCollection: received invalid validator list "
+
2163 "version "
+
2164 << m->version() << " from peer using protocol version " << to_string(protocol_);
+
2165 fee_.update(Resource::feeInvalidData, "wrong version");
+
2166 return;
+
2167 }
+
2168 onValidatorListMessage("ValidatorListCollection", m->manifest(), m->version(), ValidatorList::parseBlobs(*m));
+
2169 }
+
2170 catch (std::exception const& e)
+
2171 {
+
2172 JLOG(p_journal_.warn()) << "ValidatorListCollection: Exception, " << e.what();
+
2173 using namespace std::string_literals;
+
2174 fee_.update(Resource::feeInvalidData, e.what());
+
2175 }
+
2176}
-
2178
-
2179void
-
- -
2181{
-
2182 if (m->validation().size() < 50)
-
2183 {
-
2184 JLOG(p_journal_.warn()) << "Validation: Too small";
-
2185 fee_.update(Resource::feeMalformedRequest, "too small");
-
2186 return;
-
2187 }
-
2188
-
2189 try
-
2190 {
-
2191 auto const closeTime = app_.timeKeeper().closeTime();
-
2192
- -
2194 {
-
2195 SerialIter sit(makeSlice(m->validation()));
- -
2197 std::ref(sit),
-
2198 [this](PublicKey const& pk) { return calcNodeID(app_.validatorManifests().getMasterKey(pk)); },
-
2199 false);
-
2200 val->setSeen(closeTime);
-
2201 }
-
2202
-
2203 if (!isCurrent(
-
2204 app_.getValidations().parms(), app_.timeKeeper().closeTime(), val->getSignTime(), val->getSeenTime()))
-
2205 {
-
2206 JLOG(p_journal_.trace()) << "Validation: Not current";
-
2207 fee_.update(Resource::feeUselessData, "not current");
-
2208 return;
-
2209 }
-
2210
-
2211 // RH TODO: when isTrusted = false we should probably also cache a key
-
2212 // suppression for 30 seconds to avoid doing a relatively expensive
-
2213 // lookup every time a spam packet is received
-
2214 auto const isTrusted = app_.validators().trusted(val->getSignerPublic());
-
2215
-
2216 // If the operator has specified that untrusted validations be
-
2217 // dropped then this happens here I.e. before further wasting CPU
-
2218 // verifying the signature of an untrusted key
-
2219 if (!isTrusted)
-
2220 {
-
2221 // increase untrusted validations received
-
2222 overlay_.reportInboundTraffic(TrafficCount::category::validation_untrusted, Message::messageSize(*m));
-
2223
-
2224 if (app_.config().RELAY_UNTRUSTED_VALIDATIONS == -1)
-
2225 return;
-
2226 }
-
2227
-
2228 auto key = sha512Half(makeSlice(m->validation()));
-
2229
-
2230 auto [added, relayed] = app_.getHashRouter().addSuppressionPeerWithStatus(key, id_);
-
2231
-
2232 if (!added)
-
2233 {
-
2234 // Count unique messages (Slots has it's own 'HashRouter'), which a
-
2235 // peer receives within IDLED seconds since the message has been
-
2236 // relayed.
-
2237 if (relayed && (stopwatch().now() - *relayed) < reduce_relay::IDLED)
-
2238 overlay_.updateSlotAndSquelch(key, val->getSignerPublic(), id_, protocol::mtVALIDATION);
-
2239
-
2240 // increase duplicate validations received
-
2241 overlay_.reportInboundTraffic(TrafficCount::category::validation_duplicate, Message::messageSize(*m));
-
2242
-
2243 JLOG(p_journal_.trace()) << "Validation: duplicate";
-
2244 return;
-
2245 }
-
2246
-
2247 if (!isTrusted && (tracking_.load() == Tracking::diverged))
-
2248 {
-
2249 JLOG(p_journal_.debug()) << "Dropping untrusted validation from diverged peer";
-
2250 }
-
2251 else if (isTrusted || !app_.getFeeTrack().isLoadedLocal())
-
2252 {
-
2253 std::string const name = isTrusted ? "ChkTrust" : "ChkUntrust";
-
2254
-
2255 std::weak_ptr<PeerImp> weak = shared_from_this();
-
2256 app_.getJobQueue().addJob(isTrusted ? jtVALIDATION_t : jtVALIDATION_ut, name, [weak, val, m, key]() {
-
2257 if (auto peer = weak.lock())
-
2258 peer->checkValidation(val, key, m);
-
2259 });
-
2260 }
-
2261 else
-
2262 {
-
2263 JLOG(p_journal_.debug()) << "Dropping untrusted validation for load";
-
2264 }
-
2265 }
-
2266 catch (std::exception const& e)
-
2267 {
-
2268 JLOG(p_journal_.warn()) << "Exception processing validation: " << e.what();
-
2269 using namespace std::string_literals;
-
2270 fee_.update(Resource::feeMalformedRequest, e.what());
-
2271 }
-
2272}
+
2177
+
2178void
+
+ +
2180{
+
2181 if (m->validation().size() < 50)
+
2182 {
+
2183 JLOG(p_journal_.warn()) << "Validation: Too small";
+
2184 fee_.update(Resource::feeMalformedRequest, "too small");
+
2185 return;
+
2186 }
+
2187
+
2188 try
+
2189 {
+
2190 auto const closeTime = app_.timeKeeper().closeTime();
+
2191
+ +
2193 {
+
2194 SerialIter sit(makeSlice(m->validation()));
+ +
2196 std::ref(sit),
+
2197 [this](PublicKey const& pk) { return calcNodeID(app_.validatorManifests().getMasterKey(pk)); },
+
2198 false);
+
2199 val->setSeen(closeTime);
+
2200 }
+
2201
+
2202 if (!isCurrent(
+
2203 app_.getValidations().parms(), app_.timeKeeper().closeTime(), val->getSignTime(), val->getSeenTime()))
+
2204 {
+
2205 JLOG(p_journal_.trace()) << "Validation: Not current";
+
2206 fee_.update(Resource::feeUselessData, "not current");
+
2207 return;
+
2208 }
+
2209
+
2210 // RH TODO: when isTrusted = false we should probably also cache a key
+
2211 // suppression for 30 seconds to avoid doing a relatively expensive
+
2212 // lookup every time a spam packet is received
+
2213 auto const isTrusted = app_.validators().trusted(val->getSignerPublic());
+
2214
+
2215 // If the operator has specified that untrusted validations be
+
2216 // dropped then this happens here I.e. before further wasting CPU
+
2217 // verifying the signature of an untrusted key
+
2218 if (!isTrusted)
+
2219 {
+
2220 // increase untrusted validations received
+
2221 overlay_.reportInboundTraffic(TrafficCount::category::validation_untrusted, Message::messageSize(*m));
+
2222
+
2223 if (app_.config().RELAY_UNTRUSTED_VALIDATIONS == -1)
+
2224 return;
+
2225 }
+
2226
+
2227 auto key = sha512Half(makeSlice(m->validation()));
+
2228
+
2229 auto [added, relayed] = app_.getHashRouter().addSuppressionPeerWithStatus(key, id_);
+
2230
+
2231 if (!added)
+
2232 {
+
2233 // Count unique messages (Slots has it's own 'HashRouter'), which a
+
2234 // peer receives within IDLED seconds since the message has been
+
2235 // relayed.
+
2236 if (relayed && (stopwatch().now() - *relayed) < reduce_relay::IDLED)
+
2237 overlay_.updateSlotAndSquelch(key, val->getSignerPublic(), id_, protocol::mtVALIDATION);
+
2238
+
2239 // increase duplicate validations received
+
2240 overlay_.reportInboundTraffic(TrafficCount::category::validation_duplicate, Message::messageSize(*m));
+
2241
+
2242 JLOG(p_journal_.trace()) << "Validation: duplicate";
+
2243 return;
+
2244 }
+
2245
+
2246 if (!isTrusted && (tracking_.load() == Tracking::diverged))
+
2247 {
+
2248 JLOG(p_journal_.debug()) << "Dropping untrusted validation from diverged peer";
+
2249 }
+
2250 else if (isTrusted || !app_.getFeeTrack().isLoadedLocal())
+
2251 {
+
2252 std::string const name = isTrusted ? "ChkTrust" : "ChkUntrust";
+
2253
+
2254 std::weak_ptr<PeerImp> weak = shared_from_this();
+
2255 app_.getJobQueue().addJob(isTrusted ? jtVALIDATION_t : jtVALIDATION_ut, name, [weak, val, m, key]() {
+
2256 if (auto peer = weak.lock())
+
2257 peer->checkValidation(val, key, m);
+
2258 });
+
2259 }
+
2260 else
+
2261 {
+
2262 JLOG(p_journal_.debug()) << "Dropping untrusted validation for load";
+
2263 }
+
2264 }
+
2265 catch (std::exception const& e)
+
2266 {
+
2267 JLOG(p_journal_.warn()) << "Exception processing validation: " << e.what();
+
2268 using namespace std::string_literals;
+
2269 fee_.update(Resource::feeMalformedRequest, e.what());
+
2270 }
+
2271}
-
2273
-
2274void
-
- -
2276{
-
2277 protocol::TMGetObjectByHash& packet = *m;
-
2278
-
2279 JLOG(p_journal_.trace()) << "received TMGetObjectByHash " << packet.type() << " " << packet.objects_size();
-
2280
-
2281 if (packet.query())
-
2282 {
-
2283 // this is a query
-
2284 if (send_queue_.size() >= Tuning::dropSendQueue)
-
2285 {
-
2286 JLOG(p_journal_.debug()) << "GetObject: Large send queue";
-
2287 return;
-
2288 }
-
2289
-
2290 if (packet.type() == protocol::TMGetObjectByHash::otFETCH_PACK)
-
2291 {
-
2292 doFetchPack(m);
-
2293 return;
-
2294 }
-
2295
-
2296 if (packet.type() == protocol::TMGetObjectByHash::otTRANSACTIONS)
-
2297 {
-
2298 if (!txReduceRelayEnabled())
-
2299 {
-
2300 JLOG(p_journal_.error()) << "TMGetObjectByHash: tx reduce-relay is disabled";
-
2301 fee_.update(Resource::feeMalformedRequest, "disabled");
-
2302 return;
-
2303 }
-
2304
-
2305 std::weak_ptr<PeerImp> weak = shared_from_this();
-
2306 app_.getJobQueue().addJob(jtREQUESTED_TXN, "DoTxs", [weak, m]() {
-
2307 if (auto peer = weak.lock())
-
2308 peer->doTransactions(m);
-
2309 });
-
2310 return;
-
2311 }
-
2312
-
2313 protocol::TMGetObjectByHash reply;
-
2314
-
2315 reply.set_query(false);
-
2316
-
2317 if (packet.has_seq())
-
2318 reply.set_seq(packet.seq());
-
2319
-
2320 reply.set_type(packet.type());
-
2321
-
2322 if (packet.has_ledgerhash())
-
2323 {
-
2324 if (!stringIsUint256Sized(packet.ledgerhash()))
-
2325 {
-
2326 fee_.update(Resource::feeMalformedRequest, "ledger hash");
-
2327 return;
-
2328 }
-
2329
-
2330 reply.set_ledgerhash(packet.ledgerhash());
-
2331 }
-
2332
-
2333 fee_.update(Resource::feeModerateBurdenPeer, " received a get object by hash request");
-
2334
-
2335 // This is a very minimal implementation
-
2336 for (int i = 0; i < packet.objects_size(); ++i)
-
2337 {
-
2338 auto const& obj = packet.objects(i);
-
2339 if (obj.has_hash() && stringIsUint256Sized(obj.hash()))
-
2340 {
-
2341 uint256 const hash{obj.hash()};
-
2342 // VFALCO TODO Move this someplace more sensible so we dont
-
2343 // need to inject the NodeStore interfaces.
-
2344 std::uint32_t seq{obj.has_ledgerseq() ? obj.ledgerseq() : 0};
-
2345 auto nodeObject{app_.getNodeStore().fetchNodeObject(hash, seq)};
-
2346 if (nodeObject)
-
2347 {
-
2348 protocol::TMIndexedObject& newObj = *reply.add_objects();
-
2349 newObj.set_hash(hash.begin(), hash.size());
-
2350 newObj.set_data(&nodeObject->getData().front(), nodeObject->getData().size());
-
2351
-
2352 if (obj.has_nodeid())
-
2353 newObj.set_index(obj.nodeid());
-
2354 if (obj.has_ledgerseq())
-
2355 newObj.set_ledgerseq(obj.ledgerseq());
-
2356
-
2357 // VFALCO NOTE "seq" in the message is obsolete
-
2358
-
2359 // Check if by adding this object, reply has reached its
-
2360 // limit
-
2361 if (reply.objects_size() >= Tuning::hardMaxReplyNodes)
-
2362 {
-
2363 fee_.update(Resource::feeModerateBurdenPeer, " Reply limit reached. Truncating reply.");
-
2364 break;
-
2365 }
-
2366 }
-
2367 }
-
2368 }
-
2369
-
2370 JLOG(p_journal_.trace()) << "GetObj: " << reply.objects_size() << " of " << packet.objects_size();
-
2371 send(std::make_shared<Message>(reply, protocol::mtGET_OBJECTS));
-
2372 }
-
2373 else
-
2374 {
-
2375 // this is a reply
-
2376 std::uint32_t pLSeq = 0;
-
2377 bool pLDo = true;
-
2378 bool progress = false;
-
2379
-
2380 for (int i = 0; i < packet.objects_size(); ++i)
-
2381 {
-
2382 protocol::TMIndexedObject const& obj = packet.objects(i);
-
2383
-
2384 if (obj.has_hash() && stringIsUint256Sized(obj.hash()))
-
2385 {
-
2386 if (obj.has_ledgerseq())
-
2387 {
-
2388 if (obj.ledgerseq() != pLSeq)
-
2389 {
-
2390 if (pLDo && (pLSeq != 0))
-
2391 {
-
2392 JLOG(p_journal_.debug()) << "GetObj: Full fetch pack for " << pLSeq;
-
2393 }
-
2394 pLSeq = obj.ledgerseq();
-
2395 pLDo = !app_.getLedgerMaster().haveLedger(pLSeq);
-
2396
-
2397 if (!pLDo)
-
2398 {
-
2399 JLOG(p_journal_.debug()) << "GetObj: Late fetch pack for " << pLSeq;
-
2400 }
-
2401 else
-
2402 progress = true;
-
2403 }
-
2404 }
-
2405
-
2406 if (pLDo)
-
2407 {
-
2408 uint256 const hash{obj.hash()};
-
2409
-
2410 app_.getLedgerMaster().addFetchPack(
-
2411 hash, std::make_shared<Blob>(obj.data().begin(), obj.data().end()));
-
2412 }
-
2413 }
-
2414 }
-
2415
-
2416 if (pLDo && (pLSeq != 0))
-
2417 {
-
2418 JLOG(p_journal_.debug()) << "GetObj: Partial fetch pack for " << pLSeq;
-
2419 }
-
2420 if (packet.type() == protocol::TMGetObjectByHash::otFETCH_PACK)
-
2421 app_.getLedgerMaster().gotFetchPack(progress, pLSeq);
-
2422 }
-
2423}
+
2272
+
2273void
+
+ +
2275{
+
2276 protocol::TMGetObjectByHash& packet = *m;
+
2277
+
2278 JLOG(p_journal_.trace()) << "received TMGetObjectByHash " << packet.type() << " " << packet.objects_size();
+
2279
+
2280 if (packet.query())
+
2281 {
+
2282 // this is a query
+
2283 if (send_queue_.size() >= Tuning::dropSendQueue)
+
2284 {
+
2285 JLOG(p_journal_.debug()) << "GetObject: Large send queue";
+
2286 return;
+
2287 }
+
2288
+
2289 if (packet.type() == protocol::TMGetObjectByHash::otFETCH_PACK)
+
2290 {
+
2291 doFetchPack(m);
+
2292 return;
+
2293 }
+
2294
+
2295 if (packet.type() == protocol::TMGetObjectByHash::otTRANSACTIONS)
+
2296 {
+
2297 if (!txReduceRelayEnabled())
+
2298 {
+
2299 JLOG(p_journal_.error()) << "TMGetObjectByHash: tx reduce-relay is disabled";
+
2300 fee_.update(Resource::feeMalformedRequest, "disabled");
+
2301 return;
+
2302 }
+
2303
+
2304 std::weak_ptr<PeerImp> weak = shared_from_this();
+
2305 app_.getJobQueue().addJob(jtREQUESTED_TXN, "DoTxs", [weak, m]() {
+
2306 if (auto peer = weak.lock())
+
2307 peer->doTransactions(m);
+
2308 });
+
2309 return;
+
2310 }
+
2311
+
2312 protocol::TMGetObjectByHash reply;
+
2313
+
2314 reply.set_query(false);
+
2315
+
2316 if (packet.has_seq())
+
2317 reply.set_seq(packet.seq());
+
2318
+
2319 reply.set_type(packet.type());
+
2320
+
2321 if (packet.has_ledgerhash())
+
2322 {
+
2323 if (!stringIsUint256Sized(packet.ledgerhash()))
+
2324 {
+
2325 fee_.update(Resource::feeMalformedRequest, "ledger hash");
+
2326 return;
+
2327 }
+
2328
+
2329 reply.set_ledgerhash(packet.ledgerhash());
+
2330 }
+
2331
+
2332 fee_.update(Resource::feeModerateBurdenPeer, " received a get object by hash request");
+
2333
+
2334 // This is a very minimal implementation
+
2335 for (int i = 0; i < packet.objects_size(); ++i)
+
2336 {
+
2337 auto const& obj = packet.objects(i);
+
2338 if (obj.has_hash() && stringIsUint256Sized(obj.hash()))
+
2339 {
+
2340 uint256 const hash{obj.hash()};
+
2341 // VFALCO TODO Move this someplace more sensible so we dont
+
2342 // need to inject the NodeStore interfaces.
+
2343 std::uint32_t seq{obj.has_ledgerseq() ? obj.ledgerseq() : 0};
+
2344 auto nodeObject{app_.getNodeStore().fetchNodeObject(hash, seq)};
+
2345 if (nodeObject)
+
2346 {
+
2347 protocol::TMIndexedObject& newObj = *reply.add_objects();
+
2348 newObj.set_hash(hash.begin(), hash.size());
+
2349 newObj.set_data(&nodeObject->getData().front(), nodeObject->getData().size());
+
2350
+
2351 if (obj.has_nodeid())
+
2352 newObj.set_index(obj.nodeid());
+
2353 if (obj.has_ledgerseq())
+
2354 newObj.set_ledgerseq(obj.ledgerseq());
+
2355
+
2356 // VFALCO NOTE "seq" in the message is obsolete
+
2357
+
2358 // Check if by adding this object, reply has reached its
+
2359 // limit
+
2360 if (reply.objects_size() >= Tuning::hardMaxReplyNodes)
+
2361 {
+
2362 fee_.update(Resource::feeModerateBurdenPeer, " Reply limit reached. Truncating reply.");
+
2363 break;
+
2364 }
+
2365 }
+
2366 }
+
2367 }
+
2368
+
2369 JLOG(p_journal_.trace()) << "GetObj: " << reply.objects_size() << " of " << packet.objects_size();
+
2370 send(std::make_shared<Message>(reply, protocol::mtGET_OBJECTS));
+
2371 }
+
2372 else
+
2373 {
+
2374 // this is a reply
+
2375 std::uint32_t pLSeq = 0;
+
2376 bool pLDo = true;
+
2377 bool progress = false;
+
2378
+
2379 for (int i = 0; i < packet.objects_size(); ++i)
+
2380 {
+
2381 protocol::TMIndexedObject const& obj = packet.objects(i);
+
2382
+
2383 if (obj.has_hash() && stringIsUint256Sized(obj.hash()))
+
2384 {
+
2385 if (obj.has_ledgerseq())
+
2386 {
+
2387 if (obj.ledgerseq() != pLSeq)
+
2388 {
+
2389 if (pLDo && (pLSeq != 0))
+
2390 {
+
2391 JLOG(p_journal_.debug()) << "GetObj: Full fetch pack for " << pLSeq;
+
2392 }
+
2393 pLSeq = obj.ledgerseq();
+
2394 pLDo = !app_.getLedgerMaster().haveLedger(pLSeq);
+
2395
+
2396 if (!pLDo)
+
2397 {
+
2398 JLOG(p_journal_.debug()) << "GetObj: Late fetch pack for " << pLSeq;
+
2399 }
+
2400 else
+
2401 progress = true;
+
2402 }
+
2403 }
+
2404
+
2405 if (pLDo)
+
2406 {
+
2407 uint256 const hash{obj.hash()};
+
2408
+
2409 app_.getLedgerMaster().addFetchPack(
+
2410 hash, std::make_shared<Blob>(obj.data().begin(), obj.data().end()));
+
2411 }
+
2412 }
+
2413 }
+
2414
+
2415 if (pLDo && (pLSeq != 0))
+
2416 {
+
2417 JLOG(p_journal_.debug()) << "GetObj: Partial fetch pack for " << pLSeq;
+
2418 }
+
2419 if (packet.type() == protocol::TMGetObjectByHash::otFETCH_PACK)
+
2420 app_.getLedgerMaster().gotFetchPack(progress, pLSeq);
+
2421 }
+
2422}
-
2424
-
2425void
-
- -
2427{
-
2428 if (!txReduceRelayEnabled())
-
2429 {
-
2430 JLOG(p_journal_.error()) << "TMHaveTransactions: tx reduce-relay is disabled";
-
2431 fee_.update(Resource::feeMalformedRequest, "disabled");
-
2432 return;
-
2433 }
-
2434
-
2435 std::weak_ptr<PeerImp> weak = shared_from_this();
-
2436 app_.getJobQueue().addJob(jtMISSING_TXN, "HandleHaveTxs", [weak, m]() {
-
2437 if (auto peer = weak.lock())
-
2438 peer->handleHaveTransactions(m);
-
2439 });
-
2440}
+
2423
+
2424void
+
+ +
2426{
+
2427 if (!txReduceRelayEnabled())
+
2428 {
+
2429 JLOG(p_journal_.error()) << "TMHaveTransactions: tx reduce-relay is disabled";
+
2430 fee_.update(Resource::feeMalformedRequest, "disabled");
+
2431 return;
+
2432 }
+
2433
+
2434 std::weak_ptr<PeerImp> weak = shared_from_this();
+
2435 app_.getJobQueue().addJob(jtMISSING_TXN, "HandleHaveTxs", [weak, m]() {
+
2436 if (auto peer = weak.lock())
+
2437 peer->handleHaveTransactions(m);
+
2438 });
+
2439}
-
2441
-
2442void
-
-
2443PeerImp::handleHaveTransactions(std::shared_ptr<protocol::TMHaveTransactions> const& m)
-
2444{
-
2445 protocol::TMGetObjectByHash tmBH;
-
2446 tmBH.set_type(protocol::TMGetObjectByHash_ObjectType_otTRANSACTIONS);
-
2447 tmBH.set_query(true);
-
2448
-
2449 JLOG(p_journal_.trace()) << "received TMHaveTransactions " << m->hashes_size();
-
2450
-
2451 for (std::uint32_t i = 0; i < m->hashes_size(); i++)
-
2452 {
-
2453 if (!stringIsUint256Sized(m->hashes(i)))
-
2454 {
-
2455 JLOG(p_journal_.error()) << "TMHaveTransactions with invalid hash size";
-
2456 fee_.update(Resource::feeMalformedRequest, "hash size");
-
2457 return;
-
2458 }
-
2459
-
2460 uint256 hash(m->hashes(i));
-
2461
-
2462 auto txn = app_.getMasterTransaction().fetch_from_cache(hash);
-
2463
-
2464 JLOG(p_journal_.trace()) << "checking transaction " << (bool)txn;
-
2465
-
2466 if (!txn)
-
2467 {
-
2468 JLOG(p_journal_.debug()) << "adding transaction to request";
-
2469
-
2470 auto obj = tmBH.add_objects();
-
2471 obj->set_hash(hash.data(), hash.size());
-
2472 }
-
2473 else
-
2474 {
-
2475 // Erase only if a peer has seen this tx. If the peer has not
-
2476 // seen this tx then the tx could not has been queued for this
-
2477 // peer.
-
2478 removeTxQueue(hash);
-
2479 }
-
2480 }
-
2481
-
2482 JLOG(p_journal_.trace()) << "transaction request object is " << tmBH.objects_size();
-
2483
-
2484 if (tmBH.objects_size() > 0)
-
2485 send(std::make_shared<Message>(tmBH, protocol::mtGET_OBJECTS));
-
2486}
+
2440
+
2441void
+
+
2442PeerImp::handleHaveTransactions(std::shared_ptr<protocol::TMHaveTransactions> const& m)
+
2443{
+
2444 protocol::TMGetObjectByHash tmBH;
+
2445 tmBH.set_type(protocol::TMGetObjectByHash_ObjectType_otTRANSACTIONS);
+
2446 tmBH.set_query(true);
+
2447
+
2448 JLOG(p_journal_.trace()) << "received TMHaveTransactions " << m->hashes_size();
+
2449
+
2450 for (std::uint32_t i = 0; i < m->hashes_size(); i++)
+
2451 {
+
2452 if (!stringIsUint256Sized(m->hashes(i)))
+
2453 {
+
2454 JLOG(p_journal_.error()) << "TMHaveTransactions with invalid hash size";
+
2455 fee_.update(Resource::feeMalformedRequest, "hash size");
+
2456 return;
+
2457 }
+
2458
+
2459 uint256 hash(m->hashes(i));
+
2460
+
2461 auto txn = app_.getMasterTransaction().fetch_from_cache(hash);
+
2462
+
2463 JLOG(p_journal_.trace()) << "checking transaction " << (bool)txn;
+
2464
+
2465 if (!txn)
+
2466 {
+
2467 JLOG(p_journal_.debug()) << "adding transaction to request";
+
2468
+
2469 auto obj = tmBH.add_objects();
+
2470 obj->set_hash(hash.data(), hash.size());
+
2471 }
+
2472 else
+
2473 {
+
2474 // Erase only if a peer has seen this tx. If the peer has not
+
2475 // seen this tx then the tx could not has been queued for this
+
2476 // peer.
+
2477 removeTxQueue(hash);
+
2478 }
+
2479 }
+
2480
+
2481 JLOG(p_journal_.trace()) << "transaction request object is " << tmBH.objects_size();
+
2482
+
2483 if (tmBH.objects_size() > 0)
+
2484 send(std::make_shared<Message>(tmBH, protocol::mtGET_OBJECTS));
+
2485}
-
2487
-
2488void
-
- -
2490{
-
2491 if (!txReduceRelayEnabled())
-
2492 {
-
2493 JLOG(p_journal_.error()) << "TMTransactions: tx reduce-relay is disabled";
-
2494 fee_.update(Resource::feeMalformedRequest, "disabled");
-
2495 return;
-
2496 }
-
2497
-
2498 JLOG(p_journal_.trace()) << "received TMTransactions " << m->transactions_size();
-
2499
-
2500 overlay_.addTxMetrics(m->transactions_size());
-
2501
-
2502 for (std::uint32_t i = 0; i < m->transactions_size(); ++i)
-
2503 handleTransaction(
-
2504 std::shared_ptr<protocol::TMTransaction>(m->mutable_transactions(i), [](protocol::TMTransaction*) {}),
-
2505 false,
-
2506 true);
-
2507}
+
2486
+
2487void
+
+ +
2489{
+
2490 if (!txReduceRelayEnabled())
+
2491 {
+
2492 JLOG(p_journal_.error()) << "TMTransactions: tx reduce-relay is disabled";
+
2493 fee_.update(Resource::feeMalformedRequest, "disabled");
+
2494 return;
+
2495 }
+
2496
+
2497 JLOG(p_journal_.trace()) << "received TMTransactions " << m->transactions_size();
+
2498
+
2499 overlay_.addTxMetrics(m->transactions_size());
+
2500
+
2501 for (std::uint32_t i = 0; i < m->transactions_size(); ++i)
+
2502 handleTransaction(
+
2503 std::shared_ptr<protocol::TMTransaction>(m->mutable_transactions(i), [](protocol::TMTransaction*) {}),
+
2504 false,
+
2505 true);
+
2506}
-
2508
-
2509void
-
-
2510PeerImp::onMessage(std::shared_ptr<protocol::TMSquelch> const& m)
-
2511{
-
2512 using on_message_fn = void (PeerImp::*)(std::shared_ptr<protocol::TMSquelch> const&);
-
2513 if (!strand_.running_in_this_thread())
-
2514 return post(strand_, std::bind((on_message_fn)&PeerImp::onMessage, shared_from_this(), m));
-
2515
-
2516 if (!m->has_validatorpubkey())
-
2517 {
-
2518 fee_.update(Resource::feeInvalidData, "squelch no pubkey");
-
2519 return;
-
2520 }
-
2521 auto validator = m->validatorpubkey();
-
2522 auto const slice{makeSlice(validator)};
-
2523 if (!publicKeyType(slice))
-
2524 {
-
2525 fee_.update(Resource::feeInvalidData, "squelch bad pubkey");
-
2526 return;
-
2527 }
-
2528 PublicKey key(slice);
-
2529
-
2530 // Ignore the squelch for validator's own messages.
-
2531 if (key == app_.getValidationPublicKey())
-
2532 {
-
2533 JLOG(p_journal_.debug()) << "onMessage: TMSquelch discarding validator's squelch " << slice;
-
2534 return;
-
2535 }
-
2536
-
2537 std::uint32_t duration = m->has_squelchduration() ? m->squelchduration() : 0;
-
2538 if (!m->squelch())
-
2539 squelch_.removeSquelch(key);
-
2540 else if (!squelch_.addSquelch(key, std::chrono::seconds{duration}))
-
2541 fee_.update(Resource::feeInvalidData, "squelch duration");
-
2542
-
2543 JLOG(p_journal_.debug()) << "onMessage: TMSquelch " << slice << " " << id() << " " << duration;
-
2544}
+
2507
+
2508void
+
+
2509PeerImp::onMessage(std::shared_ptr<protocol::TMSquelch> const& m)
+
2510{
+
2511 using on_message_fn = void (PeerImp::*)(std::shared_ptr<protocol::TMSquelch> const&);
+
2512 if (!strand_.running_in_this_thread())
+
2513 return post(strand_, std::bind((on_message_fn)&PeerImp::onMessage, shared_from_this(), m));
+
2514
+
2515 if (!m->has_validatorpubkey())
+
2516 {
+
2517 fee_.update(Resource::feeInvalidData, "squelch no pubkey");
+
2518 return;
+
2519 }
+
2520 auto validator = m->validatorpubkey();
+
2521 auto const slice{makeSlice(validator)};
+
2522 if (!publicKeyType(slice))
+
2523 {
+
2524 fee_.update(Resource::feeInvalidData, "squelch bad pubkey");
+
2525 return;
+
2526 }
+
2527 PublicKey key(slice);
+
2528
+
2529 // Ignore the squelch for validator's own messages.
+
2530 if (key == app_.getValidationPublicKey())
+
2531 {
+
2532 JLOG(p_journal_.debug()) << "onMessage: TMSquelch discarding validator's squelch " << slice;
+
2533 return;
+
2534 }
+
2535
+
2536 std::uint32_t duration = m->has_squelchduration() ? m->squelchduration() : 0;
+
2537 if (!m->squelch())
+
2538 squelch_.removeSquelch(key);
+
2539 else if (!squelch_.addSquelch(key, std::chrono::seconds{duration}))
+
2540 fee_.update(Resource::feeInvalidData, "squelch duration");
+
2541
+
2542 JLOG(p_journal_.debug()) << "onMessage: TMSquelch " << slice << " " << id() << " " << duration;
+
2543}
-
2545
-
2546//--------------------------------------------------------------------------
-
2547
-
2548void
-
-
2549PeerImp::addLedger(uint256 const& hash, std::lock_guard<std::mutex> const& lockedRecentLock)
-
2550{
-
2551 // lockedRecentLock is passed as a reminder that recentLock_ must be
-
2552 // locked by the caller.
-
2553 (void)lockedRecentLock;
-
2554
-
2555 if (std::find(recentLedgers_.begin(), recentLedgers_.end(), hash) != recentLedgers_.end())
-
2556 return;
-
2557
-
2558 recentLedgers_.push_back(hash);
-
2559}
+
2544
+
2545//--------------------------------------------------------------------------
+
2546
+
2547void
+
+
2548PeerImp::addLedger(uint256 const& hash, std::lock_guard<std::mutex> const& lockedRecentLock)
+
2549{
+
2550 // lockedRecentLock is passed as a reminder that recentLock_ must be
+
2551 // locked by the caller.
+
2552 (void)lockedRecentLock;
+
2553
+
2554 if (std::find(recentLedgers_.begin(), recentLedgers_.end(), hash) != recentLedgers_.end())
+
2555 return;
+
2556
+
2557 recentLedgers_.push_back(hash);
+
2558}
-
2560
-
2561void
-
-
2562PeerImp::doFetchPack(std::shared_ptr<protocol::TMGetObjectByHash> const& packet)
-
2563{
-
2564 // VFALCO TODO Invert this dependency using an observer and shared state
-
2565 // object. Don't queue fetch pack jobs if we're under load or we already
-
2566 // have some queued.
-
2567 if (app_.getFeeTrack().isLoadedLocal() || (app_.getLedgerMaster().getValidatedLedgerAge() > 40s) ||
-
2568 (app_.getJobQueue().getJobCount(jtPACK) > 10))
-
2569 {
-
2570 JLOG(p_journal_.info()) << "Too busy to make fetch pack";
-
2571 return;
-
2572 }
-
2573
-
2574 if (!stringIsUint256Sized(packet->ledgerhash()))
-
2575 {
-
2576 JLOG(p_journal_.warn()) << "FetchPack hash size malformed";
-
2577 fee_.update(Resource::feeMalformedRequest, "hash size");
-
2578 return;
-
2579 }
-
2580
-
2581 fee_.fee = Resource::feeHeavyBurdenPeer;
-
2582
-
2583 uint256 const hash{packet->ledgerhash()};
-
2584
-
2585 std::weak_ptr<PeerImp> weak = shared_from_this();
-
2586 auto elapsed = UptimeClock::now();
-
2587 auto const pap = &app_;
-
2588 app_.getJobQueue().addJob(jtPACK, "MakeFetchPack", [pap, weak, packet, hash, elapsed]() {
-
2589 pap->getLedgerMaster().makeFetchPack(weak, packet, hash, elapsed);
-
2590 });
-
2591}
+
2559
+
2560void
+
+
2561PeerImp::doFetchPack(std::shared_ptr<protocol::TMGetObjectByHash> const& packet)
+
2562{
+
2563 // VFALCO TODO Invert this dependency using an observer and shared state
+
2564 // object. Don't queue fetch pack jobs if we're under load or we already
+
2565 // have some queued.
+
2566 if (app_.getFeeTrack().isLoadedLocal() || (app_.getLedgerMaster().getValidatedLedgerAge() > 40s) ||
+
2567 (app_.getJobQueue().getJobCount(jtPACK) > 10))
+
2568 {
+
2569 JLOG(p_journal_.info()) << "Too busy to make fetch pack";
+
2570 return;
+
2571 }
+
2572
+
2573 if (!stringIsUint256Sized(packet->ledgerhash()))
+
2574 {
+
2575 JLOG(p_journal_.warn()) << "FetchPack hash size malformed";
+
2576 fee_.update(Resource::feeMalformedRequest, "hash size");
+
2577 return;
+
2578 }
+
2579
+
2580 fee_.fee = Resource::feeHeavyBurdenPeer;
+
2581
+
2582 uint256 const hash{packet->ledgerhash()};
+
2583
+
2584 std::weak_ptr<PeerImp> weak = shared_from_this();
+
2585 auto elapsed = UptimeClock::now();
+
2586 auto const pap = &app_;
+
2587 app_.getJobQueue().addJob(jtPACK, "MakeFetchPack", [pap, weak, packet, hash, elapsed]() {
+
2588 pap->getLedgerMaster().makeFetchPack(weak, packet, hash, elapsed);
+
2589 });
+
2590}
-
2592
-
2593void
-
-
2594PeerImp::doTransactions(std::shared_ptr<protocol::TMGetObjectByHash> const& packet)
-
2595{
-
2596 protocol::TMTransactions reply;
-
2597
-
2598 JLOG(p_journal_.trace()) << "received TMGetObjectByHash requesting tx " << packet->objects_size();
-
2599
-
2600 if (packet->objects_size() > reduce_relay::MAX_TX_QUEUE_SIZE)
-
2601 {
-
2602 JLOG(p_journal_.error()) << "doTransactions, invalid number of hashes";
-
2603 fee_.update(Resource::feeMalformedRequest, "too big");
-
2604 return;
-
2605 }
-
2606
-
2607 for (std::uint32_t i = 0; i < packet->objects_size(); ++i)
-
2608 {
-
2609 auto const& obj = packet->objects(i);
-
2610
-
2611 if (!stringIsUint256Sized(obj.hash()))
-
2612 {
-
2613 fee_.update(Resource::feeMalformedRequest, "hash size");
-
2614 return;
-
2615 }
-
2616
-
2617 uint256 hash(obj.hash());
-
2618
-
2619 auto txn = app_.getMasterTransaction().fetch_from_cache(hash);
-
2620
-
2621 if (!txn)
-
2622 {
-
2623 JLOG(p_journal_.error()) << "doTransactions, transaction not found " << Slice(hash.data(), hash.size());
-
2624 fee_.update(Resource::feeMalformedRequest, "tx not found");
-
2625 return;
-
2626 }
-
2627
-
2628 Serializer s;
-
2629 auto tx = reply.add_transactions();
-
2630 auto sttx = txn->getSTransaction();
-
2631 sttx->add(s);
-
2632 tx->set_rawtransaction(s.data(), s.size());
-
2633 tx->set_status(txn->getStatus() == INCLUDED ? protocol::tsCURRENT : protocol::tsNEW);
-
2634 tx->set_receivetimestamp(app_.timeKeeper().now().time_since_epoch().count());
-
2635 tx->set_deferred(txn->getSubmitResult().queued);
-
2636 }
-
2637
-
2638 if (reply.transactions_size() > 0)
-
2639 send(std::make_shared<Message>(reply, protocol::mtTRANSACTIONS));
-
2640}
+
2591
+
2592void
+
+
2593PeerImp::doTransactions(std::shared_ptr<protocol::TMGetObjectByHash> const& packet)
+
2594{
+
2595 protocol::TMTransactions reply;
+
2596
+
2597 JLOG(p_journal_.trace()) << "received TMGetObjectByHash requesting tx " << packet->objects_size();
+
2598
+
2599 if (packet->objects_size() > reduce_relay::MAX_TX_QUEUE_SIZE)
+
2600 {
+
2601 JLOG(p_journal_.error()) << "doTransactions, invalid number of hashes";
+
2602 fee_.update(Resource::feeMalformedRequest, "too big");
+
2603 return;
+
2604 }
+
2605
+
2606 for (std::uint32_t i = 0; i < packet->objects_size(); ++i)
+
2607 {
+
2608 auto const& obj = packet->objects(i);
+
2609
+
2610 if (!stringIsUint256Sized(obj.hash()))
+
2611 {
+
2612 fee_.update(Resource::feeMalformedRequest, "hash size");
+
2613 return;
+
2614 }
+
2615
+
2616 uint256 hash(obj.hash());
+
2617
+
2618 auto txn = app_.getMasterTransaction().fetch_from_cache(hash);
+
2619
+
2620 if (!txn)
+
2621 {
+
2622 JLOG(p_journal_.error()) << "doTransactions, transaction not found " << Slice(hash.data(), hash.size());
+
2623 fee_.update(Resource::feeMalformedRequest, "tx not found");
+
2624 return;
+
2625 }
+
2626
+
2627 Serializer s;
+
2628 auto tx = reply.add_transactions();
+
2629 auto sttx = txn->getSTransaction();
+
2630 sttx->add(s);
+
2631 tx->set_rawtransaction(s.data(), s.size());
+
2632 tx->set_status(txn->getStatus() == INCLUDED ? protocol::tsCURRENT : protocol::tsNEW);
+
2633 tx->set_receivetimestamp(app_.timeKeeper().now().time_since_epoch().count());
+
2634 tx->set_deferred(txn->getSubmitResult().queued);
+
2635 }
+
2636
+
2637 if (reply.transactions_size() > 0)
+
2638 send(std::make_shared<Message>(reply, protocol::mtTRANSACTIONS));
+
2639}
-
2641
-
2642void
-
-
2643PeerImp::checkTransaction(
-
2644 HashRouterFlags flags,
-
2645 bool checkSignature,
-
2646 std::shared_ptr<STTx const> const& stx,
-
2647 bool batch)
-
2648{
-
2649 // VFALCO TODO Rewrite to not use exceptions
-
2650 try
-
2651 {
-
2652 // charge strongly for relaying batch txns
-
2653 // LCOV_EXCL_START
-
2654 /*
-
2655 There is no need to check whether the featureBatch amendment is
-
2656 enabled.
-
2657
-
2658 * If the `tfInnerBatchTxn` flag is set, and the amendment is
-
2659 enabled, then it's an invalid transaction because inner batch
-
2660 transactions should not be relayed.
-
2661 * If the `tfInnerBatchTxn` flag is set, and the amendment is *not*
-
2662 enabled, then the transaction is malformed because it's using an
-
2663 "unknown" flag. There's no need to waste the resources to send it
-
2664 to the transaction engine.
-
2665
-
2666 We don't normally check transaction validity at this level, but
-
2667 since we _need_ to check it when the amendment is enabled, we may as
-
2668 well drop it if the flag is set regardless.
-
2669 */
-
2670 if (stx->isFlag(tfInnerBatchTxn))
-
2671 {
-
2672 JLOG(p_journal_.warn()) << "Ignoring Network relayed Tx containing "
-
2673 "tfInnerBatchTxn (checkSignature).";
-
2674 charge(Resource::feeModerateBurdenPeer, "inner batch txn");
-
2675 return;
-
2676 }
-
2677 // LCOV_EXCL_STOP
-
2678
-
2679 // Expired?
-
2680 if (stx->isFieldPresent(sfLastLedgerSequence) &&
-
2681 (stx->getFieldU32(sfLastLedgerSequence) < app_.getLedgerMaster().getValidLedgerIndex()))
-
2682 {
-
2683 JLOG(p_journal_.info()) << "Marking transaction " << stx->getTransactionID()
-
2684 << "as BAD because it's expired";
-
2685 app_.getHashRouter().setFlags(stx->getTransactionID(), HashRouterFlags::BAD);
-
2686 charge(Resource::feeUselessData, "expired tx");
-
2687 return;
-
2688 }
-
2689
-
2690 if (isPseudoTx(*stx))
-
2691 {
-
2692 // Don't do anything with pseudo transactions except put them in the
-
2693 // TransactionMaster cache
-
2694 std::string reason;
-
2695 auto tx = std::make_shared<Transaction>(stx, reason, app_);
-
2696 XRPL_ASSERT(
-
2697 tx->getStatus() == NEW,
-
2698 "xrpl::PeerImp::checkTransaction Transaction created "
-
2699 "correctly");
-
2700 if (tx->getStatus() == NEW)
-
2701 {
-
2702 JLOG(p_journal_.debug()) << "Processing " << (batch ? "batch" : "unsolicited")
-
2703 << " pseudo-transaction tx " << tx->getID();
-
2704
-
2705 app_.getMasterTransaction().canonicalize(&tx);
-
2706 // Tell the overlay about it, but don't relay it.
-
2707 auto const toSkip = app_.getHashRouter().shouldRelay(tx->getID());
-
2708 if (toSkip)
-
2709 {
-
2710 JLOG(p_journal_.debug()) << "Passing skipped pseudo pseudo-transaction tx " << tx->getID();
-
2711 app_.overlay().relay(tx->getID(), {}, *toSkip);
-
2712 }
-
2713 if (!batch)
-
2714 {
-
2715 JLOG(p_journal_.debug()) << "Charging for pseudo-transaction tx " << tx->getID();
-
2716 charge(Resource::feeUselessData, "pseudo tx");
-
2717 }
-
2718
-
2719 return;
-
2720 }
-
2721 }
-
2722
-
2723 if (checkSignature)
-
2724 {
-
2725 // Check the signature before handing off to the job queue.
-
2726 if (auto [valid, validReason] = checkValidity(
-
2727 app_.getHashRouter(), *stx, app_.getLedgerMaster().getValidatedRules(), app_.config());
-
2728 valid != Validity::Valid)
-
2729 {
-
2730 if (!validReason.empty())
-
2731 {
-
2732 JLOG(p_journal_.debug()) << "Exception checking transaction: " << validReason;
-
2733 }
-
2734
-
2735 // Probably not necessary to set HashRouterFlags::BAD, but
-
2736 // doesn't hurt.
-
2737 app_.getHashRouter().setFlags(stx->getTransactionID(), HashRouterFlags::BAD);
-
2738 charge(Resource::feeInvalidSignature, "check transaction signature failure");
-
2739 return;
-
2740 }
-
2741 }
-
2742 else
-
2743 {
-
2744 forceValidity(app_.getHashRouter(), stx->getTransactionID(), Validity::Valid);
-
2745 }
-
2746
-
2747 std::string reason;
-
2748 auto tx = std::make_shared<Transaction>(stx, reason, app_);
-
2749
-
2750 if (tx->getStatus() == INVALID)
-
2751 {
-
2752 if (!reason.empty())
-
2753 {
-
2754 JLOG(p_journal_.debug()) << "Exception checking transaction: " << reason;
-
2755 }
-
2756 app_.getHashRouter().setFlags(stx->getTransactionID(), HashRouterFlags::BAD);
-
2757 charge(Resource::feeInvalidSignature, "tx (impossible)");
-
2758 return;
-
2759 }
-
2760
-
2761 bool const trusted = any(flags & HashRouterFlags::TRUSTED);
-
2762 app_.getOPs().processTransaction(tx, trusted, false, NetworkOPs::FailHard::no);
-
2763 }
-
2764 catch (std::exception const& ex)
-
2765 {
-
2766 JLOG(p_journal_.warn()) << "Exception in " << __func__ << ": " << ex.what();
-
2767 app_.getHashRouter().setFlags(stx->getTransactionID(), HashRouterFlags::BAD);
-
2768 using namespace std::string_literals;
-
2769 charge(Resource::feeInvalidData, "tx "s + ex.what());
-
2770 }
-
2771}
+
2640
+
2641void
+
+
2642PeerImp::checkTransaction(
+
2643 HashRouterFlags flags,
+
2644 bool checkSignature,
+
2645 std::shared_ptr<STTx const> const& stx,
+
2646 bool batch)
+
2647{
+
2648 // VFALCO TODO Rewrite to not use exceptions
+
2649 try
+
2650 {
+
2651 // charge strongly for relaying batch txns
+
2652 // LCOV_EXCL_START
+
2653 /*
+
2654 There is no need to check whether the featureBatch amendment is
+
2655 enabled.
+
2656
+
2657 * If the `tfInnerBatchTxn` flag is set, and the amendment is
+
2658 enabled, then it's an invalid transaction because inner batch
+
2659 transactions should not be relayed.
+
2660 * If the `tfInnerBatchTxn` flag is set, and the amendment is *not*
+
2661 enabled, then the transaction is malformed because it's using an
+
2662 "unknown" flag. There's no need to waste the resources to send it
+
2663 to the transaction engine.
+
2664
+
2665 We don't normally check transaction validity at this level, but
+
2666 since we _need_ to check it when the amendment is enabled, we may as
+
2667 well drop it if the flag is set regardless.
+
2668 */
+
2669 if (stx->isFlag(tfInnerBatchTxn))
+
2670 {
+
2671 JLOG(p_journal_.warn()) << "Ignoring Network relayed Tx containing "
+
2672 "tfInnerBatchTxn (checkSignature).";
+
2673 charge(Resource::feeModerateBurdenPeer, "inner batch txn");
+
2674 return;
+
2675 }
+
2676 // LCOV_EXCL_STOP
+
2677
+
2678 // Expired?
+
2679 if (stx->isFieldPresent(sfLastLedgerSequence) &&
+
2680 (stx->getFieldU32(sfLastLedgerSequence) < app_.getLedgerMaster().getValidLedgerIndex()))
+
2681 {
+
2682 JLOG(p_journal_.info()) << "Marking transaction " << stx->getTransactionID()
+
2683 << "as BAD because it's expired";
+
2684 app_.getHashRouter().setFlags(stx->getTransactionID(), HashRouterFlags::BAD);
+
2685 charge(Resource::feeUselessData, "expired tx");
+
2686 return;
+
2687 }
+
2688
+
2689 if (isPseudoTx(*stx))
+
2690 {
+
2691 // Don't do anything with pseudo transactions except put them in the
+
2692 // TransactionMaster cache
+
2693 std::string reason;
+
2694 auto tx = std::make_shared<Transaction>(stx, reason, app_);
+
2695 XRPL_ASSERT(
+
2696 tx->getStatus() == NEW,
+
2697 "xrpl::PeerImp::checkTransaction Transaction created "
+
2698 "correctly");
+
2699 if (tx->getStatus() == NEW)
+
2700 {
+
2701 JLOG(p_journal_.debug()) << "Processing " << (batch ? "batch" : "unsolicited")
+
2702 << " pseudo-transaction tx " << tx->getID();
+
2703
+
2704 app_.getMasterTransaction().canonicalize(&tx);
+
2705 // Tell the overlay about it, but don't relay it.
+
2706 auto const toSkip = app_.getHashRouter().shouldRelay(tx->getID());
+
2707 if (toSkip)
+
2708 {
+
2709 JLOG(p_journal_.debug()) << "Passing skipped pseudo pseudo-transaction tx " << tx->getID();
+
2710 app_.overlay().relay(tx->getID(), {}, *toSkip);
+
2711 }
+
2712 if (!batch)
+
2713 {
+
2714 JLOG(p_journal_.debug()) << "Charging for pseudo-transaction tx " << tx->getID();
+
2715 charge(Resource::feeUselessData, "pseudo tx");
+
2716 }
+
2717
+
2718 return;
+
2719 }
+
2720 }
+
2721
+
2722 if (checkSignature)
+
2723 {
+
2724 // Check the signature before handing off to the job queue.
+
2725 if (auto [valid, validReason] = checkValidity(
+
2726 app_.getHashRouter(), *stx, app_.getLedgerMaster().getValidatedRules(), app_.config());
+
2727 valid != Validity::Valid)
+
2728 {
+
2729 if (!validReason.empty())
+
2730 {
+
2731 JLOG(p_journal_.debug()) << "Exception checking transaction: " << validReason;
+
2732 }
+
2733
+
2734 // Probably not necessary to set HashRouterFlags::BAD, but
+
2735 // doesn't hurt.
+
2736 app_.getHashRouter().setFlags(stx->getTransactionID(), HashRouterFlags::BAD);
+
2737 charge(Resource::feeInvalidSignature, "check transaction signature failure");
+
2738 return;
+
2739 }
+
2740 }
+
2741 else
+
2742 {
+
2743 forceValidity(app_.getHashRouter(), stx->getTransactionID(), Validity::Valid);
+
2744 }
+
2745
+
2746 std::string reason;
+
2747 auto tx = std::make_shared<Transaction>(stx, reason, app_);
+
2748
+
2749 if (tx->getStatus() == INVALID)
+
2750 {
+
2751 if (!reason.empty())
+
2752 {
+
2753 JLOG(p_journal_.debug()) << "Exception checking transaction: " << reason;
+
2754 }
+
2755 app_.getHashRouter().setFlags(stx->getTransactionID(), HashRouterFlags::BAD);
+
2756 charge(Resource::feeInvalidSignature, "tx (impossible)");
+
2757 return;
+
2758 }
+
2759
+
2760 bool const trusted = any(flags & HashRouterFlags::TRUSTED);
+
2761 app_.getOPs().processTransaction(tx, trusted, false, NetworkOPs::FailHard::no);
+
2762 }
+
2763 catch (std::exception const& ex)
+
2764 {
+
2765 JLOG(p_journal_.warn()) << "Exception in " << __func__ << ": " << ex.what();
+
2766 app_.getHashRouter().setFlags(stx->getTransactionID(), HashRouterFlags::BAD);
+
2767 using namespace std::string_literals;
+
2768 charge(Resource::feeInvalidData, "tx "s + ex.what());
+
2769 }
+
2770}
-
2772
-
2773// Called from our JobQueue
-
2774void
-
-
2775PeerImp::checkPropose(bool isTrusted, std::shared_ptr<protocol::TMProposeSet> const& packet, RCLCxPeerPos peerPos)
-
2776{
-
2777 JLOG(p_journal_.trace()) << "Checking " << (isTrusted ? "trusted" : "UNTRUSTED") << " proposal";
-
2778
-
2779 XRPL_ASSERT(packet, "xrpl::PeerImp::checkPropose : non-null packet");
-
2780
-
2781 if (!cluster() && !peerPos.checkSign())
-
2782 {
-
2783 std::string desc{"Proposal fails sig check"};
-
2784 JLOG(p_journal_.warn()) << desc;
-
2785 charge(Resource::feeInvalidSignature, desc);
-
2786 return;
-
2787 }
-
2788
-
2789 bool relay;
-
2790
-
2791 if (isTrusted)
-
2792 relay = app_.getOPs().processTrustedProposal(peerPos);
-
2793 else
-
2794 relay = app_.config().RELAY_UNTRUSTED_PROPOSALS == 1 || cluster();
-
2795
-
2796 if (relay)
-
2797 {
-
2798 // haveMessage contains peers, which are suppressed; i.e. the peers
-
2799 // are the source of the message, consequently the message should
-
2800 // not be relayed to these peers. But the message must be counted
-
2801 // as part of the squelch logic.
-
2802 auto haveMessage = app_.overlay().relay(*packet, peerPos.suppressionID(), peerPos.publicKey());
-
2803 if (!haveMessage.empty())
-
2804 overlay_.updateSlotAndSquelch(
-
2805 peerPos.suppressionID(), peerPos.publicKey(), std::move(haveMessage), protocol::mtPROPOSE_LEDGER);
-
2806 }
-
2807}
+
2771
+
2772// Called from our JobQueue
+
2773void
+
+
2774PeerImp::checkPropose(bool isTrusted, std::shared_ptr<protocol::TMProposeSet> const& packet, RCLCxPeerPos peerPos)
+
2775{
+
2776 JLOG(p_journal_.trace()) << "Checking " << (isTrusted ? "trusted" : "UNTRUSTED") << " proposal";
+
2777
+
2778 XRPL_ASSERT(packet, "xrpl::PeerImp::checkPropose : non-null packet");
+
2779
+
2780 if (!cluster() && !peerPos.checkSign())
+
2781 {
+
2782 std::string desc{"Proposal fails sig check"};
+
2783 JLOG(p_journal_.warn()) << desc;
+
2784 charge(Resource::feeInvalidSignature, desc);
+
2785 return;
+
2786 }
+
2787
+
2788 bool relay;
+
2789
+
2790 if (isTrusted)
+
2791 relay = app_.getOPs().processTrustedProposal(peerPos);
+
2792 else
+
2793 relay = app_.config().RELAY_UNTRUSTED_PROPOSALS == 1 || cluster();
+
2794
+
2795 if (relay)
+
2796 {
+
2797 // haveMessage contains peers, which are suppressed; i.e. the peers
+
2798 // are the source of the message, consequently the message should
+
2799 // not be relayed to these peers. But the message must be counted
+
2800 // as part of the squelch logic.
+
2801 auto haveMessage = app_.overlay().relay(*packet, peerPos.suppressionID(), peerPos.publicKey());
+
2802 if (!haveMessage.empty())
+
2803 overlay_.updateSlotAndSquelch(
+
2804 peerPos.suppressionID(), peerPos.publicKey(), std::move(haveMessage), protocol::mtPROPOSE_LEDGER);
+
2805 }
+
2806}
-
2808
-
2809void
-
-
2810PeerImp::checkValidation(
- -
2812 uint256 const& key,
- -
2814{
-
2815 if (!val->isValid())
-
2816 {
-
2817 std::string desc{"Validation forwarded by peer is invalid"};
-
2818 JLOG(p_journal_.debug()) << desc;
-
2819 charge(Resource::feeInvalidSignature, desc);
-
2820 return;
-
2821 }
-
2822
-
2823 // FIXME it should be safe to remove this try/catch. Investigate codepaths.
-
2824 try
-
2825 {
-
2826 if (app_.getOPs().recvValidation(val, std::to_string(id())) || cluster())
-
2827 {
-
2828 // haveMessage contains peers, which are suppressed; i.e. the peers
-
2829 // are the source of the message, consequently the message should
-
2830 // not be relayed to these peers. But the message must be counted
-
2831 // as part of the squelch logic.
-
2832 auto haveMessage = overlay_.relay(*packet, key, val->getSignerPublic());
-
2833 if (!haveMessage.empty())
-
2834 {
-
2835 overlay_.updateSlotAndSquelch(
-
2836 key, val->getSignerPublic(), std::move(haveMessage), protocol::mtVALIDATION);
-
2837 }
-
2838 }
-
2839 }
-
2840 catch (std::exception const& ex)
-
2841 {
-
2842 JLOG(p_journal_.trace()) << "Exception processing validation: " << ex.what();
-
2843 using namespace std::string_literals;
-
2844 charge(Resource::feeMalformedRequest, "validation "s + ex.what());
-
2845 }
-
2846}
+
2807
+
2808void
+
+
2809PeerImp::checkValidation(
+ +
2811 uint256 const& key,
+ +
2813{
+
2814 if (!val->isValid())
+
2815 {
+
2816 std::string desc{"Validation forwarded by peer is invalid"};
+
2817 JLOG(p_journal_.debug()) << desc;
+
2818 charge(Resource::feeInvalidSignature, desc);
+
2819 return;
+
2820 }
+
2821
+
2822 // FIXME it should be safe to remove this try/catch. Investigate codepaths.
+
2823 try
+
2824 {
+
2825 if (app_.getOPs().recvValidation(val, std::to_string(id())) || cluster())
+
2826 {
+
2827 // haveMessage contains peers, which are suppressed; i.e. the peers
+
2828 // are the source of the message, consequently the message should
+
2829 // not be relayed to these peers. But the message must be counted
+
2830 // as part of the squelch logic.
+
2831 auto haveMessage = overlay_.relay(*packet, key, val->getSignerPublic());
+
2832 if (!haveMessage.empty())
+
2833 {
+
2834 overlay_.updateSlotAndSquelch(
+
2835 key, val->getSignerPublic(), std::move(haveMessage), protocol::mtVALIDATION);
+
2836 }
+
2837 }
+
2838 }
+
2839 catch (std::exception const& ex)
+
2840 {
+
2841 JLOG(p_journal_.trace()) << "Exception processing validation: " << ex.what();
+
2842 using namespace std::string_literals;
+
2843 charge(Resource::feeMalformedRequest, "validation "s + ex.what());
+
2844 }
+
2845}
-
2847
-
2848// Returns the set of peers that can help us get
-
2849// the TX tree with the specified root hash.
-
2850//
- -
-
2852getPeerWithTree(OverlayImpl& ov, uint256 const& rootHash, PeerImp const* skip)
-
2853{
- -
2855 int retScore = 0;
-
2856
- -
2858 if (p->hasTxSet(rootHash) && p.get() != skip)
-
2859 {
-
2860 auto score = p->getScore(true);
-
2861 if (!ret || (score > retScore))
-
2862 {
-
2863 ret = std::move(p);
-
2864 retScore = score;
-
2865 }
-
2866 }
-
2867 });
-
2868
-
2869 return ret;
-
2870}
+
2846
+
2847// Returns the set of peers that can help us get
+
2848// the TX tree with the specified root hash.
+
2849//
+ +
+
2851getPeerWithTree(OverlayImpl& ov, uint256 const& rootHash, PeerImp const* skip)
+
2852{
+ +
2854 int retScore = 0;
+
2855
+ +
2857 if (p->hasTxSet(rootHash) && p.get() != skip)
+
2858 {
+
2859 auto score = p->getScore(true);
+
2860 if (!ret || (score > retScore))
+
2861 {
+
2862 ret = std::move(p);
+
2863 retScore = score;
+
2864 }
+
2865 }
+
2866 });
+
2867
+
2868 return ret;
+
2869}
-
2871
-
2872// Returns a random peer weighted by how likely to
-
2873// have the ledger and how responsive it is.
-
2874//
- -
-
2876getPeerWithLedger(OverlayImpl& ov, uint256 const& ledgerHash, LedgerIndex ledger, PeerImp const* skip)
-
2877{
- -
2879 int retScore = 0;
-
2880
- -
2882 if (p->hasLedger(ledgerHash, ledger) && p.get() != skip)
-
2883 {
-
2884 auto score = p->getScore(true);
-
2885 if (!ret || (score > retScore))
-
2886 {
-
2887 ret = std::move(p);
-
2888 retScore = score;
-
2889 }
-
2890 }
-
2891 });
-
2892
-
2893 return ret;
-
2894}
+
2870
+
2871// Returns a random peer weighted by how likely to
+
2872// have the ledger and how responsive it is.
+
2873//
+ +
+
2875getPeerWithLedger(OverlayImpl& ov, uint256 const& ledgerHash, LedgerIndex ledger, PeerImp const* skip)
+
2876{
+ +
2878 int retScore = 0;
+
2879
+ +
2881 if (p->hasLedger(ledgerHash, ledger) && p.get() != skip)
+
2882 {
+
2883 auto score = p->getScore(true);
+
2884 if (!ret || (score > retScore))
+
2885 {
+
2886 ret = std::move(p);
+
2887 retScore = score;
+
2888 }
+
2889 }
+
2890 });
+
2891
+
2892 return ret;
+
2893}
-
2895
-
2896void
-
-
2897PeerImp::sendLedgerBase(std::shared_ptr<Ledger const> const& ledger, protocol::TMLedgerData& ledgerData)
-
2898{
-
2899 JLOG(p_journal_.trace()) << "sendLedgerBase: Base data";
-
2900
-
2901 Serializer s(sizeof(LedgerHeader));
-
2902 addRaw(ledger->header(), s);
-
2903 ledgerData.add_nodes()->set_nodedata(s.getDataPtr(), s.getLength());
-
2904
-
2905 auto const& stateMap{ledger->stateMap()};
-
2906 if (stateMap.getHash() != beast::zero)
-
2907 {
-
2908 // Return account state root node if possible
-
2909 Serializer root(768);
-
2910
-
2911 stateMap.serializeRoot(root);
-
2912 ledgerData.add_nodes()->set_nodedata(root.getDataPtr(), root.getLength());
-
2913
-
2914 if (ledger->header().txHash != beast::zero)
-
2915 {
-
2916 auto const& txMap{ledger->txMap()};
-
2917 if (txMap.getHash() != beast::zero)
-
2918 {
-
2919 // Return TX root node if possible
-
2920 root.erase();
-
2921 txMap.serializeRoot(root);
-
2922 ledgerData.add_nodes()->set_nodedata(root.getDataPtr(), root.getLength());
-
2923 }
-
2924 }
-
2925 }
-
2926
-
2927 auto message{std::make_shared<Message>(ledgerData, protocol::mtLEDGER_DATA)};
-
2928 send(message);
-
2929}
+
2894
+
2895void
+
+
2896PeerImp::sendLedgerBase(std::shared_ptr<Ledger const> const& ledger, protocol::TMLedgerData& ledgerData)
+
2897{
+
2898 JLOG(p_journal_.trace()) << "sendLedgerBase: Base data";
+
2899
+
2900 Serializer s(sizeof(LedgerHeader));
+
2901 addRaw(ledger->header(), s);
+
2902 ledgerData.add_nodes()->set_nodedata(s.getDataPtr(), s.getLength());
+
2903
+
2904 auto const& stateMap{ledger->stateMap()};
+
2905 if (stateMap.getHash() != beast::zero)
+
2906 {
+
2907 // Return account state root node if possible
+
2908 Serializer root(768);
+
2909
+
2910 stateMap.serializeRoot(root);
+
2911 ledgerData.add_nodes()->set_nodedata(root.getDataPtr(), root.getLength());
+
2912
+
2913 if (ledger->header().txHash != beast::zero)
+
2914 {
+
2915 auto const& txMap{ledger->txMap()};
+
2916 if (txMap.getHash() != beast::zero)
+
2917 {
+
2918 // Return TX root node if possible
+
2919 root.erase();
+
2920 txMap.serializeRoot(root);
+
2921 ledgerData.add_nodes()->set_nodedata(root.getDataPtr(), root.getLength());
+
2922 }
+
2923 }
+
2924 }
+
2925
+
2926 auto message{std::make_shared<Message>(ledgerData, protocol::mtLEDGER_DATA)};
+
2927 send(message);
+
2928}
-
2930
- -
-
2932PeerImp::getLedger(std::shared_ptr<protocol::TMGetLedger> const& m)
-
2933{
-
2934 JLOG(p_journal_.trace()) << "getLedger: Ledger";
-
2935
- -
2937
-
2938 if (m->has_ledgerhash())
-
2939 {
-
2940 // Attempt to find ledger by hash
-
2941 uint256 const ledgerHash{m->ledgerhash()};
-
2942 ledger = app_.getLedgerMaster().getLedgerByHash(ledgerHash);
-
2943 if (!ledger)
-
2944 {
-
2945 JLOG(p_journal_.trace()) << "getLedger: Don't have ledger with hash " << ledgerHash;
-
2946
-
2947 if (m->has_querytype() && !m->has_requestcookie())
-
2948 {
-
2949 // Attempt to relay the request to a peer
-
2950 if (auto const peer =
-
2951 getPeerWithLedger(overlay_, ledgerHash, m->has_ledgerseq() ? m->ledgerseq() : 0, this))
-
2952 {
-
2953 m->set_requestcookie(id());
-
2954 peer->send(std::make_shared<Message>(*m, protocol::mtGET_LEDGER));
-
2955 JLOG(p_journal_.debug()) << "getLedger: Request relayed to peer";
-
2956 return ledger;
-
2957 }
-
2958
-
2959 JLOG(p_journal_.trace()) << "getLedger: Failed to find peer to relay request";
-
2960 }
-
2961 }
-
2962 }
-
2963 else if (m->has_ledgerseq())
-
2964 {
-
2965 // Attempt to find ledger by sequence
-
2966 if (m->ledgerseq() < app_.getLedgerMaster().getEarliestFetch())
-
2967 {
-
2968 JLOG(p_journal_.debug()) << "getLedger: Early ledger sequence request";
-
2969 }
-
2970 else
-
2971 {
-
2972 ledger = app_.getLedgerMaster().getLedgerBySeq(m->ledgerseq());
-
2973 if (!ledger)
-
2974 {
-
2975 JLOG(p_journal_.debug()) << "getLedger: Don't have ledger with sequence " << m->ledgerseq();
-
2976 }
-
2977 }
-
2978 }
-
2979 else if (m->has_ltype() && m->ltype() == protocol::ltCLOSED)
-
2980 {
-
2981 ledger = app_.getLedgerMaster().getClosedLedger();
-
2982 }
-
2983
-
2984 if (ledger)
-
2985 {
-
2986 // Validate retrieved ledger sequence
-
2987 auto const ledgerSeq{ledger->header().seq};
-
2988 if (m->has_ledgerseq())
-
2989 {
-
2990 if (ledgerSeq != m->ledgerseq())
-
2991 {
-
2992 // Do not resource charge a peer responding to a relay
-
2993 if (!m->has_requestcookie())
-
2994 charge(Resource::feeMalformedRequest, "get_ledger ledgerSeq");
-
2995
-
2996 ledger.reset();
-
2997 JLOG(p_journal_.warn()) << "getLedger: Invalid ledger sequence " << ledgerSeq;
-
2998 }
-
2999 }
-
3000 else if (ledgerSeq < app_.getLedgerMaster().getEarliestFetch())
-
3001 {
-
3002 ledger.reset();
-
3003 JLOG(p_journal_.debug()) << "getLedger: Early ledger sequence request " << ledgerSeq;
-
3004 }
-
3005 }
-
3006 else
-
3007 {
-
3008 JLOG(p_journal_.debug()) << "getLedger: Unable to find ledger";
-
3009 }
-
3010
-
3011 return ledger;
-
3012}
+
2929
+ +
+
2931PeerImp::getLedger(std::shared_ptr<protocol::TMGetLedger> const& m)
+
2932{
+
2933 JLOG(p_journal_.trace()) << "getLedger: Ledger";
+
2934
+ +
2936
+
2937 if (m->has_ledgerhash())
+
2938 {
+
2939 // Attempt to find ledger by hash
+
2940 uint256 const ledgerHash{m->ledgerhash()};
+
2941 ledger = app_.getLedgerMaster().getLedgerByHash(ledgerHash);
+
2942 if (!ledger)
+
2943 {
+
2944 JLOG(p_journal_.trace()) << "getLedger: Don't have ledger with hash " << ledgerHash;
+
2945
+
2946 if (m->has_querytype() && !m->has_requestcookie())
+
2947 {
+
2948 // Attempt to relay the request to a peer
+
2949 if (auto const peer =
+
2950 getPeerWithLedger(overlay_, ledgerHash, m->has_ledgerseq() ? m->ledgerseq() : 0, this))
+
2951 {
+
2952 m->set_requestcookie(id());
+
2953 peer->send(std::make_shared<Message>(*m, protocol::mtGET_LEDGER));
+
2954 JLOG(p_journal_.debug()) << "getLedger: Request relayed to peer";
+
2955 return ledger;
+
2956 }
+
2957
+
2958 JLOG(p_journal_.trace()) << "getLedger: Failed to find peer to relay request";
+
2959 }
+
2960 }
+
2961 }
+
2962 else if (m->has_ledgerseq())
+
2963 {
+
2964 // Attempt to find ledger by sequence
+
2965 if (m->ledgerseq() < app_.getLedgerMaster().getEarliestFetch())
+
2966 {
+
2967 JLOG(p_journal_.debug()) << "getLedger: Early ledger sequence request";
+
2968 }
+
2969 else
+
2970 {
+
2971 ledger = app_.getLedgerMaster().getLedgerBySeq(m->ledgerseq());
+
2972 if (!ledger)
+
2973 {
+
2974 JLOG(p_journal_.debug()) << "getLedger: Don't have ledger with sequence " << m->ledgerseq();
+
2975 }
+
2976 }
+
2977 }
+
2978 else if (m->has_ltype() && m->ltype() == protocol::ltCLOSED)
+
2979 {
+
2980 ledger = app_.getLedgerMaster().getClosedLedger();
+
2981 }
+
2982
+
2983 if (ledger)
+
2984 {
+
2985 // Validate retrieved ledger sequence
+
2986 auto const ledgerSeq{ledger->header().seq};
+
2987 if (m->has_ledgerseq())
+
2988 {
+
2989 if (ledgerSeq != m->ledgerseq())
+
2990 {
+
2991 // Do not resource charge a peer responding to a relay
+
2992 if (!m->has_requestcookie())
+
2993 charge(Resource::feeMalformedRequest, "get_ledger ledgerSeq");
+
2994
+
2995 ledger.reset();
+
2996 JLOG(p_journal_.warn()) << "getLedger: Invalid ledger sequence " << ledgerSeq;
+
2997 }
+
2998 }
+
2999 else if (ledgerSeq < app_.getLedgerMaster().getEarliestFetch())
+
3000 {
+
3001 ledger.reset();
+
3002 JLOG(p_journal_.debug()) << "getLedger: Early ledger sequence request " << ledgerSeq;
+
3003 }
+
3004 }
+
3005 else
+
3006 {
+
3007 JLOG(p_journal_.debug()) << "getLedger: Unable to find ledger";
+
3008 }
+
3009
+
3010 return ledger;
+
3011}
-
3013
- -
-
3015PeerImp::getTxSet(std::shared_ptr<protocol::TMGetLedger> const& m) const
-
3016{
-
3017 JLOG(p_journal_.trace()) << "getTxSet: TX set";
-
3018
-
3019 uint256 const txSetHash{m->ledgerhash()};
-
3020 std::shared_ptr<SHAMap> shaMap{app_.getInboundTransactions().getSet(txSetHash, false)};
-
3021 if (!shaMap)
-
3022 {
-
3023 if (m->has_querytype() && !m->has_requestcookie())
-
3024 {
-
3025 // Attempt to relay the request to a peer
-
3026 if (auto const peer = getPeerWithTree(overlay_, txSetHash, this))
-
3027 {
-
3028 m->set_requestcookie(id());
-
3029 peer->send(std::make_shared<Message>(*m, protocol::mtGET_LEDGER));
-
3030 JLOG(p_journal_.debug()) << "getTxSet: Request relayed";
-
3031 }
-
3032 else
-
3033 {
-
3034 JLOG(p_journal_.debug()) << "getTxSet: Failed to find relay peer";
-
3035 }
-
3036 }
-
3037 else
-
3038 {
-
3039 JLOG(p_journal_.debug()) << "getTxSet: Failed to find TX set";
-
3040 }
-
3041 }
-
3042
-
3043 return shaMap;
-
3044}
+
3012
+ +
+
3014PeerImp::getTxSet(std::shared_ptr<protocol::TMGetLedger> const& m) const
+
3015{
+
3016 JLOG(p_journal_.trace()) << "getTxSet: TX set";
+
3017
+
3018 uint256 const txSetHash{m->ledgerhash()};
+
3019 std::shared_ptr<SHAMap> shaMap{app_.getInboundTransactions().getSet(txSetHash, false)};
+
3020 if (!shaMap)
+
3021 {
+
3022 if (m->has_querytype() && !m->has_requestcookie())
+
3023 {
+
3024 // Attempt to relay the request to a peer
+
3025 if (auto const peer = getPeerWithTree(overlay_, txSetHash, this))
+
3026 {
+
3027 m->set_requestcookie(id());
+
3028 peer->send(std::make_shared<Message>(*m, protocol::mtGET_LEDGER));
+
3029 JLOG(p_journal_.debug()) << "getTxSet: Request relayed";
+
3030 }
+
3031 else
+
3032 {
+
3033 JLOG(p_journal_.debug()) << "getTxSet: Failed to find relay peer";
+
3034 }
+
3035 }
+
3036 else
+
3037 {
+
3038 JLOG(p_journal_.debug()) << "getTxSet: Failed to find TX set";
+
3039 }
+
3040 }
+
3041
+
3042 return shaMap;
+
3043}
-
3045
-
3046void
-
-
3047PeerImp::processLedgerRequest(std::shared_ptr<protocol::TMGetLedger> const& m)
-
3048{
-
3049 // Do not resource charge a peer responding to a relay
-
3050 if (!m->has_requestcookie())
-
3051 charge(Resource::feeModerateBurdenPeer, "received a get ledger request");
-
3052
- - -
3055 SHAMap const* map{nullptr};
-
3056 protocol::TMLedgerData ledgerData;
-
3057 bool fatLeaves{true};
-
3058 auto const itype{m->itype()};
-
3059
-
3060 if (itype == protocol::liTS_CANDIDATE)
-
3061 {
-
3062 if (sharedMap = getTxSet(m); !sharedMap)
-
3063 return;
-
3064 map = sharedMap.get();
-
3065
-
3066 // Fill out the reply
-
3067 ledgerData.set_ledgerseq(0);
-
3068 ledgerData.set_ledgerhash(m->ledgerhash());
-
3069 ledgerData.set_type(protocol::liTS_CANDIDATE);
-
3070 if (m->has_requestcookie())
-
3071 ledgerData.set_requestcookie(m->requestcookie());
-
3072
-
3073 // We'll already have most transactions
-
3074 fatLeaves = false;
-
3075 }
-
3076 else
-
3077 {
-
3078 if (send_queue_.size() >= Tuning::dropSendQueue)
-
3079 {
-
3080 JLOG(p_journal_.debug()) << "processLedgerRequest: Large send queue";
-
3081 return;
-
3082 }
-
3083 if (app_.getFeeTrack().isLoadedLocal() && !cluster())
-
3084 {
-
3085 JLOG(p_journal_.debug()) << "processLedgerRequest: Too busy";
-
3086 return;
-
3087 }
-
3088
-
3089 if (ledger = getLedger(m); !ledger)
-
3090 return;
-
3091
-
3092 // Fill out the reply
-
3093 auto const ledgerHash{ledger->header().hash};
-
3094 ledgerData.set_ledgerhash(ledgerHash.begin(), ledgerHash.size());
-
3095 ledgerData.set_ledgerseq(ledger->header().seq);
-
3096 ledgerData.set_type(itype);
-
3097 if (m->has_requestcookie())
-
3098 ledgerData.set_requestcookie(m->requestcookie());
-
3099
-
3100 switch (itype)
-
3101 {
-
3102 case protocol::liBASE:
-
3103 sendLedgerBase(ledger, ledgerData);
-
3104 return;
-
3105
-
3106 case protocol::liTX_NODE:
-
3107 map = &ledger->txMap();
-
3108 JLOG(p_journal_.trace()) << "processLedgerRequest: TX map hash " << to_string(map->getHash());
-
3109 break;
-
3110
-
3111 case protocol::liAS_NODE:
-
3112 map = &ledger->stateMap();
-
3113 JLOG(p_journal_.trace()) << "processLedgerRequest: Account state map hash "
-
3114 << to_string(map->getHash());
-
3115 break;
-
3116
-
3117 default:
-
3118 // This case should not be possible here
-
3119 JLOG(p_journal_.error()) << "processLedgerRequest: Invalid ledger info type";
-
3120 return;
-
3121 }
-
3122 }
-
3123
-
3124 if (!map)
-
3125 {
-
3126 JLOG(p_journal_.warn()) << "processLedgerRequest: Unable to find map";
-
3127 return;
-
3128 }
-
3129
-
3130 // Add requested node data to reply
-
3131 if (m->nodeids_size() > 0)
-
3132 {
-
3133 auto const queryDepth{m->has_querydepth() ? m->querydepth() : (isHighLatency() ? 2 : 1)};
-
3134
- -
3136
-
3137 for (int i = 0; i < m->nodeids_size() && ledgerData.nodes_size() < Tuning::softMaxReplyNodes; ++i)
-
3138 {
-
3139 auto const shaMapNodeId{deserializeSHAMapNodeID(m->nodeids(i))};
-
3140
-
3141 data.clear();
-
3142 data.reserve(Tuning::softMaxReplyNodes);
-
3143
-
3144 try
-
3145 {
-
3146 if (map->getNodeFat(*shaMapNodeId, data, fatLeaves, queryDepth))
-
3147 {
-
3148 JLOG(p_journal_.trace()) << "processLedgerRequest: getNodeFat got " << data.size() << " nodes";
-
3149
-
3150 for (auto const& d : data)
-
3151 {
-
3152 if (ledgerData.nodes_size() >= Tuning::hardMaxReplyNodes)
-
3153 break;
-
3154 protocol::TMLedgerNode* node{ledgerData.add_nodes()};
-
3155 node->set_nodeid(d.first.getRawString());
-
3156 node->set_nodedata(d.second.data(), d.second.size());
-
3157 }
-
3158 }
-
3159 else
-
3160 {
-
3161 JLOG(p_journal_.warn()) << "processLedgerRequest: getNodeFat returns false";
-
3162 }
-
3163 }
-
3164 catch (std::exception const& e)
-
3165 {
-
3166 std::string info;
-
3167 switch (itype)
-
3168 {
-
3169 case protocol::liBASE:
-
3170 // This case should not be possible here
-
3171 info = "Ledger base";
-
3172 break;
-
3173
-
3174 case protocol::liTX_NODE:
-
3175 info = "TX node";
-
3176 break;
-
3177
-
3178 case protocol::liAS_NODE:
-
3179 info = "AS node";
-
3180 break;
-
3181
-
3182 case protocol::liTS_CANDIDATE:
-
3183 info = "TS candidate";
-
3184 break;
-
3185
-
3186 default:
-
3187 info = "Invalid";
-
3188 break;
-
3189 }
-
3190
-
3191 if (!m->has_ledgerhash())
-
3192 info += ", no hash specified";
-
3193
-
3194 JLOG(p_journal_.warn()) << "processLedgerRequest: getNodeFat with nodeId " << *shaMapNodeId
-
3195 << " and ledger info type " << info << " throws exception: " << e.what();
-
3196 }
-
3197 }
-
3198
-
3199 JLOG(p_journal_.info()) << "processLedgerRequest: Got request for " << m->nodeids_size() << " nodes at depth "
-
3200 << queryDepth << ", return " << ledgerData.nodes_size() << " nodes";
-
3201 }
-
3202
-
3203 if (ledgerData.nodes_size() == 0)
-
3204 return;
-
3205
-
3206 send(std::make_shared<Message>(ledgerData, protocol::mtLEDGER_DATA));
-
3207}
+
3044
+
3045void
+
+
3046PeerImp::processLedgerRequest(std::shared_ptr<protocol::TMGetLedger> const& m)
+
3047{
+
3048 // Do not resource charge a peer responding to a relay
+
3049 if (!m->has_requestcookie())
+
3050 charge(Resource::feeModerateBurdenPeer, "received a get ledger request");
+
3051
+ + +
3054 SHAMap const* map{nullptr};
+
3055 protocol::TMLedgerData ledgerData;
+
3056 bool fatLeaves{true};
+
3057 auto const itype{m->itype()};
+
3058
+
3059 if (itype == protocol::liTS_CANDIDATE)
+
3060 {
+
3061 if (sharedMap = getTxSet(m); !sharedMap)
+
3062 return;
+
3063 map = sharedMap.get();
+
3064
+
3065 // Fill out the reply
+
3066 ledgerData.set_ledgerseq(0);
+
3067 ledgerData.set_ledgerhash(m->ledgerhash());
+
3068 ledgerData.set_type(protocol::liTS_CANDIDATE);
+
3069 if (m->has_requestcookie())
+
3070 ledgerData.set_requestcookie(m->requestcookie());
+
3071
+
3072 // We'll already have most transactions
+
3073 fatLeaves = false;
+
3074 }
+
3075 else
+
3076 {
+
3077 if (send_queue_.size() >= Tuning::dropSendQueue)
+
3078 {
+
3079 JLOG(p_journal_.debug()) << "processLedgerRequest: Large send queue";
+
3080 return;
+
3081 }
+
3082 if (app_.getFeeTrack().isLoadedLocal() && !cluster())
+
3083 {
+
3084 JLOG(p_journal_.debug()) << "processLedgerRequest: Too busy";
+
3085 return;
+
3086 }
+
3087
+
3088 if (ledger = getLedger(m); !ledger)
+
3089 return;
+
3090
+
3091 // Fill out the reply
+
3092 auto const ledgerHash{ledger->header().hash};
+
3093 ledgerData.set_ledgerhash(ledgerHash.begin(), ledgerHash.size());
+
3094 ledgerData.set_ledgerseq(ledger->header().seq);
+
3095 ledgerData.set_type(itype);
+
3096 if (m->has_requestcookie())
+
3097 ledgerData.set_requestcookie(m->requestcookie());
+
3098
+
3099 switch (itype)
+
3100 {
+
3101 case protocol::liBASE:
+
3102 sendLedgerBase(ledger, ledgerData);
+
3103 return;
+
3104
+
3105 case protocol::liTX_NODE:
+
3106 map = &ledger->txMap();
+
3107 JLOG(p_journal_.trace()) << "processLedgerRequest: TX map hash " << to_string(map->getHash());
+
3108 break;
+
3109
+
3110 case protocol::liAS_NODE:
+
3111 map = &ledger->stateMap();
+
3112 JLOG(p_journal_.trace()) << "processLedgerRequest: Account state map hash "
+
3113 << to_string(map->getHash());
+
3114 break;
+
3115
+
3116 default:
+
3117 // This case should not be possible here
+
3118 JLOG(p_journal_.error()) << "processLedgerRequest: Invalid ledger info type";
+
3119 return;
+
3120 }
+
3121 }
+
3122
+
3123 if (!map)
+
3124 {
+
3125 JLOG(p_journal_.warn()) << "processLedgerRequest: Unable to find map";
+
3126 return;
+
3127 }
+
3128
+
3129 // Add requested node data to reply
+
3130 if (m->nodeids_size() > 0)
+
3131 {
+
3132 auto const queryDepth{m->has_querydepth() ? m->querydepth() : (isHighLatency() ? 2 : 1)};
+
3133
+ +
3135
+
3136 for (int i = 0; i < m->nodeids_size() && ledgerData.nodes_size() < Tuning::softMaxReplyNodes; ++i)
+
3137 {
+
3138 auto const shaMapNodeId{deserializeSHAMapNodeID(m->nodeids(i))};
+
3139
+
3140 data.clear();
+
3141 data.reserve(Tuning::softMaxReplyNodes);
+
3142
+
3143 try
+
3144 {
+
3145 if (map->getNodeFat(*shaMapNodeId, data, fatLeaves, queryDepth))
+
3146 {
+
3147 JLOG(p_journal_.trace()) << "processLedgerRequest: getNodeFat got " << data.size() << " nodes";
+
3148
+
3149 for (auto const& d : data)
+
3150 {
+
3151 if (ledgerData.nodes_size() >= Tuning::hardMaxReplyNodes)
+
3152 break;
+
3153 protocol::TMLedgerNode* node{ledgerData.add_nodes()};
+
3154 node->set_nodeid(d.first.getRawString());
+
3155 node->set_nodedata(d.second.data(), d.second.size());
+
3156 }
+
3157 }
+
3158 else
+
3159 {
+
3160 JLOG(p_journal_.warn()) << "processLedgerRequest: getNodeFat returns false";
+
3161 }
+
3162 }
+
3163 catch (std::exception const& e)
+
3164 {
+
3165 std::string info;
+
3166 switch (itype)
+
3167 {
+
3168 case protocol::liBASE:
+
3169 // This case should not be possible here
+
3170 info = "Ledger base";
+
3171 break;
+
3172
+
3173 case protocol::liTX_NODE:
+
3174 info = "TX node";
+
3175 break;
+
3176
+
3177 case protocol::liAS_NODE:
+
3178 info = "AS node";
+
3179 break;
+
3180
+
3181 case protocol::liTS_CANDIDATE:
+
3182 info = "TS candidate";
+
3183 break;
+
3184
+
3185 default:
+
3186 info = "Invalid";
+
3187 break;
+
3188 }
+
3189
+
3190 if (!m->has_ledgerhash())
+
3191 info += ", no hash specified";
+
3192
+
3193 JLOG(p_journal_.warn()) << "processLedgerRequest: getNodeFat with nodeId " << *shaMapNodeId
+
3194 << " and ledger info type " << info << " throws exception: " << e.what();
+
3195 }
+
3196 }
+
3197
+
3198 JLOG(p_journal_.info()) << "processLedgerRequest: Got request for " << m->nodeids_size() << " nodes at depth "
+
3199 << queryDepth << ", return " << ledgerData.nodes_size() << " nodes";
+
3200 }
+
3201
+
3202 if (ledgerData.nodes_size() == 0)
+
3203 return;
+
3204
+
3205 send(std::make_shared<Message>(ledgerData, protocol::mtLEDGER_DATA));
+
3206}
-
3208
-
3209int
-
-
3210PeerImp::getScore(bool haveItem) const
-
3211{
-
3212 // Random component of score, used to break ties and avoid
-
3213 // overloading the "best" peer
-
3214 static int const spRandomMax = 9999;
-
3215
-
3216 // Score for being very likely to have the thing we are
-
3217 // look for; should be roughly spRandomMax
-
3218 static int const spHaveItem = 10000;
-
3219
-
3220 // Score reduction for each millisecond of latency; should
-
3221 // be roughly spRandomMax divided by the maximum reasonable
-
3222 // latency
-
3223 static int const spLatency = 30;
-
3224
-
3225 // Penalty for unknown latency; should be roughly spRandomMax
-
3226 static int const spNoLatency = 8000;
-
3227
-
3228 int score = rand_int(spRandomMax);
-
3229
-
3230 if (haveItem)
-
3231 score += spHaveItem;
-
3232
- -
3234 {
-
3235 std::lock_guard sl(recentLock_);
-
3236 latency = latency_;
-
3237 }
-
3238
-
3239 if (latency)
-
3240 score -= latency->count() * spLatency;
-
3241 else
-
3242 score -= spNoLatency;
-
3243
-
3244 return score;
-
3245}
+
3207
+
3208int
+
+
3209PeerImp::getScore(bool haveItem) const
+
3210{
+
3211 // Random component of score, used to break ties and avoid
+
3212 // overloading the "best" peer
+
3213 static int const spRandomMax = 9999;
+
3214
+
3215 // Score for being very likely to have the thing we are
+
3216 // look for; should be roughly spRandomMax
+
3217 static int const spHaveItem = 10000;
+
3218
+
3219 // Score reduction for each millisecond of latency; should
+
3220 // be roughly spRandomMax divided by the maximum reasonable
+
3221 // latency
+
3222 static int const spLatency = 30;
+
3223
+
3224 // Penalty for unknown latency; should be roughly spRandomMax
+
3225 static int const spNoLatency = 8000;
+
3226
+
3227 int score = rand_int(spRandomMax);
+
3228
+
3229 if (haveItem)
+
3230 score += spHaveItem;
+
3231
+ +
3233 {
+
3234 std::lock_guard sl(recentLock_);
+
3235 latency = latency_;
+
3236 }
+
3237
+
3238 if (latency)
+
3239 score -= latency->count() * spLatency;
+
3240 else
+
3241 score -= spNoLatency;
+
3242
+
3243 return score;
+
3244}
-
3246
-
3247bool
-
-
3248PeerImp::isHighLatency() const
-
3249{
-
3250 std::lock_guard sl(recentLock_);
-
3251 return latency_ >= peerHighLatency;
-
3252}
+
3245
+
3246bool
+
+
3247PeerImp::isHighLatency() const
+
3248{
+
3249 std::lock_guard sl(recentLock_);
+
3250 return latency_ >= peerHighLatency;
+
3251}
-
3253
-
3254void
-
-
3255PeerImp::Metrics::add_message(std::uint64_t bytes)
-
3256{
-
3257 using namespace std::chrono_literals;
-
3258 std::unique_lock lock{mutex_};
-
3259
-
3260 totalBytes_ += bytes;
-
3261 accumBytes_ += bytes;
-
3262 auto const timeElapsed = clock_type::now() - intervalStart_;
-
3263 auto const timeElapsedInSecs = std::chrono::duration_cast<std::chrono::seconds>(timeElapsed);
-
3264
-
3265 if (timeElapsedInSecs >= 1s)
-
3266 {
-
3267 auto const avgBytes = accumBytes_ / timeElapsedInSecs.count();
-
3268 rollingAvg_.push_back(avgBytes);
-
3269
-
3270 auto const totalBytes = std::accumulate(rollingAvg_.begin(), rollingAvg_.end(), 0ull);
-
3271 rollingAvgBytes_ = totalBytes / rollingAvg_.size();
-
3272
-
3273 intervalStart_ = clock_type::now();
-
3274 accumBytes_ = 0;
-
3275 }
-
3276}
+
3252
+
3253void
+
+
3254PeerImp::Metrics::add_message(std::uint64_t bytes)
+
3255{
+
3256 using namespace std::chrono_literals;
+
3257 std::unique_lock lock{mutex_};
+
3258
+
3259 totalBytes_ += bytes;
+
3260 accumBytes_ += bytes;
+
3261 auto const timeElapsed = clock_type::now() - intervalStart_;
+
3262 auto const timeElapsedInSecs = std::chrono::duration_cast<std::chrono::seconds>(timeElapsed);
+
3263
+
3264 if (timeElapsedInSecs >= 1s)
+
3265 {
+
3266 auto const avgBytes = accumBytes_ / timeElapsedInSecs.count();
+
3267 rollingAvg_.push_back(avgBytes);
+
3268
+
3269 auto const totalBytes = std::accumulate(rollingAvg_.begin(), rollingAvg_.end(), 0ull);
+
3270 rollingAvgBytes_ = totalBytes / rollingAvg_.size();
+
3271
+
3272 intervalStart_ = clock_type::now();
+
3273 accumBytes_ = 0;
+
3274 }
+
3275}
-
3277
- -
-
3279PeerImp::Metrics::average_bytes() const
-
3280{
-
3281 std::shared_lock lock{mutex_};
-
3282 return rollingAvgBytes_;
-
3283}
+
3276
+ +
+
3278PeerImp::Metrics::average_bytes() const
+
3279{
+
3280 std::shared_lock lock{mutex_};
+
3281 return rollingAvgBytes_;
+
3282}
-
3284
- -
-
3286PeerImp::Metrics::total_bytes() const
-
3287{
-
3288 std::shared_lock lock{mutex_};
-
3289 return totalBytes_;
-
3290}
+
3283
+ +
+
3285PeerImp::Metrics::total_bytes() const
+
3286{
+
3287 std::shared_lock lock{mutex_};
+
3288 return totalBytes_;
+
3289}
-
3291
-
3292} // namespace xrpl
+
3290
+
3291} // namespace xrpl
T accumulate(T... args)
@@ -3579,22 +3578,22 @@ $(document).ready(function() { init_codefold(0); });
static std::size_t messageSize(::google::protobuf::Message const &message)
Definition Message.cpp:32
virtual bool isNeedNetworkLedger()=0
-
void deletePeer(Peer::id_t id)
Called when the peer is deleted.
-
void remove(std::shared_ptr< PeerFinder::Slot > const &slot)
-
void addTxMetrics(Args... args)
Add tx reduce-relay metrics.
-
void incPeerDisconnectCharges() override
-
void activate(std::shared_ptr< PeerImp > const &peer)
Called when a peer has connected successfully This is called after the peer handshake has been comple...
-
void reportOutboundTraffic(TrafficCount::category cat, int bytes)
-
void for_each(UnaryFunc &&f) const
-
void onManifests(std::shared_ptr< protocol::TMManifests > const &m, std::shared_ptr< PeerImp > const &from)
+
void deletePeer(Peer::id_t id)
Called when the peer is deleted.
+
void remove(std::shared_ptr< PeerFinder::Slot > const &slot)
+
void addTxMetrics(Args... args)
Add tx reduce-relay metrics.
+
void incPeerDisconnectCharges() override
+
void activate(std::shared_ptr< PeerImp > const &peer)
Called when a peer has connected successfully This is called after the peer handshake has been comple...
+
void reportOutboundTraffic(TrafficCount::category cat, int bytes)
+
void for_each(UnaryFunc &&f) const
+
void onManifests(std::shared_ptr< protocol::TMManifests > const &m, std::shared_ptr< PeerImp > const &from)
PeerFinder::Manager & peerFinder()
Resource::Manager & resourceManager()
-
void incPeerDisconnect() override
Increment and retrieve counters for total peer disconnects, and disconnects we initiate for excessive...
-
void onPeerDeactivate(Peer::id_t id)
+
void incPeerDisconnect() override
Increment and retrieve counters for total peer disconnects, and disconnects we initiate for excessive...
+
void onPeerDeactivate(Peer::id_t id)
Setup const & setup() const
-
std::shared_ptr< Message > getManifestsMessage()
-
void reportInboundTraffic(TrafficCount::category cat, int bytes)
-
void incJqTransOverflow() override
Increment and retrieve counter for transaction job queue overflows.
+
std::shared_ptr< Message > getManifestsMessage()
+
void reportInboundTraffic(TrafficCount::category cat, int bytes)
+
void incJqTransOverflow() override
Increment and retrieve counter for transaction job queue overflows.
virtual void on_closed(std::shared_ptr< Slot > const &slot)=0
Called when the slot is closed.
virtual Config config()=0
Returns the configuration for the manager.
virtual void on_endpoints(std::shared_ptr< Slot > const &slot, Endpoints const &endpoints)=0
Called when mtENDPOINTS is received.
@@ -3605,22 +3604,22 @@ $(document).ready(function() { init_codefold(0); });
std::string getVersion() const
Return the version of rippled that the peer is running, if reported.
Definition PeerImp.cpp:335
std::unique_ptr< LoadEvent > load_event_
Definition PeerImp.h:233
beast::Journal const p_journal_
Definition PeerImp.h:119
-
void onMessage(std::shared_ptr< protocol::TMManifests > const &m)
Definition PeerImp.cpp:1035
+
void onMessage(std::shared_ptr< protocol::TMManifests > const &m)
Definition PeerImp.cpp:1034
ProtocolVersion protocol_
Definition PeerImp.h:138
bool txReduceRelayEnabled_
Definition PeerImp.h:245
-
void close()
Forcibly closes the underlying socket connection.
Definition PeerImp.cpp:611
+
void close()
Forcibly closes the underlying socket connection.
Definition PeerImp.cpp:610
bool readPending_
Definition PeerImp.h:227
-
void handleTransaction(std::shared_ptr< protocol::TMTransaction > const &m, bool eraseTxQueue, bool batch)
Called from onMessage(TMTransaction(s)).
Definition PeerImp.cpp:1218
+
void handleTransaction(std::shared_ptr< protocol::TMTransaction > const &m, bool eraseTxQueue, bool batch)
Called from onMessage(TMTransaction(s)).
Definition PeerImp.cpp:1217
http_request_type request_
Definition PeerImp.h:215
void removeTxQueue(uint256 const &hash) override
Remove transaction's hash from the transactions' hashes queue.
Definition PeerImp.cpp:296
-
std::string name() const
Definition PeerImp.cpp:804
+
std::string name() const
Definition PeerImp.cpp:803
bool txReduceRelayEnabled() const override
Definition PeerImp.h:486
std::shared_ptr< PeerFinder::Slot > const slot_
Definition PeerImp.h:213
boost::beast::http::fields const & headers_
Definition PeerImp.h:217
Compressed compressionEnabled_
Definition PeerImp.h:238
-
void onTimer(error_code const &ec)
Handles the expiration of the peer activity timer.
Definition PeerImp.cpp:661
+
void onTimer(error_code const &ec)
Handles the expiration of the peer activity timer.
Definition PeerImp.cpp:660
boost::system::error_code error_code
Definition PeerImp.h:103
-
void tryAsyncShutdown()
Attempts to perform a graceful SSL shutdown if conditions are met.
Definition PeerImp.cpp:552
+
void tryAsyncShutdown()
Attempts to perform a graceful SSL shutdown if conditions are met.
Definition PeerImp.cpp:551
LedgerIndex minLedger_
Definition PeerImp.h:149
id_t const id_
Definition PeerImp.h:113
std::string const & fingerprint() const override
Definition PeerImp.h:664
@@ -3636,27 +3635,27 @@ $(document).ready(function() { init_codefold(0); });
socket_type & socket_
Definition PeerImp.h:121
beast::IP::Endpoint const remote_address_
Definition PeerImp.h:130
int large_sendq_
Definition PeerImp.h:232
-
std::string domain() const
Definition PeerImp.cpp:811
+
std::string domain() const
Definition PeerImp.cpp:810
std::atomic< Tracking > tracking_
Definition PeerImp.h:140
void ledgerRange(std::uint32_t &minSeq, std::uint32_t &maxSeq) const override
Definition PeerImp.cpp:484
clock_type::duration uptime() const
Definition PeerImp.h:417
LedgerIndex maxLedger_
Definition PeerImp.h:150
Application & app_
Definition PeerImp.h:112
PublicKey const publicKey_
Definition PeerImp.h:143
-
void onMessageBegin(std::uint16_t type, std::shared_ptr<::google::protobuf::Message > const &m, std::size_t size, std::size_t uncompressed_size, bool isCompressed)
Definition PeerImp.cpp:991
+
void onMessageBegin(std::uint16_t type, std::shared_ptr<::google::protobuf::Message > const &m, std::size_t size, std::size_t uncompressed_size, bool isCompressed)
Definition PeerImp.cpp:990
Json::Value json() override
Definition PeerImp.cpp:343
bool cluster() const override
Returns true if this connection is a member of the cluster.
Definition PeerImp.cpp:329
-
void onWriteMessage(error_code ec, std::size_t bytes_transferred)
Definition PeerImp.cpp:933
+
void onWriteMessage(error_code ec, std::size_t bytes_transferred)
Definition PeerImp.cpp:932
virtual void run()
Definition PeerImp.cpp:129
OverlayImpl & overlay_
Definition PeerImp.h:134
std::queue< std::shared_ptr< Message > > send_queue_
Definition PeerImp.h:218
virtual ~PeerImp()
Definition PeerImp.cpp:106
-
static std::string makePrefix(std::string const &fingerprint)
Definition PeerImp.cpp:653
+
static std::string makePrefix(std::string const &fingerprint)
Definition PeerImp.cpp:652
ChargeWithContext fee_
Definition PeerImp.h:212
-
void onShutdown(error_code ec)
Handles the completion of the asynchronous SSL shutdown.
Definition PeerImp.cpp:587
-
void onMessageUnknown(std::uint16_t type)
Definition PeerImp.cpp:985
+
void onShutdown(error_code ec)
Handles the completion of the asynchronous SSL shutdown.
Definition PeerImp.cpp:586
+
void onMessageUnknown(std::uint16_t type)
Definition PeerImp.cpp:984
protocol::TMStatusChange last_status_
Definition PeerImp.h:210
-
void onReadMessage(error_code ec, std::size_t bytes_transferred)
Definition PeerImp.cpp:854
+
void onReadMessage(error_code ec, std::size_t bytes_transferred)
Definition PeerImp.cpp:853
Tracking
Whether the peer's view of the ledger converges or diverges from ours.
Definition PeerImp.h:99
@@ -3667,8 +3666,8 @@ $(document).ready(function() { init_codefold(0); });
void send(std::shared_ptr< Message > const &m) override
Definition PeerImp.cpp:204
hash_set< uint256 > txQueue_
Definition PeerImp.h:243
bool supportsFeature(ProtocolFeature f) const override
Definition PeerImp.cpp:454
-
void doProtocolStart()
Definition PeerImp.cpp:821
-
void shutdown()
Initiates the peer disconnection sequence.
Definition PeerImp.cpp:572
+
void doProtocolStart()
Definition PeerImp.cpp:820
+
void shutdown()
Initiates the peer disconnection sequence.
Definition PeerImp.cpp:571
bool const inbound_
Definition PeerImp.h:135
boost::asio::strand< boost::asio::executor > strand_
Definition PeerImp.h:123
waitable_timer timer_
Definition PeerImp.h:126
@@ -3680,7 +3679,7 @@ $(document).ready(function() { init_codefold(0); });
boost::circular_buffer< uint256 > recentTxSets_
Definition PeerImp.h:155
clock_type::time_point trackingTime_
Definition PeerImp.h:141
beast::Journal const journal_
Definition PeerImp.h:118
-
void cancelTimer() noexcept
Cancels any pending wait on the peer activity timer.
Definition PeerImp.cpp:724
+
void cancelTimer() noexcept
Cancels any pending wait on the peer activity timer.
Definition PeerImp.cpp:723
bool hasRange(std::uint32_t uMin, std::uint32_t uMax) override
Definition PeerImp.cpp:510
uint256 previousLedgerHash_
Definition PeerImp.h:152
Resource::Consumer usage_
Definition PeerImp.h:211
@@ -3688,14 +3687,14 @@ $(document).ready(function() { init_codefold(0); });
std::optional< std::uint32_t > lastPingSeq_
Definition PeerImp.h:158
bool crawl() const
Returns true if this connection will publicly share its IP address.
Definition PeerImp.cpp:320
void stop() override
Definition PeerImp.cpp:184
-
void setTimer(std::chrono::seconds interval)
Sets and starts the peer timer.
Definition PeerImp.cpp:635
+
void setTimer(std::chrono::seconds interval)
Sets and starts the peer timer.
Definition PeerImp.cpp:634
bool shutdown_
Definition PeerImp.h:221
-
void doAccept()
Definition PeerImp.cpp:738
+
void doAccept()
Definition PeerImp.cpp:737
void fail(std::string const &name, error_code ec)
Handles a failure associated with a specific error code.
Definition PeerImp.cpp:519
std::mutex recentLock_
Definition PeerImp.h:209
bool hasLedger(uint256 const &hash, std::uint32_t seq) const override
Definition PeerImp.cpp:471
boost::asio::basic_waitable_timer< std::chrono::steady_clock > waitable_timer
Definition PeerImp.h:109
-
void onMessageEnd(std::uint16_t type, std::shared_ptr<::google::protobuf::Message > const &m)
Definition PeerImp.cpp:1028
+
void onMessageEnd(std::uint16_t type, std::shared_ptr<::google::protobuf::Message > const &m)
Definition PeerImp.cpp:1027
Represents a peer connection in the overlay.
A public key.
Definition PublicKey.h:42
A peer's signed, proposed position for use in RCLConsensus.
@@ -3800,9 +3799,9 @@ $(document).ready(function() { init_codefold(0); }); -
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
static bool stringIsUint256Sized(std::string const &pBuffStr)
Definition PeerImp.cpp:123
-
static std::shared_ptr< PeerImp > getPeerWithLedger(OverlayImpl &ov, uint256 const &ledgerHash, LedgerIndex ledger, PeerImp const *skip)
Definition PeerImp.cpp:2876
+
static std::shared_ptr< PeerImp > getPeerWithLedger(OverlayImpl &ov, uint256 const &ledgerHash, LedgerIndex ledger, PeerImp const *skip)
Definition PeerImp.cpp:2875
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
std::pair< std::size_t, boost::system::error_code > invokeProtocolMessage(Buffers const &buffers, Handler &handler, std::size_t &hint)
Calls the handler for up to one protocol message in the passed buffers.
@ jtMISSING_TXN
Definition Job.h:42
@@ -3841,7 +3840,7 @@ $(document).ready(function() { init_codefold(0); });
static constexpr char FEATURE_VPRR[]
Definition Handshake.h:120
std::string protocolMessageName(int type)
Returns the name of a protocol message given its type.
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
Definition STTx.cpp:776
-
static std::shared_ptr< PeerImp > getPeerWithTree(OverlayImpl &ov, uint256 const &rootHash, PeerImp const *skip)
Definition PeerImp.cpp:2852
+
static std::shared_ptr< PeerImp > getPeerWithTree(OverlayImpl &ov, uint256 const &rootHash, PeerImp const *skip)
Definition PeerImp.cpp:2851
static constexpr char FEATURE_TXRR[]
Definition Handshake.h:122
T nth_element(T... args)
diff --git a/PeerImp_8h_source.html b/PeerImp_8h_source.html index 20d506c27a..7dd6322095 100644 --- a/PeerImp_8h_source.html +++ b/PeerImp_8h_source.html @@ -815,43 +815,43 @@ $(document).ready(function() { init_codefold(0); });
Metrics & operator=(Metrics &&)=delete
Metrics(Metrics &&)=delete
-
std::uint64_t average_bytes() const
Definition PeerImp.cpp:3279
-
std::uint64_t total_bytes() const
Definition PeerImp.cpp:3286
+
std::uint64_t average_bytes() const
Definition PeerImp.cpp:3278
+
std::uint64_t total_bytes() const
Definition PeerImp.cpp:3285
std::uint64_t accumBytes_
Definition PeerImp.h:275
-
void add_message(std::uint64_t bytes)
Definition PeerImp.cpp:3255
+
void add_message(std::uint64_t bytes)
Definition PeerImp.cpp:3254
std::uint64_t totalBytes_
Definition PeerImp.h:274
This class manages established peer-to-peer connections, handles message exchange,...
Definition PeerImp.h:96
-
void checkTracking(std::uint32_t validationSeq)
Check if the peer is tracking.
Definition PeerImp.cpp:1872
+
void checkTracking(std::uint32_t validationSeq)
Check if the peer is tracking.
Definition PeerImp.cpp:1871
std::optional< std::chrono::milliseconds > latency_
Definition PeerImp.h:157
void addTxQueue(uint256 const &hash) override
Add transaction's hash to the transactions' hashes queue.
Definition PeerImp.cpp:280
std::string getVersion() const
Return the version of rippled that the peer is running, if reported.
Definition PeerImp.cpp:335
std::unique_ptr< LoadEvent > load_event_
Definition PeerImp.h:233
beast::Journal const p_journal_
Definition PeerImp.h:119
boost::asio::ip::tcp::endpoint endpoint_type
Definition PeerImp.h:108
-
void onMessage(std::shared_ptr< protocol::TMManifests > const &m)
Definition PeerImp.cpp:1035
+
void onMessage(std::shared_ptr< protocol::TMManifests > const &m)
Definition PeerImp.cpp:1034
ProtocolVersion protocol_
Definition PeerImp.h:138
bool txReduceRelayEnabled_
Definition PeerImp.h:245
-
void checkTransaction(HashRouterFlags flags, bool checkSignature, std::shared_ptr< STTx const > const &stx, bool batch)
Definition PeerImp.cpp:2643
+
void checkTransaction(HashRouterFlags flags, bool checkSignature, std::shared_ptr< STTx const > const &stx, bool batch)
Definition PeerImp.cpp:2642
std::shared_ptr< PeerFinder::Slot > const & slot()
Definition PeerImp.h:326
-
void close()
Forcibly closes the underlying socket connection.
Definition PeerImp.cpp:611
+
void close()
Forcibly closes the underlying socket connection.
Definition PeerImp.cpp:610
boost::beast::tcp_stream middle_type
Definition PeerImp.h:105
bool readPending_
Definition PeerImp.h:227
-
void handleTransaction(std::shared_ptr< protocol::TMTransaction > const &m, bool eraseTxQueue, bool batch)
Called from onMessage(TMTransaction(s)).
Definition PeerImp.cpp:1218
+
void handleTransaction(std::shared_ptr< protocol::TMTransaction > const &m, bool eraseTxQueue, bool batch)
Called from onMessage(TMTransaction(s)).
Definition PeerImp.cpp:1217
http_request_type request_
Definition PeerImp.h:215
void removeTxQueue(uint256 const &hash) override
Remove transaction's hash from the transactions' hashes queue.
Definition PeerImp.cpp:296
beast::WrappedSink sink_
Definition PeerImp.h:116
-
std::string name() const
Definition PeerImp.cpp:804
+
std::string name() const
Definition PeerImp.cpp:803
bool txReduceRelayEnabled() const override
Definition PeerImp.h:486
std::shared_ptr< PeerFinder::Slot > const slot_
Definition PeerImp.h:213
boost::asio::ip::tcp::socket socket_type
Definition PeerImp.h:104
boost::beast::http::fields const & headers_
Definition PeerImp.h:217
Compressed compressionEnabled_
Definition PeerImp.h:238
-
void onTimer(error_code const &ec)
Handles the expiration of the peer activity timer.
Definition PeerImp.cpp:661
+
void onTimer(error_code const &ec)
Handles the expiration of the peer activity timer.
Definition PeerImp.cpp:660
uint256 const & getClosedLedgerHash() const override
Definition PeerImp.h:452
boost::system::error_code error_code
Definition PeerImp.h:103
-
void addLedger(uint256 const &hash, std::lock_guard< std::mutex > const &lockedRecentLock)
Definition PeerImp.cpp:2549
+
void addLedger(uint256 const &hash, std::lock_guard< std::mutex > const &lockedRecentLock)
Definition PeerImp.cpp:2548
PeerImp & operator=(PeerImp const &)=delete
-
void tryAsyncShutdown()
Attempts to perform a graceful SSL shutdown if conditions are met.
Definition PeerImp.cpp:552
+
void tryAsyncShutdown()
Attempts to perform a graceful SSL shutdown if conditions are met.
Definition PeerImp.cpp:551
LedgerIndex minLedger_
Definition PeerImp.h:149
std::string prefix_
Definition PeerImp.h:115
void sendEndpoints(FwdIt first, FwdIt last)
Send a set of PeerFinder endpoints as a protocol message.
Definition PeerImp.h:849
@@ -865,13 +865,13 @@ $(document).ready(function() { init_codefold(0); });
reduce_relay::Squelch< UptimeClock > squelch_
Definition PeerImp.h:162
stream_type & stream_
Definition PeerImp.h:122
PeerImp(PeerImp const &)=delete
-
void checkValidation(std::shared_ptr< STValidation > const &val, uint256 const &key, std::shared_ptr< protocol::TMValidation > const &packet)
Definition PeerImp.cpp:2810
+
void checkValidation(std::shared_ptr< STValidation > const &val, uint256 const &key, std::shared_ptr< protocol::TMValidation > const &packet)
Definition PeerImp.cpp:2809
void cycleStatus() override
Definition PeerImp.cpp:500
std::shared_mutex nameMutex_
Definition PeerImp.h:145
socket_type & socket_
Definition PeerImp.h:121
beast::IP::Endpoint const remote_address_
Definition PeerImp.h:130
int large_sendq_
Definition PeerImp.h:232
-
std::string domain() const
Definition PeerImp.cpp:811
+
std::string domain() const
Definition PeerImp.cpp:810
std::atomic< Tracking > tracking_
Definition PeerImp.h:140
std::optional< std::size_t > publisherListSequence(PublicKey const &pubKey) const override
Definition PeerImp.h:429
void ledgerRange(std::uint32_t &minSeq, std::uint32_t &maxSeq) const override
Definition PeerImp.cpp:484
@@ -880,30 +880,30 @@ $(document).ready(function() { init_codefold(0); });
LedgerIndex maxLedger_
Definition PeerImp.h:150
Application & app_
Definition PeerImp.h:112
PublicKey const publicKey_
Definition PeerImp.h:143
-
void onMessageBegin(std::uint16_t type, std::shared_ptr<::google::protobuf::Message > const &m, std::size_t size, std::size_t uncompressed_size, bool isCompressed)
Definition PeerImp.cpp:991
+
void onMessageBegin(std::uint16_t type, std::shared_ptr<::google::protobuf::Message > const &m, std::size_t size, std::size_t uncompressed_size, bool isCompressed)
Definition PeerImp.cpp:990
Json::Value json() override
Definition PeerImp.cpp:343
bool cluster() const override
Returns true if this connection is a member of the cluster.
Definition PeerImp.cpp:329
Metrics recv
Definition PeerImp.h:282
-
void checkPropose(bool isTrusted, std::shared_ptr< protocol::TMProposeSet > const &packet, RCLCxPeerPos peerPos)
Definition PeerImp.cpp:2775
-
void onWriteMessage(error_code ec, std::size_t bytes_transferred)
Definition PeerImp.cpp:933
+
void checkPropose(bool isTrusted, std::shared_ptr< protocol::TMProposeSet > const &packet, RCLCxPeerPos peerPos)
Definition PeerImp.cpp:2774
+
void onWriteMessage(error_code ec, std::size_t bytes_transferred)
Definition PeerImp.cpp:932
virtual void run()
Definition PeerImp.cpp:129
OverlayImpl & overlay_
Definition PeerImp.h:134
std::queue< std::shared_ptr< Message > > send_queue_
Definition PeerImp.h:218
virtual ~PeerImp()
Definition PeerImp.cpp:106
-
void sendLedgerBase(std::shared_ptr< Ledger const > const &ledger, protocol::TMLedgerData &ledgerData)
Definition PeerImp.cpp:2897
+
void sendLedgerBase(std::shared_ptr< Ledger const > const &ledger, protocol::TMLedgerData &ledgerData)
Definition PeerImp.cpp:2896
Peer::id_t id() const override
Definition PeerImp.h:384
-
static std::string makePrefix(std::string const &fingerprint)
Definition PeerImp.cpp:653
+
static std::string makePrefix(std::string const &fingerprint)
Definition PeerImp.cpp:652
beast::Journal const & pJournal() const
Definition PeerImp.h:320
ChargeWithContext fee_
Definition PeerImp.h:212
-
void onShutdown(error_code ec)
Handles the completion of the asynchronous SSL shutdown.
Definition PeerImp.cpp:587
-
void handleHaveTransactions(std::shared_ptr< protocol::TMHaveTransactions > const &m)
Handle protocol message with hashes of transactions that have not been relayed by an upstream node do...
Definition PeerImp.cpp:2443
+
void onShutdown(error_code ec)
Handles the completion of the asynchronous SSL shutdown.
Definition PeerImp.cpp:586
+
void handleHaveTransactions(std::shared_ptr< protocol::TMHaveTransactions > const &m)
Handle protocol message with hashes of transactions that have not been relayed by an upstream node do...
Definition PeerImp.cpp:2442
beast::IP::Endpoint getRemoteAddress() const override
Definition PeerImp.h:371
-
void onMessageUnknown(std::uint16_t type)
Definition PeerImp.cpp:985
+
void onMessageUnknown(std::uint16_t type)
Definition PeerImp.cpp:984
protocol::TMStatusChange last_status_
Definition PeerImp.h:210
-
void onReadMessage(error_code ec, std::size_t bytes_transferred)
Definition PeerImp.cpp:854
+
void onReadMessage(error_code ec, std::size_t bytes_transferred)
Definition PeerImp.cpp:853
void setPublisherListSequence(PublicKey const &pubKey, std::size_t const seq) override
Definition PeerImp.h:440
bool compressionEnabled() const override
Definition PeerImp.h:480
-
void processLedgerRequest(std::shared_ptr< protocol::TMGetLedger > const &m)
Definition PeerImp.cpp:3047
+
void processLedgerRequest(std::shared_ptr< protocol::TMGetLedger > const &m)
Definition PeerImp.cpp:3046
Tracking
Whether the peer's view of the ledger converges or diverges from ours.
Definition PeerImp.h:99
@@ -916,18 +916,18 @@ $(document).ready(function() { init_codefold(0); });
LedgerReplayMsgHandler ledgerReplayMsgHandler_
Definition PeerImp.h:248
void send(std::shared_ptr< Message > const &m) override
Definition PeerImp.cpp:204
hash_set< uint256 > txQueue_
Definition PeerImp.h:243
-
void doFetchPack(std::shared_ptr< protocol::TMGetObjectByHash > const &packet)
Definition PeerImp.cpp:2562
+
void doFetchPack(std::shared_ptr< protocol::TMGetObjectByHash > const &packet)
Definition PeerImp.cpp:2561
bool supportsFeature(ProtocolFeature f) const override
Definition PeerImp.cpp:454
-
void doProtocolStart()
Definition PeerImp.cpp:821
-
void shutdown()
Initiates the peer disconnection sequence.
Definition PeerImp.cpp:572
+
void doProtocolStart()
Definition PeerImp.cpp:820
+
void shutdown()
Initiates the peer disconnection sequence.
Definition PeerImp.cpp:571
PublicKey const & getNodePublic() const override
Definition PeerImp.h:406
-
std::shared_ptr< Ledger const > getLedger(std::shared_ptr< protocol::TMGetLedger > const &m)
Definition PeerImp.cpp:2932
+
std::shared_ptr< Ledger const > getLedger(std::shared_ptr< protocol::TMGetLedger > const &m)
Definition PeerImp.cpp:2931
bool const inbound_
Definition PeerImp.h:135
boost::asio::strand< boost::asio::executor > strand_
Definition PeerImp.h:123
waitable_timer timer_
Definition PeerImp.h:126
clock_type::time_point lastPingTime_
Definition PeerImp.h:159
boost::circular_buffer< uint256 > recentLedgers_
Definition PeerImp.h:154
-
int getScore(bool haveItem) const override
Definition PeerImp.cpp:3210
+
int getScore(bool haveItem) const override
Definition PeerImp.cpp:3209
std::string name_
Definition PeerImp.h:144
bool hasTxSet(uint256 const &hash) const override
Definition PeerImp.cpp:493
bool writePending_
Definition PeerImp.h:230
@@ -936,10 +936,10 @@ $(document).ready(function() { init_codefold(0); });
clock_type::time_point trackingTime_
Definition PeerImp.h:141
beast::Journal const journal_
Definition PeerImp.h:118
std::string fingerprint_
Definition PeerImp.h:114
-
bool isHighLatency() const override
Definition PeerImp.cpp:3248
-
void cancelTimer() noexcept
Cancels any pending wait on the peer activity timer.
Definition PeerImp.cpp:724
+
bool isHighLatency() const override
Definition PeerImp.cpp:3247
+
void cancelTimer() noexcept
Cancels any pending wait on the peer activity timer.
Definition PeerImp.cpp:723
bool hasRange(std::uint32_t uMin, std::uint32_t uMax) override
Definition PeerImp.cpp:510
-
std::shared_ptr< SHAMap const > getTxSet(std::shared_ptr< protocol::TMGetLedger > const &m) const
Definition PeerImp.cpp:3015
+
std::shared_ptr< SHAMap const > getTxSet(std::shared_ptr< protocol::TMGetLedger > const &m) const
Definition PeerImp.cpp:3014
uint256 previousLedgerHash_
Definition PeerImp.h:152
Resource::Consumer usage_
Definition PeerImp.h:211
bool shutdownStarted_
Definition PeerImp.h:224
@@ -947,18 +947,18 @@ $(document).ready(function() { init_codefold(0); });
boost::beast::ssl_stream< middle_type > stream_type
Definition PeerImp.h:106
bool crawl() const
Returns true if this connection will publicly share its IP address.
Definition PeerImp.cpp:320
void stop() override
Definition PeerImp.cpp:184
-
void setTimer(std::chrono::seconds interval)
Sets and starts the peer timer.
Definition PeerImp.cpp:635
-
void onValidatorListMessage(std::string const &messageType, std::string const &manifest, std::uint32_t version, std::vector< ValidatorBlobInfo > const &blobs)
Definition PeerImp.cpp:1937
+
void setTimer(std::chrono::seconds interval)
Sets and starts the peer timer.
Definition PeerImp.cpp:634
+
void onValidatorListMessage(std::string const &messageType, std::string const &manifest, std::uint32_t version, std::vector< ValidatorBlobInfo > const &blobs)
Definition PeerImp.cpp:1936
bool shutdown_
Definition PeerImp.h:221
std::string const & prefix() const
Definition PeerImp.h:670
-
void doTransactions(std::shared_ptr< protocol::TMGetObjectByHash > const &packet)
Process peer's request to send missing transactions.
Definition PeerImp.cpp:2594
-
void doAccept()
Definition PeerImp.cpp:738
+
void doTransactions(std::shared_ptr< protocol::TMGetObjectByHash > const &packet)
Process peer's request to send missing transactions.
Definition PeerImp.cpp:2593
+
void doAccept()
Definition PeerImp.cpp:737
hash_map< PublicKey, std::size_t > publisherListSequences_
Definition PeerImp.h:236
void fail(std::string const &name, error_code ec)
Handles a failure associated with a specific error code.
Definition PeerImp.cpp:519
std::mutex recentLock_
Definition PeerImp.h:209
bool hasLedger(uint256 const &hash, std::uint32_t seq) const override
Definition PeerImp.cpp:471
boost::asio::basic_waitable_timer< std::chrono::steady_clock > waitable_timer
Definition PeerImp.h:109
-
void onMessageEnd(std::uint16_t type, std::shared_ptr<::google::protobuf::Message > const &m)
Definition PeerImp.cpp:1028
+
void onMessageEnd(std::uint16_t type, std::shared_ptr<::google::protobuf::Message > const &m)
Definition PeerImp.cpp:1027
Represents a peer connection in the overlay.
A public key.
Definition PublicKey.h:42
A peer's signed, proposed position for use in RCLConsensus.
diff --git a/PeerReservationTable_8cpp_source.html b/PeerReservationTable_8cpp_source.html index 36a683b017..f6f5c35fd7 100644 --- a/PeerReservationTable_8cpp_source.html +++ b/PeerReservationTable_8cpp_source.html @@ -228,9 +228,9 @@ $(document).ready(function() { init_codefold(0); });
STL namespace.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
-
void deletePeerReservation(soci::session &session, PublicKey const &nodeId)
deletePeerReservation Deletes an entry from the peer reservation table.
Definition Wallet.cpp:181
-
void insertPeerReservation(soci::session &session, PublicKey const &nodeId, std::string const &description)
insertPeerReservation Adds an entry to the peer reservation table.
Definition Wallet.cpp:170
-
std::unordered_set< PeerReservation, beast::uhash<>, KeyEqual > getPeerReservationTable(soci::session &session, beast::Journal j)
getPeerReservationTable Returns the peer reservation table.
Definition Wallet.cpp:135
+
void deletePeerReservation(soci::session &session, PublicKey const &nodeId)
deletePeerReservation Deletes an entry from the peer reservation table.
Definition Wallet.cpp:182
+
void insertPeerReservation(soci::session &session, PublicKey const &nodeId, std::string const &description)
insertPeerReservation Adds an entry to the peer reservation table.
Definition Wallet.cpp:171
+
std::unordered_set< PeerReservation, beast::uhash<>, KeyEqual > getPeerReservationTable(soci::session &session, beast::Journal j)
getPeerReservationTable Returns the peer reservation table.
Definition Wallet.cpp:136
T sort(T... args)
diff --git a/PerfLogImp_8cpp_source.html b/PerfLogImp_8cpp_source.html index 89ea390aef..8190beb669 100644 --- a/PerfLogImp_8cpp_source.html +++ b/PerfLogImp_8cpp_source.html @@ -599,7 +599,7 @@ $(document).ready(function() { init_codefold(0); });
Decorator for streaming out compact json.
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:324
diff --git a/PerfLog__test_8cpp_source.html b/PerfLog__test_8cpp_source.html index bd04bf4597..827e4522c9 100644 --- a/PerfLog__test_8cpp_source.html +++ b/PerfLog__test_8cpp_source.html @@ -1117,11 +1117,11 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
+
bool isArray() const
UInt size() const
Number of values in array or object.
-
bool isObject() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
A testsuite class.
Definition suite.h:51
log_os< char > log
Logging output stream.
Definition suite.h:144
diff --git a/PermissionedDEX__test_8cpp_source.html b/PermissionedDEX__test_8cpp_source.html index 74f3ce82b8..75cdc0cae7 100644 --- a/PermissionedDEX__test_8cpp_source.html +++ b/PermissionedDEX__test_8cpp_source.html @@ -1472,7 +1472,7 @@ $(document).ready(function() { init_codefold(0); });
std::map< uint256, Json::Value > getObjects(Account const &account, Env &env, bool withType)
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
std::uint32_t ownerCount(Env const &env, Account const &account)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:23
FeatureBitset testable_amendments()
Definition Env.h:76
diff --git a/PermissionedDomainDelete_8cpp_source.html b/PermissionedDomainDelete_8cpp_source.html index 42524c2c7b..14b87d8230 100644 --- a/PermissionedDomainDelete_8cpp_source.html +++ b/PermissionedDomainDelete_8cpp_source.html @@ -158,7 +158,7 @@ $(document).ready(function() { init_codefold(0); });
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
-
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1041
+
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1047
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
uint256 getFieldH256(SField const &field) const
Definition STObject.cpp:606
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:618
@@ -175,7 +175,7 @@ $(document).ready(function() { init_codefold(0); });
@ temMALFORMED
Definition TER.h:67
@ tecNO_ENTRY
Definition TER.h:287
@ tecNO_PERMISSION
Definition TER.h:286
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
uint256 key
Definition Keylet.h:20
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
diff --git a/PermissionedDomainSet_8cpp_source.html b/PermissionedDomainSet_8cpp_source.html index 423aae945d..21b3d49281 100644 --- a/PermissionedDomainSet_8cpp_source.html +++ b/PermissionedDomainSet_8cpp_source.html @@ -224,7 +224,7 @@ $(document).ready(function() { init_codefold(0); });
XRPAmount xrp() const
Definition STAmount.cpp:249
void push_back(STObject const &object)
Definition STArray.h:187
-
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1041
+
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1047
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:576
STArray const & getFieldArray(SField const &field) const
Definition STObject.cpp:663
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
@@ -247,7 +247,7 @@ $(document).ready(function() { init_codefold(0); });
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:955
std::size_t constexpr maxPermissionedDomainCredentialsArraySize
The maximum number of credentials can be passed in array for permissioned domain.
Definition Protocol.h:227
@ temMALFORMED
Definition TER.h:67
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecDIR_FULL
Definition TER.h:268
@ tecNO_ENTRY
Definition TER.h:287
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
diff --git a/PermissionedDomains__test_8cpp_source.html b/PermissionedDomains__test_8cpp_source.html index df63438759..92ac40138f 100644 --- a/PermissionedDomains__test_8cpp_source.html +++ b/PermissionedDomains__test_8cpp_source.html @@ -674,7 +674,7 @@ $(document).ready(function() { init_codefold(0); });
Credentials sortCredentials(Credentials const &input)
std::map< uint256, Json::Value > getObjects(Account const &account, Env &env, bool withType)
bool objectExists(uint256 const &objID, Env &env)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
diff --git a/Print_8cpp_source.html b/Print_8cpp_source.html index 6f5fd02932..7a34e0fa31 100644 --- a/Print_8cpp_source.html +++ b/Print_8cpp_source.html @@ -111,9 +111,9 @@ $(document).ready(function() { init_codefold(0); });
26
27} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
bool isString() const
-
bool isObject() const
+
bool isArray() const
+
bool isString() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
void write(PropertyStream &stream)
write this source and all its children recursively to the stream.
A PropertyStream::Sink which produces a Json::Value of type objectValue.
diff --git a/RPCCall_8cpp_source.html b/RPCCall_8cpp_source.html index 9d5639442f..2afd814664 100644 --- a/RPCCall_8cpp_source.html +++ b/RPCCall_8cpp_source.html @@ -1863,18 +1863,18 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
const_iterator begin() const
-
bool isArray() const
-
Value & append(Value const &value)
Append value to array at the end.
+
const_iterator begin() const
+
bool isArray() const
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
const_iterator end() const
-
bool isObjectOrNull() const
+
const_iterator end() const
+
bool isObjectOrNull() const
Int asInt() const
UInt asUInt() const
-
bool isObject() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
-
bool isNull() const
isNull() tests to see if this field is null.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isNull() const
isNull() tests to see if this field is null.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:300
Stream trace() const
Severity stream access functions.
Definition Journal.h:294
diff --git a/RPCErr_8cpp_source.html b/RPCErr_8cpp_source.html index ca8ec9a925..45fcb4f8eb 100644 --- a/RPCErr_8cpp_source.html +++ b/RPCErr_8cpp_source.html @@ -112,8 +112,8 @@ $(document).ready(function() { init_codefold(0); });
25
26} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isObject() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isObject() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
void inject_error(error_code_i code, Json::Value &json)
Add or update the json update to reflect the error code.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/RPCHelpers_8cpp_source.html b/RPCHelpers_8cpp_source.html index 567b053965..e1bbecc9d3 100644 --- a/RPCHelpers_8cpp_source.html +++ b/RPCHelpers_8cpp_source.html @@ -474,10 +474,10 @@ $(document).ready(function() { init_codefold(0); });
373} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isString() const
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
-
bool isNull() const
isNull() tests to see if this field is null.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isNull() const
isNull() tests to see if this field is null.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A view into a ledger.
Definition ReadView.h:31
Seeds are used to generate deterministic secret keys.
Definition Seed.h:14
An immutable linear range of bytes.
Definition Slice.h:26
diff --git a/RPCLedgerHelpers_8cpp_source.html b/RPCLedgerHelpers_8cpp_source.html index 45bbde448a..0889b3df46 100644 --- a/RPCLedgerHelpers_8cpp_source.html +++ b/RPCLedgerHelpers_8cpp_source.html @@ -453,108 +453,109 @@ $(document).ready(function() { init_codefold(0); });
355 if ((hasHash + hasIndex) != 1)
356 {
357 return Unexpected(
-
358 RPC::make_param_error("Exactly one of 'ledger_hash' or "
-
359 "'ledger_index' can be specified."));
-
360 }
-
361
-
362 if (hasHash)
-
363 {
-
364 auto const& jsonHash = context.params.get(jss::ledger_hash, Json::nullValue);
-
365 if (!jsonHash.isString() || !ledgerHash.parseHex(jsonHash.asString()))
-
366 return Unexpected(RPC::expected_field_error(jss::ledger_hash, "hex string"));
-
367 }
-
368 else
-
369 {
-
370 auto const& jsonIndex = context.params.get(jss::ledger_index, Json::nullValue);
-
371 if (!jsonIndex.isInt() && !jsonIndex.isUInt())
-
372 return Unexpected(RPC::expected_field_error(jss::ledger_index, "number"));
-
373
-
374 // We need a validated ledger to get the hash from the sequence
-
375 if (ledgerMaster.getValidatedLedgerAge() > RPC::Tuning::maxValidatedLedgerAge)
-
376 {
-
377 if (context.apiVersion == 1)
- - -
380 }
-
381
-
382 ledgerIndex = jsonIndex.asInt();
-
383 auto ledger = ledgerMaster.getValidatedLedger();
-
384
-
385 if (ledgerIndex >= ledger->header().seq)
-
386 return Unexpected(RPC::make_param_error("Ledger index too large"));
-
387 if (ledgerIndex <= 0)
-
388 return Unexpected(RPC::make_param_error("Ledger index too small"));
-
389
-
390 auto const j = context.app.journal("RPCHandler");
-
391 // Try to get the hash of the desired ledger from the validated
-
392 // ledger
-
393 auto neededHash = hashOfSeq(*ledger, ledgerIndex, j);
-
394 if (!neededHash)
-
395 {
-
396 // Find a ledger more likely to have the hash of the desired
-
397 // ledger
-
398 auto const refIndex = getCandidateLedger(ledgerIndex);
-
399 auto refHash = hashOfSeq(*ledger, refIndex, j);
-
400 XRPL_ASSERT(refHash, "xrpl::RPC::getOrAcquireLedger : nonzero ledger hash");
-
401
-
402 ledger = ledgerMaster.getLedgerByHash(*refHash);
-
403 if (!ledger)
-
404 {
-
405 // We don't have the ledger we need to figure out which
-
406 // ledger they want. Try to get it.
-
407
-
408 if (auto il =
-
409 context.app.getInboundLedgers().acquire(*refHash, refIndex, InboundLedger::Reason::GENERIC))
-
410 {
-
411 Json::Value jvResult =
-
412 RPC::make_error(rpcLGR_NOT_FOUND, "acquiring ledger containing requested index");
-
413 jvResult[jss::acquiring] = getJson(LedgerFill(*il, &context));
-
414 return Unexpected(jvResult);
-
415 }
-
416
-
417 if (auto il = context.app.getInboundLedgers().find(*refHash))
-
418 {
-
419 Json::Value jvResult =
-
420 RPC::make_error(rpcLGR_NOT_FOUND, "acquiring ledger containing requested index");
-
421 jvResult[jss::acquiring] = il->getJson(0);
-
422 return Unexpected(jvResult);
-
423 }
-
424
-
425 // Likely the app is shutting down
-
426 return Unexpected(Json::Value());
-
427 }
-
428
-
429 neededHash = hashOfSeq(*ledger, ledgerIndex, j);
-
430 }
-
431 XRPL_ASSERT(neededHash, "xrpl::RPC::getOrAcquireLedger : nonzero needed hash");
-
432 ledgerHash = neededHash ? *neededHash : beast::zero; // kludge
-
433 }
-
434
-
435 // Try to get the desired ledger
-
436 // Verify all nodes even if we think we have it
-
437 auto ledger = context.app.getInboundLedgers().acquire(ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
-
438
-
439 // In standalone mode, accept the ledger from the ledger cache
-
440 if (!ledger && context.app.config().standalone())
-
441 ledger = ledgerMaster.getLedgerByHash(ledgerHash);
-
442
-
443 if (ledger)
-
444 return ledger;
-
445
-
446 if (auto il = context.app.getInboundLedgers().find(ledgerHash))
-
447 return Unexpected(il->getJson(0));
-
448
-
449 return Unexpected(RPC::make_error(rpcNOT_READY, "findCreate failed to return an inbound ledger"));
-
450}
+ +
359 "Exactly one of 'ledger_hash' or "
+
360 "'ledger_index' can be specified."));
+
361 }
+
362
+
363 if (hasHash)
+
364 {
+
365 auto const& jsonHash = context.params.get(jss::ledger_hash, Json::nullValue);
+
366 if (!jsonHash.isString() || !ledgerHash.parseHex(jsonHash.asString()))
+
367 return Unexpected(RPC::expected_field_error(jss::ledger_hash, "hex string"));
+
368 }
+
369 else
+
370 {
+
371 auto const& jsonIndex = context.params.get(jss::ledger_index, Json::nullValue);
+
372 if (!jsonIndex.isInt() && !jsonIndex.isUInt())
+
373 return Unexpected(RPC::expected_field_error(jss::ledger_index, "number"));
+
374
+
375 // We need a validated ledger to get the hash from the sequence
+
376 if (ledgerMaster.getValidatedLedgerAge() > RPC::Tuning::maxValidatedLedgerAge)
+
377 {
+
378 if (context.apiVersion == 1)
+ + +
381 }
+
382
+
383 ledgerIndex = jsonIndex.asInt();
+
384 auto ledger = ledgerMaster.getValidatedLedger();
+
385
+
386 if (ledgerIndex >= ledger->header().seq)
+
387 return Unexpected(RPC::make_param_error("Ledger index too large"));
+
388 if (ledgerIndex <= 0)
+
389 return Unexpected(RPC::make_param_error("Ledger index too small"));
+
390
+
391 auto const j = context.app.journal("RPCHandler");
+
392 // Try to get the hash of the desired ledger from the validated
+
393 // ledger
+
394 auto neededHash = hashOfSeq(*ledger, ledgerIndex, j);
+
395 if (!neededHash)
+
396 {
+
397 // Find a ledger more likely to have the hash of the desired
+
398 // ledger
+
399 auto const refIndex = getCandidateLedger(ledgerIndex);
+
400 auto refHash = hashOfSeq(*ledger, refIndex, j);
+
401 XRPL_ASSERT(refHash, "xrpl::RPC::getOrAcquireLedger : nonzero ledger hash");
+
402
+
403 ledger = ledgerMaster.getLedgerByHash(*refHash);
+
404 if (!ledger)
+
405 {
+
406 // We don't have the ledger we need to figure out which
+
407 // ledger they want. Try to get it.
+
408
+
409 if (auto il =
+
410 context.app.getInboundLedgers().acquire(*refHash, refIndex, InboundLedger::Reason::GENERIC))
+
411 {
+
412 Json::Value jvResult =
+
413 RPC::make_error(rpcLGR_NOT_FOUND, "acquiring ledger containing requested index");
+
414 jvResult[jss::acquiring] = getJson(LedgerFill(*il, &context));
+
415 return Unexpected(jvResult);
+
416 }
+
417
+
418 if (auto il = context.app.getInboundLedgers().find(*refHash))
+
419 {
+
420 Json::Value jvResult =
+
421 RPC::make_error(rpcLGR_NOT_FOUND, "acquiring ledger containing requested index");
+
422 jvResult[jss::acquiring] = il->getJson(0);
+
423 return Unexpected(jvResult);
+
424 }
+
425
+
426 // Likely the app is shutting down
+
427 return Unexpected(Json::Value());
+
428 }
+
429
+
430 neededHash = hashOfSeq(*ledger, ledgerIndex, j);
+
431 }
+
432 XRPL_ASSERT(neededHash, "xrpl::RPC::getOrAcquireLedger : nonzero needed hash");
+
433 ledgerHash = neededHash ? *neededHash : beast::zero; // kludge
+
434 }
+
435
+
436 // Try to get the desired ledger
+
437 // Verify all nodes even if we think we have it
+
438 auto ledger = context.app.getInboundLedgers().acquire(ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
+
439
+
440 // In standalone mode, accept the ledger from the ledger cache
+
441 if (!ledger && context.app.config().standalone())
+
442 ledger = ledgerMaster.getLedgerByHash(ledgerHash);
+
443
+
444 if (ledger)
+
445 return ledger;
+
446
+
447 if (auto il = context.app.getInboundLedgers().find(ledgerHash))
+
448 return Unexpected(il->getJson(0));
+
449
+
450 return Unexpected(RPC::make_error(rpcNOT_READY, "findCreate failed to return an inbound ledger"));
+
451}
-
451
-
452} // namespace RPC
-
453} // namespace xrpl
+
452
+
453} // namespace RPC
+
454} // namespace xrpl
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
virtual Config & config()=0
bool standalone() const
Definition Config.h:312
diff --git a/RPCOverload__test_8cpp_source.html b/RPCOverload__test_8cpp_source.html index 3a5c077394..3e936604da 100644 --- a/RPCOverload__test_8cpp_source.html +++ b/RPCOverload__test_8cpp_source.html @@ -172,7 +172,7 @@ $(document).ready(function() { init_codefold(0); });
A transaction testing environment.
Definition Env.h:119
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
std::unique_ptr< Config > no_admin(std::unique_ptr< Config >)
adjust config so no admin ports are enabled
Definition envconfig.cpp:57
diff --git a/ReducedOffer__test_8cpp_source.html b/ReducedOffer__test_8cpp_source.html index 77557a3063..90dab45fc6 100644 --- a/ReducedOffer__test_8cpp_source.html +++ b/ReducedOffer__test_8cpp_source.html @@ -748,7 +748,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -783,7 +783,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:23
FeatureBitset testable_amendments()
Definition Env.h:76
diff --git a/Regression__test_8cpp_source.html b/Regression__test_8cpp_source.html index 4a4c5f3d51..4bd54421c6 100644 --- a/Regression__test_8cpp_source.html +++ b/Regression__test_8cpp_source.html @@ -408,7 +408,7 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
bool isObject() const
+
bool isObject() const
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
virtual Config & config()=0
@@ -454,7 +454,7 @@ $(document).ready(function() { init_codefold(0); });
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
Json::Value cash(jtx::Account const &dest, uint256 const &checkId, STAmount const &amount)
Cash a check requiring that a specific amount be delivered.
Definition check.cpp:14
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
static none_t const none
Definition tags.h:14
constexpr XRPAmount dropsPerXRP
diff --git a/Reservations_8cpp_source.html b/Reservations_8cpp_source.html index dd48460540..7faf8c3956 100644 --- a/Reservations_8cpp_source.html +++ b/Reservations_8cpp_source.html @@ -197,7 +197,7 @@ $(document).ready(function() { init_codefold(0); });
108} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
std::vector< PeerReservation > list() const
std::optional< PeerReservation > insert_or_assign(PeerReservation const &reservation)
std::optional< PeerReservation > erase(PublicKey const &nodeId)
diff --git a/RipplePathFind_8cpp_source.html b/RipplePathFind_8cpp_source.html index 6db82a7acd..7f8de7da69 100644 --- a/RipplePathFind_8cpp_source.html +++ b/RipplePathFind_8cpp_source.html @@ -238,8 +238,8 @@ $(document).ready(function() { init_codefold(0); });
153
154} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Members getMemberNames() const
Return a list of the member names.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Members getMemberNames() const
Return a list of the member names.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
int PATH_SEARCH_MAX
Definition Config.h:180
bool standalone() const
Definition Config.h:312
diff --git a/RobustTransaction__test_8cpp_source.html b/RobustTransaction__test_8cpp_source.html index b74e9c1f3a..3cbd9afdad 100644 --- a/RobustTransaction__test_8cpp_source.html +++ b/RobustTransaction__test_8cpp_source.html @@ -528,8 +528,8 @@ $(document).ready(function() { init_codefold(0); });
433} // namespace test
434} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value & append(Value const &value)
Append value to array at the end.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
virtual Config & config()=0
void rendezvous()
Block until no jobs running.
Definition JobQueue.cpp:229
@@ -550,7 +550,7 @@ $(document).ready(function() { init_codefold(0); });
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:319
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
@ arrayValue
array value (ordered list)
Definition json_value.h:25
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition WSClient.cpp:285
diff --git a/Role_8cpp_source.html b/Role_8cpp_source.html index 0d2759a69d..ade1338e2b 100644 --- a/Role_8cpp_source.html +++ b/Role_8cpp_source.html @@ -374,7 +374,7 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
bool isString() const
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
A version-independent IP address and port combination.
Definition IPEndpoint.h:18
Address const & address() const
Returns the address portion of this endpoint.
Definition IPEndpoint.h:55
diff --git a/Roles__test_8cpp_source.html b/Roles__test_8cpp_source.html index 5776891a64..b898b4c1f9 100644 --- a/Roles__test_8cpp_source.html +++ b/Roles__test_8cpp_source.html @@ -440,7 +440,7 @@ $(document).ready(function() { init_codefold(0); });
347} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
virtual Config & config()=0
diff --git a/SHAMapDelta_8cpp_source.html b/SHAMapDelta_8cpp_source.html index 75f4e7d5ef..6f96d07537 100644 --- a/SHAMapDelta_8cpp_source.html +++ b/SHAMapDelta_8cpp_source.html @@ -366,63 +366,64 @@ $(document).ready(function() { init_codefold(0); });
276 nodeStacks[rootChildIndex].push(intr_ptr::static_pointer_cast<SHAMapInnerNode>(child));
277
278 JLOG(journal_.debug()) << "starting worker " << rootChildIndex;
-
279 workers.push_back(std::thread(
-
280 [&m, &missingNodes, &maxMissing, &exceptions, this](
-
281 std::stack<StackEntry, std::vector<StackEntry>> nodeStack) {
-
282 try
-
283 {
-
284 while (!nodeStack.empty())
-
285 {
-
286 intr_ptr::SharedPtr<SHAMapInnerNode> node = std::move(nodeStack.top());
-
287 XRPL_ASSERT(node, "xrpl::SHAMap::walkMapParallel : non-null node");
-
288 nodeStack.pop();
-
289
-
290 for (int i = 0; i < 16; ++i)
-
291 {
-
292 if (node->isEmptyBranch(i))
-
293 continue;
-
294 intr_ptr::SharedPtr<SHAMapTreeNode> nextNode = descendNoStore(*node, i);
-
295
-
296 if (nextNode)
-
297 {
-
298 if (nextNode->isInner())
-
299 nodeStack.push(intr_ptr::static_pointer_cast<SHAMapInnerNode>(nextNode));
-
300 }
-
301 else
-
302 {
-
303 std::lock_guard l{m};
-
304 missingNodes.emplace_back(type_, node->getChildHash(i));
-
305 if (--maxMissing <= 0)
-
306 return;
-
307 }
-
308 }
-
309 }
-
310 }
-
311 catch (SHAMapMissingNode const& e)
-
312 {
-
313 std::lock_guard l(m);
-
314 exceptions.push_back(e);
-
315 }
-
316 },
-
317 std::move(nodeStacks[rootChildIndex])));
-
318 }
-
319
-
320 for (std::thread& worker : workers)
-
321 worker.join();
-
322
-
323 std::lock_guard l(m);
-
324 if (exceptions.empty())
-
325 return true;
- -
327 ss << "Exception(s) in ledger load: ";
-
328 for (auto const& e : exceptions)
-
329 ss << e.what() << ", ";
-
330 JLOG(journal_.error()) << ss.str();
-
331 return false;
-
332}
+
279 workers.push_back(
+ +
281 [&m, &missingNodes, &maxMissing, &exceptions, this](
+
282 std::stack<StackEntry, std::vector<StackEntry>> nodeStack) {
+
283 try
+
284 {
+
285 while (!nodeStack.empty())
+
286 {
+
287 intr_ptr::SharedPtr<SHAMapInnerNode> node = std::move(nodeStack.top());
+
288 XRPL_ASSERT(node, "xrpl::SHAMap::walkMapParallel : non-null node");
+
289 nodeStack.pop();
+
290
+
291 for (int i = 0; i < 16; ++i)
+
292 {
+
293 if (node->isEmptyBranch(i))
+
294 continue;
+
295 intr_ptr::SharedPtr<SHAMapTreeNode> nextNode = descendNoStore(*node, i);
+
296
+
297 if (nextNode)
+
298 {
+
299 if (nextNode->isInner())
+
300 nodeStack.push(intr_ptr::static_pointer_cast<SHAMapInnerNode>(nextNode));
+
301 }
+
302 else
+
303 {
+
304 std::lock_guard l{m};
+
305 missingNodes.emplace_back(type_, node->getChildHash(i));
+
306 if (--maxMissing <= 0)
+
307 return;
+
308 }
+
309 }
+
310 }
+
311 }
+
312 catch (SHAMapMissingNode const& e)
+
313 {
+
314 std::lock_guard l(m);
+
315 exceptions.push_back(e);
+
316 }
+
317 },
+
318 std::move(nodeStacks[rootChildIndex])));
+
319 }
+
320
+
321 for (std::thread& worker : workers)
+
322 worker.join();
+
323
+
324 std::lock_guard l(m);
+
325 if (exceptions.empty())
+
326 return true;
+ +
328 ss << "Exception(s) in ledger load: ";
+
329 for (auto const& e : exceptions)
+
330 ss << e.what() << ", ";
+
331 JLOG(journal_.error()) << ss.str();
+
332 return false;
+
333}
-
333
-
334} // namespace xrpl
+
334
+
335} // namespace xrpl
diff --git a/SHAMapStore__test_8cpp_source.html b/SHAMapStore__test_8cpp_source.html index 444fb7954d..cffcbb541d 100644 --- a/SHAMapStore__test_8cpp_source.html +++ b/SHAMapStore__test_8cpp_source.html @@ -734,7 +734,7 @@ $(document).ready(function() { init_codefold(0); });
T make_pair(T... args)
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/SHAMap__test_8cpp_source.html b/SHAMap__test_8cpp_source.html index 16fed83e83..6df7d79218 100644 --- a/SHAMap__test_8cpp_source.html +++ b/SHAMap__test_8cpp_source.html @@ -293,185 +293,209 @@ $(document).ready(function() { init_codefold(0); });
196 testcase("build/tear unbacked");
197 {
198 constexpr std::array keys{
-
199 uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
200 "5a772c6ca8"),
-
201 uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
202 "5a772c6ca8"),
-
203 uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
204 "5a772c6ca8"),
-
205 uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
206 "5a772c6ca8"),
-
207 uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
208 "5a772c6ca8"),
-
209 uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
210 "5a772c6ca8"),
-
211 uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
212 "5a772c6ca8"),
-
213 uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
214 "5a772c6ca8")};
-
215
-
216 constexpr std::array hashes{
-
217 uint256("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C"
-
218 "64B6281F7F"),
-
219 uint256("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546"
-
220 "7FB2ECE266"),
-
221 uint256("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A"
-
222 "E756795B75"),
-
223 uint256("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC"
-
224 "6C74F93E07"),
-
225 uint256("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596"
-
226 "462B0E3A3E"),
-
227 uint256("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9"
-
228 "8A84D9DDE4"),
-
229 uint256("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9"
-
230 "4361143615"),
-
231 uint256("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0"
-
232 "2BBE7230E5")};
-
233
-
234 SHAMap map(SHAMapType::FREE, f);
-
235 if (!backed)
-
236 map.setUnbacked();
-
237
-
238 BEAST_EXPECT(map.getHash() == beast::zero);
-
239 for (int k = 0; k < keys.size(); ++k)
-
240 {
-
241 BEAST_EXPECT(map.addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(keys[k], IntToVUC(k))));
-
242 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
-
243 map.invariants();
-
244 }
-
245 for (int k = keys.size() - 1; k >= 0; --k)
-
246 {
-
247 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
-
248 BEAST_EXPECT(map.delItem(keys[k]));
-
249 map.invariants();
-
250 }
-
251 BEAST_EXPECT(map.getHash() == beast::zero);
-
252 }
+
199 uint256(
+
200 "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
201 "5a772c6ca8"),
+
202 uint256(
+
203 "b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
204 "5a772c6ca8"),
+
205 uint256(
+
206 "b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
207 "5a772c6ca8"),
+
208 uint256(
+
209 "b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
210 "5a772c6ca8"),
+
211 uint256(
+
212 "b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
213 "5a772c6ca8"),
+
214 uint256(
+
215 "b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
216 "5a772c6ca8"),
+
217 uint256(
+
218 "f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
219 "5a772c6ca8"),
+
220 uint256(
+
221 "292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
222 "5a772c6ca8")};
+
223
+
224 constexpr std::array hashes{
+
225 uint256(
+
226 "B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C"
+
227 "64B6281F7F"),
+
228 uint256(
+
229 "FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546"
+
230 "7FB2ECE266"),
+
231 uint256(
+
232 "4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A"
+
233 "E756795B75"),
+
234 uint256(
+
235 "7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC"
+
236 "6C74F93E07"),
+
237 uint256(
+
238 "395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596"
+
239 "462B0E3A3E"),
+
240 uint256(
+
241 "D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9"
+
242 "8A84D9DDE4"),
+
243 uint256(
+
244 "76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9"
+
245 "4361143615"),
+
246 uint256(
+
247 "DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0"
+
248 "2BBE7230E5")};
+
249
+
250 SHAMap map(SHAMapType::FREE, f);
+
251 if (!backed)
+
252 map.setUnbacked();
253
-
254 if (backed)
-
255 testcase("iterate backed");
-
256 else
-
257 testcase("iterate unbacked");
-
258
-
259 {
-
260 constexpr std::array keys{
-
261 uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
262 "5a772c6ca8"),
-
263 uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
264 "5a772c6ca8"),
-
265 uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
266 "5a772c6ca8"),
-
267 uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
268 "5a772c6ca8"),
-
269 uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
270 "5a772c6ca8"),
-
271 uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
272 "5a772c6ca8"),
-
273 uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
274 "5a772c6ca8"),
-
275 uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
-
276 "5a772c6ca8")};
-
277
-
278 tests::TestNodeFamily tf{journal};
-
279 SHAMap map{SHAMapType::FREE, tf};
-
280 if (!backed)
-
281 map.setUnbacked();
-
282 for (auto const& k : keys)
-
283 {
- -
285 map.invariants();
-
286 }
-
287
-
288 int h = 7;
-
289 for (auto const& k : map)
-
290 {
-
291 BEAST_EXPECT(k.key() == keys[h]);
-
292 --h;
-
293 }
-
294 }
-
295 }
+
254 BEAST_EXPECT(map.getHash() == beast::zero);
+
255 for (int k = 0; k < keys.size(); ++k)
+
256 {
+
257 BEAST_EXPECT(map.addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(keys[k], IntToVUC(k))));
+
258 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
+
259 map.invariants();
+
260 }
+
261 for (int k = keys.size() - 1; k >= 0; --k)
+
262 {
+
263 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
+
264 BEAST_EXPECT(map.delItem(keys[k]));
+
265 map.invariants();
+
266 }
+
267 BEAST_EXPECT(map.getHash() == beast::zero);
+
268 }
+
269
+
270 if (backed)
+
271 testcase("iterate backed");
+
272 else
+
273 testcase("iterate unbacked");
+
274
+
275 {
+
276 constexpr std::array keys{
+
277 uint256(
+
278 "f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
279 "5a772c6ca8"),
+
280 uint256(
+
281 "b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
282 "5a772c6ca8"),
+
283 uint256(
+
284 "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
285 "5a772c6ca8"),
+
286 uint256(
+
287 "b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
288 "5a772c6ca8"),
+
289 uint256(
+
290 "b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
291 "5a772c6ca8"),
+
292 uint256(
+
293 "b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
294 "5a772c6ca8"),
+
295 uint256(
+
296 "b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
297 "5a772c6ca8"),
+
298 uint256(
+
299 "292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
+
300 "5a772c6ca8")};
+
301
+
302 tests::TestNodeFamily tf{journal};
+
303 SHAMap map{SHAMapType::FREE, tf};
+
304 if (!backed)
+
305 map.setUnbacked();
+
306 for (auto const& k : keys)
+
307 {
+ +
309 map.invariants();
+
310 }
+
311
+
312 int h = 7;
+
313 for (auto const& k : map)
+
314 {
+
315 BEAST_EXPECT(k.key() == keys[h]);
+
316 --h;
+
317 }
+
318 }
+
319 }
-
296};
+
320};
-
297
-
- -
299{
-
300 void
-
-
301 run() override
-
302 {
-
303 test::SuiteJournal journal("SHAMapPathProof_test", *this);
-
304
-
305 tests::TestNodeFamily tf{journal};
-
306 SHAMap map{SHAMapType::FREE, tf};
-
307 map.setUnbacked();
-
308
-
309 uint256 key;
-
310 uint256 rootHash;
-
311 std::vector<Blob> goodPath;
-
312
-
313 for (unsigned char c = 1; c < 100; ++c)
-
314 {
-
315 uint256 k(c);
- -
317 map.invariants();
-
318
-
319 auto root = map.getHash().as_uint256();
-
320 auto path = map.getProofPath(k);
-
321 BEAST_EXPECT(path);
-
322 if (!path)
-
323 break;
-
324 BEAST_EXPECT(map.verifyProofPath(root, k, *path));
-
325 if (c == 1)
-
326 {
-
327 // extra node
-
328 path->insert(path->begin(), path->front());
-
329 BEAST_EXPECT(!map.verifyProofPath(root, k, *path));
-
330 // wrong key
-
331 uint256 wrongKey(c + 1);
-
332 BEAST_EXPECT(!map.getProofPath(wrongKey));
-
333 }
-
334 if (c == 99)
-
335 {
-
336 key = k;
-
337 rootHash = root;
-
338 goodPath = std::move(*path);
-
339 }
-
340 }
-
341
-
342 // still good
-
343 BEAST_EXPECT(map.verifyProofPath(rootHash, key, goodPath));
-
344 // empty path
-
345 std::vector<Blob> badPath;
-
346 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
-
347 // too long
-
348 badPath = goodPath;
-
349 badPath.push_back(goodPath.back());
-
350 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
-
351 // bad node
-
352 badPath.clear();
-
353 badPath.emplace_back(100, 100);
-
354 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
-
355 // bad node type
-
356 badPath.clear();
-
357 badPath.push_back(goodPath.front());
-
358 badPath.front().back()--; // change node type
-
359 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
-
360 // all inner
-
361 badPath.clear();
-
362 badPath = goodPath;
-
363 badPath.erase(badPath.begin());
-
364 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
-
365 }
+
321
+
+ +
323{
+
324 void
+
+
325 run() override
+
326 {
+
327 test::SuiteJournal journal("SHAMapPathProof_test", *this);
+
328
+
329 tests::TestNodeFamily tf{journal};
+
330 SHAMap map{SHAMapType::FREE, tf};
+
331 map.setUnbacked();
+
332
+
333 uint256 key;
+
334 uint256 rootHash;
+
335 std::vector<Blob> goodPath;
+
336
+
337 for (unsigned char c = 1; c < 100; ++c)
+
338 {
+
339 uint256 k(c);
+ +
341 map.invariants();
+
342
+
343 auto root = map.getHash().as_uint256();
+
344 auto path = map.getProofPath(k);
+
345 BEAST_EXPECT(path);
+
346 if (!path)
+
347 break;
+
348 BEAST_EXPECT(map.verifyProofPath(root, k, *path));
+
349 if (c == 1)
+
350 {
+
351 // extra node
+
352 path->insert(path->begin(), path->front());
+
353 BEAST_EXPECT(!map.verifyProofPath(root, k, *path));
+
354 // wrong key
+
355 uint256 wrongKey(c + 1);
+
356 BEAST_EXPECT(!map.getProofPath(wrongKey));
+
357 }
+
358 if (c == 99)
+
359 {
+
360 key = k;
+
361 rootHash = root;
+
362 goodPath = std::move(*path);
+
363 }
+
364 }
+
365
+
366 // still good
+
367 BEAST_EXPECT(map.verifyProofPath(rootHash, key, goodPath));
+
368 // empty path
+
369 std::vector<Blob> badPath;
+
370 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
+
371 // too long
+
372 badPath = goodPath;
+
373 badPath.push_back(goodPath.back());
+
374 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
+
375 // bad node
+
376 badPath.clear();
+
377 badPath.emplace_back(100, 100);
+
378 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
+
379 // bad node type
+
380 badPath.clear();
+
381 badPath.push_back(goodPath.front());
+
382 badPath.front().back()--; // change node type
+
383 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
+
384 // all inner
+
385 badPath.clear();
+
386 badPath = goodPath;
+
387 badPath.erase(badPath.begin());
+
388 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
+
389 }
-
366};
+
390};
-
367
-
368BEAST_DEFINE_TESTSUITE(SHAMap, shamap, xrpl);
-
369BEAST_DEFINE_TESTSUITE(SHAMapPathProof, shamap, xrpl);
-
370} // namespace tests
-
371} // namespace xrpl
+
391
+
392BEAST_DEFINE_TESTSUITE(SHAMap, shamap, xrpl);
+
393BEAST_DEFINE_TESTSUITE(SHAMapPathProof, shamap, xrpl);
+
394} // namespace tests
+
395} // namespace xrpl
T back(T... args)
T begin(T... args)
@@ -502,8 +526,8 @@ $(document).ready(function() { init_codefold(0); });
pointer data()
Definition base_uint.h:101
static constexpr std::size_t size()
Definition base_uint.h:494
- -
void run() override
Runs the suite.
+ +
void run() override
Runs the suite.
static Buffer IntToVUC(int v)
void run() override
Runs the suite.
@@ -530,7 +554,7 @@ $(document).ready(function() { init_codefold(0); });
boost::intrusive_ptr< SHAMapItem > make_shamapitem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:137
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
base_uint< 256 > uint256
Definition base_uint.h:526
T push_back(T... args)
diff --git a/SQLiteDatabase_8cpp_source.html b/SQLiteDatabase_8cpp_source.html index be1144aef3..b01ff2ea88 100644 --- a/SQLiteDatabase_8cpp_source.html +++ b/SQLiteDatabase_8cpp_source.html @@ -835,27 +835,27 @@ $(document).ready(function() { init_codefold(0); });
void deleteBeforeLedgerSeq(soci::session &session, TableType type, LedgerIndex ledgerSeq)
deleteBeforeLedgerSeq Deletes all entries in given table for the ledgers with given sequence and all ...
Definition Node.cpp:126
std::optional< LedgerIndex > getMinLedgerSeq(soci::session &session, TableType type)
getMinLedgerSeq Returns minimum ledger sequence in given table.
Definition Node.cpp:100
std::optional< LedgerIndex > getMaxLedgerSeq(soci::session &session, TableType type)
getMaxLedgerSeq Returns maximum ledger sequence in given table.
Definition Node.cpp:110
-
std::pair< RelationalDatabase::AccountTxs, int > getNewestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxs Returns newest transactions for given account which match given criteria starting...
Definition Node.cpp:775
+
std::pair< RelationalDatabase::AccountTxs, int > getNewestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxs Returns newest transactions for given account which match given criteria starting...
Definition Node.cpp:778
std::pair< std::vector< std::shared_ptr< Transaction > >, int > getTxHistory(soci::session &session, Application &app, LedgerIndex startIndex, int quantity)
getTxHistory Returns given number of most recent transactions starting from given number of entry.
Definition Node.cpp:550
-
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getOldestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxsB Returns oldest transactions in binary form for given account which match given c...
Definition Node.cpp:857
+
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getOldestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxsB Returns oldest transactions in binary form for given account which match given c...
Definition Node.cpp:860
std::optional< LedgerHeader > getNewestLedgerInfo(soci::session &session, beast::Journal j)
getNewestLedgerInfo Returns info of newest saved ledger.
Definition Node.cpp:425
std::optional< LedgerHeader > getLimitedOldestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedOldestLedgerInfo Returns info of oldest ledger from ledgers with sequences greater or equal...
Definition Node.cpp:433
-
std::variant< RelationalDatabase::AccountTx, TxSearched > getTransaction(soci::session &session, Application &app, uint256 const &id, std::optional< ClosedInterval< uint32_t > > const &range, error_code_i &ec)
getTransaction Returns transaction with given hash.
Definition Node.cpp:1061
+
std::variant< RelationalDatabase::AccountTx, TxSearched > getTransaction(soci::session &session, Application &app, uint256 const &id, std::optional< ClosedInterval< uint32_t > > const &range, error_code_i &ec)
getTransaction Returns transaction with given hash.
Definition Node.cpp:1064
DatabasePairValid makeLedgerDBs(Config const &config, DatabaseCon::Setup const &setup, DatabaseCon::CheckpointerSetup const &checkpointerSetup, beast::Journal j)
makeLedgerDBs Opens ledger and transactions databases.
Definition Node.cpp:49
RelationalDatabase::CountMinMax getRowsMinMax(soci::session &session, TableType type)
getRowsMinMax Returns minimum ledger sequence, maximum ledger sequence and total number of rows in gi...
Definition Node.cpp:144
std::size_t getRows(soci::session &session, TableType type)
getRows Returns number of rows in given table.
Definition Node.cpp:132
std::optional< LedgerHeader > getLedgerInfoByIndex(soci::session &session, LedgerIndex ledgerSeq, beast::Journal j)
getLedgerInfoByIndex Returns ledger by its sequence.
Definition Node.cpp:417
-
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getNewestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxsB Returns newest transactions in binary form for given account which match given c...
Definition Node.cpp:867
+
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getNewestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxsB Returns newest transactions in binary form for given account which match given c...
Definition Node.cpp:870
uint256 getHashByIndex(soci::session &session, LedgerIndex ledgerIndex)
getHashByIndex Returns hash of ledger with given sequence.
Definition Node.cpp:457
void deleteByLedgerSeq(soci::session &session, TableType type, LedgerIndex ledgerSeq)
deleteByLedgerSeq Deletes all entries in given table for the ledger with given sequence.
Definition Node.cpp:120
-
std::pair< RelationalDatabase::AccountTxs, int > getOldestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxs Returns oldest transactions for given account which match given criteria starting...
Definition Node.cpp:764
-
bool dbHasSpace(soci::session &session, Config const &config, beast::Journal j)
dbHasSpace Checks if given database has available space.
Definition Node.cpp:1136
+
std::pair< RelationalDatabase::AccountTxs, int > getOldestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxs Returns oldest transactions for given account which match given criteria starting...
Definition Node.cpp:767
+
bool dbHasSpace(soci::session &session, Config const &config, beast::Journal j)
dbHasSpace Checks if given database has available space.
Definition Node.cpp:1139
std::optional< LedgerHeader > getLimitedNewestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedNewestLedgerInfo Returns info of newest ledger from ledgers with sequences greater or equal...
Definition Node.cpp:441
-
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > oldestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
oldestAccountTxPage Searches oldest transactions for given account which match given criteria startin...
Definition Node.cpp:1039
-
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > newestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
newestAccountTxPage Searches newest transactions for given account which match given criteria startin...
Definition Node.cpp:1050
+
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > oldestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
oldestAccountTxPage Searches oldest transactions for given account which match given criteria startin...
Definition Node.cpp:1042
+
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > newestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t page_length)
newestAccountTxPage Searches newest transactions for given account which match given criteria startin...
Definition Node.cpp:1053
std::optional< LedgerHashPair > getHashesByIndex(soci::session &session, LedgerIndex ledgerIndex, beast::Journal j)
getHashesByIndex Returns hash of the ledger and hash of parent ledger for the ledger of given sequenc...
Definition Node.cpp:486
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void saveLedgerAsync(Application &app, std::uint32_t seq)
diff --git a/STAmount_8cpp_source.html b/STAmount_8cpp_source.html index 60940ff8f9..8a93eeb742 100644 --- a/STAmount_8cpp_source.html +++ b/STAmount_8cpp_source.html @@ -1810,19 +1810,19 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
bool isObjectOrNull() const
+
bool isArray() const
+
bool isObjectOrNull() const
Int asInt() const
UInt asAbsUInt() const
Correct absolute value from int or unsigned int.
-
bool isString() const
+
bool isString() const
UInt asUInt() const
-
bool isObject() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
-
bool isUInt() const
-
bool isNull() const
isNull() tests to see if this field is null.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
-
bool isInt() const
+
bool isUInt() const
+
bool isNull() const
isNull() tests to see if this field is null.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
bool isInt() const
constexpr TIss const & get() const
std::string getText() const
Definition Asset.cpp:23
@@ -1830,14 +1830,14 @@ $(document).ready(function() { init_codefold(0); });
void setJson(Json::Value &jv) const
Definition Asset.cpp:29
constexpr bool holds() const
Definition Asset.h:130
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
-
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:161
-
exponent_type exponent() const noexcept
Definition IOUAmount.h:155
+
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:163
+
exponent_type exponent() const noexcept
Definition IOUAmount.h:157
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
AccountID account
Definition Issue.h:16
bool native() const
Definition Issue.cpp:47
-
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:113
+
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:114
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:26
@@ -1857,7 +1857,7 @@ $(document).ready(function() { init_codefold(0); });
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
Issue const & issue() const
Definition STAmount.h:454
static std::uint64_t const uRateOne
Definition STAmount.h:62
-
static STAmount fromNumber(A const &asset, Number const &number)
Definition STAmount.h:513
+
static STAmount fromNumber(A const &asset, Number const &number)
Definition STAmount.h:515
void add(Serializer &s) const override
Definition STAmount.cpp:729
void canonicalize()
Definition STAmount.cpp:794
std::uint64_t mantissa() const noexcept
Definition STAmount.h:435
@@ -1876,7 +1876,7 @@ $(document).ready(function() { init_codefold(0); });
bool isDefault() const override
Definition STAmount.cpp:770
bool integral() const noexcept
Definition STAmount.h:410
mantissa_type mValue
Definition STAmount.h:38
-
STAmount & operator=(beast::Zero)
Definition STAmount.h:498
+
STAmount & operator=(beast::Zero)
Definition STAmount.h:500
static std::unique_ptr< STAmount > construct(SerialIter &, SField const &name)
Definition STAmount.cpp:226
static constexpr std::uint64_t cIssuedCurrency
Definition STAmount.h:57
bool native() const noexcept
Definition STAmount.h:416
@@ -1892,7 +1892,7 @@ $(document).ready(function() { init_codefold(0); });
STBase * move(std::size_t n, void *buf) override
Definition STAmount.cpp:238
XRPAmount xrp() const
Definition STAmount.cpp:249
static constexpr std::uint64_t cPositive
Definition STAmount.h:58
-
STAmount const & value() const noexcept
Definition STAmount.h:560
+
STAmount const & value() const noexcept
Definition STAmount.h:562
STAmount(SerialIter &sit, SField const &name)
Definition STAmount.cpp:100
A type which can be exported to a well known binary format.
Definition STBase.h:115
SField const & getFName() const
Definition STBase.cpp:122
diff --git a/STAmount_8h_source.html b/STAmount_8h_source.html index 20cce8543c..2bb23ac22d 100644 --- a/STAmount_8h_source.html +++ b/STAmount_8h_source.html @@ -614,269 +614,271 @@ $(document).ready(function() { init_codefold(0); });
482
-
483inline STAmount::operator bool() const noexcept
-
484{
-
485 return *this != beast::zero;
-
486}
+
483inline STAmount::
+
484operator bool() const noexcept
+
485{
+
486 return *this != beast::zero;
+
487}
-
487
-
-
488inline STAmount::operator Number() const
-
489{
-
490 if (native())
-
491 return xrp();
-
492 if (mAsset.holds<MPTIssue>())
-
493 return mpt();
-
494 return iou();
-
495}
+
488
+
+
489inline STAmount::
+
490operator Number() const
+
491{
+
492 if (native())
+
493 return xrp();
+
494 if (mAsset.holds<MPTIssue>())
+
495 return mpt();
+
496 return iou();
+
497}
-
496
-
497inline STAmount&
-
- -
499{
-
500 clear();
-
501 return *this;
-
502}
+
498
+
499inline STAmount&
+
+ +
501{
+
502 clear();
+
503 return *this;
+
504}
-
503
-
504inline STAmount&
-
- -
506{
-
507 *this = STAmount(amount);
-
508 return *this;
-
509}
+
505
+
506inline STAmount&
+
+ +
508{
+
509 *this = STAmount(amount);
+
510 return *this;
+
511}
-
510
-
511template <AssetType A>
-
512inline STAmount
-
-
513STAmount::fromNumber(A const& a, Number const& number)
-
514{
-
515 bool const negative = number.mantissa() < 0;
-
516 Number const working{negative ? -number : number};
-
517 Asset asset{a};
-
518 if (asset.integral())
-
519 {
-
520 std::uint64_t const intValue = static_cast<std::int64_t>(working);
-
521 return STAmount{asset, intValue, 0, negative};
-
522 }
-
523
-
524 auto const [mantissa, exponent] = working.normalizeToRange(cMinValue, cMaxValue);
+
512
+
513template <AssetType A>
+
514inline STAmount
+
+
515STAmount::fromNumber(A const& a, Number const& number)
+
516{
+
517 bool const negative = number.mantissa() < 0;
+
518 Number const working{negative ? -number : number};
+
519 Asset asset{a};
+
520 if (asset.integral())
+
521 {
+
522 std::uint64_t const intValue = static_cast<std::int64_t>(working);
+
523 return STAmount{asset, intValue, 0, negative};
+
524 }
525
- -
527}
+
526 auto const [mantissa, exponent] = working.normalizeToRange(cMinValue, cMaxValue);
+
527
+ +
529}
-
528
-
529inline void
-
- -
531{
-
532 if (*this != beast::zero)
- -
534}
+
530
+
531inline void
+
+ +
533{
+
534 if (*this != beast::zero)
+ +
536}
-
535
-
536inline void
-
- -
538{
-
539 // The -100 is used to allow 0 to sort less than a small positive values
-
540 // which have a negative exponent.
-
541 mOffset = integral() ? 0 : -100;
-
542 mValue = 0;
-
543 mIsNegative = false;
-
544}
+
537
+
538inline void
+
+ +
540{
+
541 // The -100 is used to allow 0 to sort less than a small positive values
+
542 // which have a negative exponent.
+
543 mOffset = integral() ? 0 : -100;
+
544 mValue = 0;
+
545 mIsNegative = false;
+
546}
-
545
-
546inline void
-
- -
548{
- -
550 clear();
-
551}
+
547
+
548inline void
+
+ +
550{
+ +
552 clear();
+
553}
-
552
-
553inline void
-
- -
555{
-
556 mAsset.get<Issue>().account = uIssuer;
-
557}
+
554
+
555inline void
+
+ +
557{
+
558 mAsset.get<Issue>().account = uIssuer;
+
559}
-
558
-
559inline STAmount const&
-
-
560STAmount::value() const noexcept
-
561{
-
562 return *this;
-
563}
+
560
+
561inline STAmount const&
+
+
562STAmount::value() const noexcept
+
563{
+
564 return *this;
+
565}
-
564
-
565inline bool
-
-
566isLegalNet(STAmount const& value)
-
567{
-
568 return !value.native() || (value.mantissa() <= STAmount::cMaxNativeN);
-
569}
+
566
+
567inline bool
+
+
568isLegalNet(STAmount const& value)
+
569{
+
570 return !value.native() || (value.mantissa() <= STAmount::cMaxNativeN);
+
571}
-
570
-
571//------------------------------------------------------------------------------
-
572//
-
573// Operators
+
572
+
573//------------------------------------------------------------------------------
574//
-
575//------------------------------------------------------------------------------
-
576
-
577bool
-
578operator==(STAmount const& lhs, STAmount const& rhs);
+
575// Operators
+
576//
+
577//------------------------------------------------------------------------------
+
578
579bool
-
580operator<(STAmount const& lhs, STAmount const& rhs);
-
581
-
582inline bool
-
-
583operator!=(STAmount const& lhs, STAmount const& rhs)
-
584{
-
585 return !(lhs == rhs);
-
586}
+
580operator==(STAmount const& lhs, STAmount const& rhs);
+
581bool
+
582operator<(STAmount const& lhs, STAmount const& rhs);
+
583
+
584inline bool
+
+
585operator!=(STAmount const& lhs, STAmount const& rhs)
+
586{
+
587 return !(lhs == rhs);
+
588}
-
587
-
588inline bool
-
-
589operator>(STAmount const& lhs, STAmount const& rhs)
-
590{
-
591 return rhs < lhs;
-
592}
+
589
+
590inline bool
+
+
591operator>(STAmount const& lhs, STAmount const& rhs)
+
592{
+
593 return rhs < lhs;
+
594}
-
593
-
-
594inline bool
-
595operator<=(STAmount const& lhs, STAmount const& rhs)
-
596{
-
597 return !(rhs < lhs);
-
598}
+
595
+
+
596inline bool
+
597operator<=(STAmount const& lhs, STAmount const& rhs)
+
598{
+
599 return !(rhs < lhs);
+
600}
-
599
-
600inline bool
-
-
601operator>=(STAmount const& lhs, STAmount const& rhs)
-
602{
-
603 return !(lhs < rhs);
-
604}
+
601
+
602inline bool
+
+
603operator>=(STAmount const& lhs, STAmount const& rhs)
+
604{
+
605 return !(lhs < rhs);
+
606}
-
605
-
606STAmount
-
607operator-(STAmount const& value);
-
608
-
609//------------------------------------------------------------------------------
-
610//
-
611// Arithmetic
+
607
+
608STAmount
+
609operator-(STAmount const& value);
+
610
+
611//------------------------------------------------------------------------------
612//
-
613//------------------------------------------------------------------------------
-
614
-
615STAmount
-
616operator+(STAmount const& v1, STAmount const& v2);
+
613// Arithmetic
+
614//
+
615//------------------------------------------------------------------------------
+
616
617STAmount
-
618operator-(STAmount const& v1, STAmount const& v2);
-
619
-
620STAmount
-
621divide(STAmount const& v1, STAmount const& v2, Asset const& asset);
-
622
-
623STAmount
-
624multiply(STAmount const& v1, STAmount const& v2, Asset const& asset);
-
625
-
626// multiply rounding result in specified direction
-
627STAmount
-
628mulRound(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
-
629
-
630// multiply following the rounding directions more precisely.
-
631STAmount
-
632mulRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
-
633
-
634// divide rounding result in specified direction
-
635STAmount
-
636divRound(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
-
637
-
638// divide following the rounding directions more precisely.
-
639STAmount
-
640divRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
-
641
-
642// Someone is offering X for Y, what is the rate?
-
643// Rate: smaller is better, the taker wants the most out: in/out
-
644// VFALCO TODO Return a Quality object
- -
646getRate(STAmount const& offerOut, STAmount const& offerIn);
-
647
-
660[[nodiscard]] STAmount
-
661roundToScale(STAmount const& value, std::int32_t scale, Number::rounding_mode rounding = Number::getround());
-
662
-
672template <AssetType A>
-
673void
-
-
674roundToAsset(A const& asset, Number& value)
-
675{
-
676 value = STAmount{asset, value};
-
677}
+
618operator+(STAmount const& v1, STAmount const& v2);
+
619STAmount
+
620operator-(STAmount const& v1, STAmount const& v2);
+
621
+
622STAmount
+
623divide(STAmount const& v1, STAmount const& v2, Asset const& asset);
+
624
+
625STAmount
+
626multiply(STAmount const& v1, STAmount const& v2, Asset const& asset);
+
627
+
628// multiply rounding result in specified direction
+
629STAmount
+
630mulRound(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
+
631
+
632// multiply following the rounding directions more precisely.
+
633STAmount
+
634mulRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
+
635
+
636// divide rounding result in specified direction
+
637STAmount
+
638divRound(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
+
639
+
640// divide following the rounding directions more precisely.
+
641STAmount
+
642divRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
+
643
+
644// Someone is offering X for Y, what is the rate?
+
645// Rate: smaller is better, the taker wants the most out: in/out
+
646// VFALCO TODO Return a Quality object
+ +
648getRate(STAmount const& offerOut, STAmount const& offerIn);
+
649
+
662[[nodiscard]] STAmount
+
663roundToScale(STAmount const& value, std::int32_t scale, Number::rounding_mode rounding = Number::getround());
+
664
+
674template <AssetType A>
+
675void
+
+
676roundToAsset(A const& asset, Number& value)
+
677{
+
678 value = STAmount{asset, value};
+
679}
-
678
-
690template <AssetType A>
-
691[[nodiscard]] Number
-
- -
693 A const& asset,
-
694 Number const& value,
-
695 std::int32_t scale,
- -
697{
-
698 NumberRoundModeGuard mg(rounding);
-
699 STAmount const ret{asset, value};
-
700 if (ret.integral())
-
701 return ret;
-
702 // Note that the ctor will round integral types (XRP, MPT) via canonicalize,
-
703 // so no extra work is needed for those.
-
704 return roundToScale(ret, scale);
-
705}
+
680
+
692template <AssetType A>
+
693[[nodiscard]] Number
+
+ +
695 A const& asset,
+
696 Number const& value,
+
697 std::int32_t scale,
+ +
699{
+
700 NumberRoundModeGuard mg(rounding);
+
701 STAmount const ret{asset, value};
+
702 if (ret.integral())
+
703 return ret;
+
704 // Note that the ctor will round integral types (XRP, MPT) via canonicalize,
+
705 // so no extra work is needed for those.
+
706 return roundToScale(ret, scale);
+
707}
-
706
-
707//------------------------------------------------------------------------------
708
-
709inline bool
-
-
710isXRP(STAmount const& amount)
-
711{
-
712 return amount.native();
-
713}
+
709//------------------------------------------------------------------------------
+
710
+
711inline bool
+
+
712isXRP(STAmount const& amount)
+
713{
+
714 return amount.native();
+
715}
-
714
-
715bool
-
716canAdd(STAmount const& amt1, STAmount const& amt2);
-
717
-
718bool
-
719canSubtract(STAmount const& amt1, STAmount const& amt2);
-
720
-
721} // namespace xrpl
+
716
+
717bool
+
718canAdd(STAmount const& amt1, STAmount const& amt2);
+
719
+
720bool
+
721canSubtract(STAmount const& amt1, STAmount const& amt2);
722
-
723//------------------------------------------------------------------------------
-
724namespace Json {
-
725template <>
-
726inline xrpl::STAmount
-
-
727getOrThrow(Json::Value const& v, xrpl::SField const& field)
-
728{
-
729 using namespace xrpl;
-
730 Json::StaticString const& key = field.getJsonName();
-
731 if (!v.isMember(key))
-
732 Throw<JsonMissingKeyError>(key);
-
733 Json::Value const& inner = v[key];
-
734 return amountFromJson(field, inner);
-
735}
+
723} // namespace xrpl
+
724
+
725//------------------------------------------------------------------------------
+
726namespace Json {
+
727template <>
+
728inline xrpl::STAmount
+
+
729getOrThrow(Json::Value const& v, xrpl::SField const& field)
+
730{
+
731 using namespace xrpl;
+
732 Json::StaticString const& key = field.getJsonName();
+
733 if (!v.isMember(key))
+
734 Throw<JsonMissingKeyError>(key);
+
735 Json::Value const& inner = v[key];
+
736 return amountFromJson(field, inner);
+
737}
-
736} // namespace Json
+
738} // namespace Json
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
constexpr TIss const & get() const
AccountID const & getIssuer() const
Definition Asset.cpp:17
@@ -905,10 +907,10 @@ $(document).ready(function() { init_codefold(0); });
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
Issue const & issue() const
Definition STAmount.h:454
static std::uint64_t const uRateOne
Definition STAmount.h:62
-
static STAmount fromNumber(A const &asset, Number const &number)
Definition STAmount.h:513
+
static STAmount fromNumber(A const &asset, Number const &number)
Definition STAmount.h:515
void add(Serializer &s) const override
Definition STAmount.cpp:729
-
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:554
-
void negate()
Definition STAmount.h:530
+
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:556
+
void negate()
Definition STAmount.h:532
void canonicalize()
Definition STAmount.cpp:794
std::uint64_t mantissa() const noexcept
Definition STAmount.h:435
int signum() const noexcept
Definition STAmount.h:472
@@ -929,7 +931,7 @@ $(document).ready(function() { init_codefold(0); });
bool isDefault() const override
Definition STAmount.cpp:770
bool integral() const noexcept
Definition STAmount.h:410
mantissa_type mValue
Definition STAmount.h:38
-
STAmount & operator=(beast::Zero)
Definition STAmount.h:498
+
STAmount & operator=(beast::Zero)
Definition STAmount.h:500
Currency const & getCurrency() const
Definition STAmount.h:460
static std::unique_ptr< STAmount > construct(SerialIter &, SField const &name)
Definition STAmount.cpp:226
static constexpr std::uint64_t cIssuedCurrency
Definition STAmount.h:57
@@ -948,10 +950,10 @@ $(document).ready(function() { init_codefold(0); });
STBase * move(std::size_t n, void *buf) override
Definition STAmount.cpp:238
XRPAmount xrp() const
Definition STAmount.cpp:249
static constexpr std::uint64_t cPositive
Definition STAmount.h:58
-
STAmount const & value() const noexcept
Definition STAmount.h:560
+
STAmount const & value() const noexcept
Definition STAmount.h:562
STAmount(SerialIter &sit, SField const &name)
Definition STAmount.cpp:100
STAmount(A const &asset, std::uint64_t mantissa=0, int exponent=0, bool negative=false)
Definition STAmount.h:103
- +
A type which can be exported to a well known binary format.
Definition STBase.h:115
@@ -967,14 +969,14 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
bool operator<(Slice const &lhs, Slice const &rhs) noexcept
Definition Slice.h:198
-
bool operator>=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:601
+
bool operator>=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:603
bool isXRP(AccountID const &c)
Definition AccountID.h:70
constexpr base_uint< Bits, Tag > operator+(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
Definition base_uint.h:589
Number operator-(Number const &x, Number const &y)
Definition Number.h:638
STAmount amountFromJson(SField const &name, Json::Value const &v)
Definition STAmount.cpp:948
STAmount mulRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
STAmount amountFromString(Asset const &asset, std::string const &amount)
Definition STAmount.cpp:939
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
@@ -987,12 +989,12 @@ $(document).ready(function() { init_codefold(0); });
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:434
constexpr bool isPowerOfTen(T value)
Definition Number.h:36
bool operator!=(Buffer const &lhs, Buffer const &rhs) noexcept
Definition Buffer.h:209
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
-
bool operator<=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:595
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
+
bool operator<=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:597
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Definition STAmount.cpp:545
STAmount divRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
-
bool operator>(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:589
+
bool operator>(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:591
constexpr std::enable_if_t< std::is_integral_v< Dest > &&std::is_integral_v< Src >, Dest > safe_cast(Src s) noexcept
Definition safe_cast.h:19
diff --git a/STArray_8cpp_source.html b/STArray_8cpp_source.html index c1cb4e4c98..bd55e8dc2f 100644 --- a/STArray_8cpp_source.html +++ b/STArray_8cpp_source.html @@ -298,7 +298,7 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Identifies fields.
Definition SField.h:126
static SField const & getField(int fieldCode)
Definition SField.cpp:94
diff --git a/STBitString_8h_source.html b/STBitString_8h_source.html index 315ae90c2a..7a8b4a3e8e 100644 --- a/STBitString_8h_source.html +++ b/STBitString_8h_source.html @@ -279,22 +279,23 @@ $(document).ready(function() { init_codefold(0); });
170
171template <int Bits>
- -
173{
-
174 return value_;
-
175}
+ +
173operator value_type() const
+
174{
+
175 return value_;
+
176}
-
176
-
177template <int Bits>
-
178bool
-
- -
180{
-
181 return value_ == beast::zero;
-
182}
+
177
+
178template <int Bits>
+
179bool
+
+ +
181{
+
182 return value_ == beast::zero;
+
183}
-
183
-
184} // namespace xrpl
+
184
+
185} // namespace xrpl
Tracks the number of instances of an object.
Identifies fields.
Definition SField.h:126
@@ -302,7 +303,7 @@ $(document).ready(function() { init_codefold(0); });
value_type value_
Definition STBitString.h:22
std::string getText() const override
-
bool isDefault() const override
+
bool isDefault() const override
void add(Serializer &s) const override
SerializedTypeID getSType() const override
STBase * copy(std::size_t n, void *buf) const override
Definition STBitString.h:92
diff --git a/STCurrency_8cpp_source.html b/STCurrency_8cpp_source.html index f8e761c8a3..3150332413 100644 --- a/STCurrency_8cpp_source.html +++ b/STCurrency_8cpp_source.html @@ -211,7 +211,7 @@ $(document).ready(function() { init_codefold(0); });
102} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isString() const
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
Identifies fields.
Definition SField.h:126
A type which can be exported to a well known binary format.
Definition STBase.h:115
diff --git a/STInteger_8h_source.html b/STInteger_8h_source.html index 1b97de845b..6fec6fed5f 100644 --- a/STInteger_8h_source.html +++ b/STInteger_8h_source.html @@ -240,13 +240,14 @@ $(document).ready(function() { init_codefold(0); });
135
136template <typename Integer>
-
137inline STInteger<Integer>::operator Integer() const
-
138{
-
139 return value_;
-
140}
+ +
138operator Integer() const
+
139{
+
140 return value_;
+
141}
-
141
-
142} // namespace xrpl
+
142
+
143} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
Tracks the number of instances of an object.
diff --git a/STNumber_8cpp_source.html b/STNumber_8cpp_source.html index 4f3a6a7bdc..ddb9582719 100644 --- a/STNumber_8cpp_source.html +++ b/STNumber_8cpp_source.html @@ -360,11 +360,11 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
Int asInt() const
UInt asAbsUInt() const
Correct absolute value from int or unsigned int.
-
bool isString() const
+
bool isString() const
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
-
bool isUInt() const
-
bool isInt() const
+
bool isUInt() const
+
bool isInt() const
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
constexpr rep mantissa() const noexcept
Returns the mantissa of the external view of the Number.
Definition Number.h:542
@@ -409,7 +409,7 @@ $(document).ready(function() { init_codefold(0); });
std::optional< Rules > const & getCurrentTransactionRules()
Definition Rules.cpp:31
SerializedTypeID
Definition SField.h:90
-
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
+
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:676
STNumber numberFromJson(SField const &field, Json::Value const &value)
Definition STNumber.cpp:205
diff --git a/STObject_8cpp_source.html b/STObject_8cpp_source.html index 8fde976616..81cd352100 100644 --- a/STObject_8cpp_source.html +++ b/STObject_8cpp_source.html @@ -1200,28 +1200,28 @@ $(document).ready(function() { init_codefold(0); });
void setFieldU8(SField const &field, unsigned char)
Definition STObject.cpp:706
SField const & getFieldSType(int index) const
Definition STObject.cpp:406
uint192 getFieldH192(SField const &field) const
Definition STObject.cpp:600
-
bool isFree() const
Definition STObject.h:951
-
STBase const * peekAtPIndex(int offset) const
Definition STObject.h:1000
+
bool isFree() const
Definition STObject.h:957
+
STBase const * peekAtPIndex(int offset) const
Definition STObject.h:1006
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STObject.cpp:814
STCurrency const & getFieldCurrency(SField const &field) const
Definition STObject.cpp:670
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:624
void setFieldIssue(SField const &field, STIssue const &)
Definition STObject.cpp:784
uint128 getFieldH128(SField const &field) const
Definition STObject.cpp:588
-
iterator begin() const
Definition STObject.h:927
+
iterator begin() const
Definition STObject.h:933
bool operator==(STObject const &o) const
Definition STObject.cpp:827
bool isEquivalent(STBase const &t) const override
Definition STObject.cpp:327
-
bool empty() const
Definition STObject.h:939
+
bool empty() const
Definition STObject.h:945
void setFieldNumber(SField const &field, STNumber const &)
Definition STObject.cpp:790
void setFieldV256(SField const &field, STVector256 const &v)
Definition STObject.cpp:748
void setFieldU64(SField const &field, std::uint64_t)
Definition STObject.cpp:724
unsigned char getFieldU8(SField const &field) const
Definition STObject.cpp:564
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:576
-
STBase const & peekAtIndex(int offset) const
Definition STObject.h:988
+
STBase const & peekAtIndex(int offset) const
Definition STObject.h:994
bool hasMatchingEntry(STBase const &)
Definition STObject.cpp:266
STNumber const & getFieldNumber(SField const &field) const
Definition STObject.cpp:677
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:760
-
std::size_t emplace_back(Args &&... args)
Definition STObject.h:975
-
iterator end() const
Definition STObject.h:933
+
std::size_t emplace_back(Args &&... args)
Definition STObject.h:981
+
iterator end() const
Definition STObject.h:939
void applyTemplate(SOTemplate const &type)
Definition STObject.cpp:148
int getFieldIndex(SField const &field) const
Definition STObject.cpp:368
uint256 getHash(HashPrefix prefix) const
Definition STObject.cpp:350
@@ -1243,7 +1243,7 @@ $(document).ready(function() { init_codefold(0); });
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
STObject & operator=(STObject const &)=default
STObject(STObject const &)=default
-
int getCount() const
Definition STObject.h:982
+
int getCount() const
Definition STObject.h:988
SerializedTypeID getSType() const override
Definition STObject.cpp:105
void setFieldU16(SField const &field, std::uint16_t)
Definition STObject.cpp:712
uint256 getFieldH256(SField const &field) const
Definition STObject.cpp:606
@@ -1267,15 +1267,15 @@ $(document).ready(function() { init_codefold(0); });
void setFieldH128(SField const &field, uint128 const &)
Definition STObject.cpp:730
STObject getFieldObject(SField const &field) const
Definition STObject.cpp:653
void setAccountID(SField const &field, AccountID const &)
Definition STObject.cpp:754
-
void setFieldUsingAssignment(SField const &field, T const &value)
Definition STObject.h:1206
+
void setFieldUsingAssignment(SField const &field, T const &value)
Definition STObject.h:1212
bool delField(SField const &field)
Definition STObject.cpp:540
- - - -
STBase & getIndex(int offset)
Definition STObject.h:994
+ + + +
STBase & getIndex(int offset)
Definition STObject.h:1000
uint160 getFieldH160(SField const &field) const
Definition STObject.cpp:594
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:618
-
STBase * getPIndex(int offset)
Definition STObject.h:1006
+
STBase * getPIndex(int offset)
Definition STObject.h:1012
STVector256 const & getFieldV256(SField const &field) const
Definition STObject.cpp:646
STPathSet const & getFieldPathSet(SField const &field) const
Definition STObject.cpp:639
bool isDefault() const override
Definition STObject.cpp:111
diff --git a/STObject_8h_source.html b/STObject_8h_source.html index ba36d71452..bec3051efd 100644 --- a/STObject_8h_source.html +++ b/STObject_8h_source.html @@ -237,1164 +237,1170 @@ $(document).ready(function() { init_codefold(0); });
147 int
148 getCount() const;
149
- - -
152 bool isFlag(std::uint32_t) const;
-
153
- -
155 getFlags() const;
+
150 bool
+ +
152 bool
+ +
154 bool
+
155 isFlag(std::uint32_t) const;
156
-
157 uint256
-
158 getHash(HashPrefix prefix) const;
+ +
158 getFlags() const;
159
160 uint256
-
161 getSigningHash(HashPrefix prefix) const;
+
161 getHash(HashPrefix prefix) const;
162
-
163 STBase const&
-
164 peekAtIndex(int offset) const;
+
163 uint256
+
164 getSigningHash(HashPrefix prefix) const;
165
-
166 STBase&
-
167 getIndex(int offset);
+
166 STBase const&
+
167 peekAtIndex(int offset) const;
168
-
169 STBase const*
-
170 peekAtPIndex(int offset) const;
+
169 STBase&
+
170 getIndex(int offset);
171
-
172 STBase*
-
173 getPIndex(int offset);
+
172 STBase const*
+
173 peekAtPIndex(int offset) const;
174
-
175 int
-
176 getFieldIndex(SField const& field) const;
+
175 STBase*
+
176 getPIndex(int offset);
177
-
178 SField const&
-
179 getFieldSType(int index) const;
+
178 int
+
179 getFieldIndex(SField const& field) const;
180
-
181 STBase const&
-
182 peekAtField(SField const& field) const;
+
181 SField const&
+
182 getFieldSType(int index) const;
183
-
184 STBase&
-
185 getField(SField const& field);
+
184 STBase const&
+
185 peekAtField(SField const& field) const;
186
-
187 STBase const*
-
188 peekAtPField(SField const& field) const;
+
187 STBase&
+
188 getField(SField const& field);
189
-
190 STBase*
-
191 getPField(SField const& field, bool createOkay = false);
+
190 STBase const*
+
191 peekAtPField(SField const& field) const;
192
-
193 // these throw if the field type doesn't match, or return default values
-
194 // if the field is optional but not present
-
195 unsigned char
-
196 getFieldU8(SField const& field) const;
- -
198 getFieldU16(SField const& field) const;
- -
200 getFieldU32(SField const& field) const;
- -
202 getFieldU64(SField const& field) const;
-
203 uint128
-
204 getFieldH128(SField const& field) const;
-
205
-
206 uint160
-
207 getFieldH160(SField const& field) const;
-
208 uint192
-
209 getFieldH192(SField const& field) const;
-
210 uint256
-
211 getFieldH256(SField const& field) const;
- -
213 getFieldI32(SField const& field) const;
- -
215 getAccountID(SField const& field) const;
-
216
-
217 Blob
-
218 getFieldVL(SField const& field) const;
-
219 STAmount const&
-
220 getFieldAmount(SField const& field) const;
-
221 STPathSet const&
-
222 getFieldPathSet(SField const& field) const;
-
223 STVector256 const&
-
224 getFieldV256(SField const& field) const;
-
225 // If not found, returns an object constructed with the given field
- -
227 getFieldObject(SField const& field) const;
-
228 STArray const&
-
229 getFieldArray(SField const& field) const;
-
230 STCurrency const&
-
231 getFieldCurrency(SField const& field) const;
-
232 STNumber const&
-
233 getFieldNumber(SField const& field) const;
-
234
-
242 template <class T>
-
243 typename T::value_type
-
244 operator[](TypedField<T> const& f) const;
-
245
-
254 template <class T>
- -
256 operator[](OptionaledField<T> const& of) const;
-
257
-
265 template <class T>
- - -
268
-
278 template <class T>
- - -
281
-
289 template <class T>
-
290 typename T::value_type
-
291 at(TypedField<T> const& f) const;
-
292
-
301 template <class T>
- -
303 at(OptionaledField<T> const& of) const;
-
304
-
312 template <class T>
- -
314 at(TypedField<T> const& f);
-
315
-
325 template <class T>
- - -
328
-
332 void
- -
334
+
193 STBase*
+
194 getPField(SField const& field, bool createOkay = false);
+
195
+
196 // these throw if the field type doesn't match, or return default values
+
197 // if the field is optional but not present
+
198 unsigned char
+
199 getFieldU8(SField const& field) const;
+ +
201 getFieldU16(SField const& field) const;
+ +
203 getFieldU32(SField const& field) const;
+ +
205 getFieldU64(SField const& field) const;
+
206 uint128
+
207 getFieldH128(SField const& field) const;
+
208
+
209 uint160
+
210 getFieldH160(SField const& field) const;
+
211 uint192
+
212 getFieldH192(SField const& field) const;
+
213 uint256
+
214 getFieldH256(SField const& field) const;
+ +
216 getFieldI32(SField const& field) const;
+ +
218 getAccountID(SField const& field) const;
+
219
+
220 Blob
+
221 getFieldVL(SField const& field) const;
+
222 STAmount const&
+
223 getFieldAmount(SField const& field) const;
+
224 STPathSet const&
+
225 getFieldPathSet(SField const& field) const;
+
226 STVector256 const&
+
227 getFieldV256(SField const& field) const;
+
228 // If not found, returns an object constructed with the given field
+ +
230 getFieldObject(SField const& field) const;
+
231 STArray const&
+
232 getFieldArray(SField const& field) const;
+
233 STCurrency const&
+
234 getFieldCurrency(SField const& field) const;
+
235 STNumber const&
+
236 getFieldNumber(SField const& field) const;
+
237
+
245 template <class T>
+
246 typename T::value_type
+
247 operator[](TypedField<T> const& f) const;
+
248
+
257 template <class T>
+ +
259 operator[](OptionaledField<T> const& of) const;
+
260
+
268 template <class T>
+ + +
271
+
281 template <class T>
+ + +
284
+
292 template <class T>
+
293 typename T::value_type
+
294 at(TypedField<T> const& f) const;
+
295
+
304 template <class T>
+ +
306 at(OptionaledField<T> const& of) const;
+
307
+
315 template <class T>
+ +
317 at(TypedField<T> const& f);
+
318
+
328 template <class T>
+ + +
331
335 void
-
336 set(STBase&& v);
+
337
338 void
-
339 setFieldU8(SField const& field, unsigned char);
-
340 void
-
341 setFieldU16(SField const& field, std::uint16_t);
-
342 void
-
343 setFieldU32(SField const& field, std::uint32_t);
-
344 void
-
345 setFieldU64(SField const& field, std::uint64_t);
-
346 void
-
347 setFieldH128(SField const& field, uint128 const&);
-
348 void
-
349 setFieldH256(SField const& field, uint256 const&);
-
350 void
-
351 setFieldI32(SField const& field, std::int32_t);
-
352 void
-
353 setFieldVL(SField const& field, Blob const&);
-
354 void
-
355 setFieldVL(SField const& field, Slice const&);
-
356
+
339 set(STBase&& v);
+
340
+
341 void
+
342 setFieldU8(SField const& field, unsigned char);
+
343 void
+
344 setFieldU16(SField const& field, std::uint16_t);
+
345 void
+
346 setFieldU32(SField const& field, std::uint32_t);
+
347 void
+
348 setFieldU64(SField const& field, std::uint64_t);
+
349 void
+
350 setFieldH128(SField const& field, uint128 const&);
+
351 void
+
352 setFieldH256(SField const& field, uint256 const&);
+
353 void
+
354 setFieldI32(SField const& field, std::int32_t);
+
355 void
+
356 setFieldVL(SField const& field, Blob const&);
357 void
-
358 setAccountID(SField const& field, AccountID const&);
+
358 setFieldVL(SField const& field, Slice const&);
359
360 void
-
361 setFieldAmount(SField const& field, STAmount const&);
-
362 void
-
363 setFieldIssue(SField const& field, STIssue const&);
-
364 void
-
365 setFieldCurrency(SField const& field, STCurrency const&);
-
366 void
-
367 setFieldNumber(SField const& field, STNumber const&);
-
368 void
-
369 setFieldPathSet(SField const& field, STPathSet const&);
-
370 void
-
371 setFieldV256(SField const& field, STVector256 const& v);
-
372 void
-
373 setFieldArray(SField const& field, STArray const& v);
-
374 void
-
375 setFieldObject(SField const& field, STObject const& v);
-
376
-
377 template <class Tag>
-
378 void
-
379 setFieldH160(SField const& field, base_uint<160, Tag> const& v);
-
380
-
381 STObject&
-
382 peekFieldObject(SField const& field);
-
383 STArray&
-
384 peekFieldArray(SField const& field);
-
385
-
386 bool
-
387 isFieldPresent(SField const& field) const;
-
388 STBase*
-
389 makeFieldPresent(SField const& field);
-
390 void
-
391 makeFieldAbsent(SField const& field);
-
392 bool
-
393 delField(SField const& field);
-
394 void
-
395 delField(int index);
-
396
- -
398 getStyle(SField const& field) const;
+
361 setAccountID(SField const& field, AccountID const&);
+
362
+
363 void
+
364 setFieldAmount(SField const& field, STAmount const&);
+
365 void
+
366 setFieldIssue(SField const& field, STIssue const&);
+
367 void
+
368 setFieldCurrency(SField const& field, STCurrency const&);
+
369 void
+
370 setFieldNumber(SField const& field, STNumber const&);
+
371 void
+
372 setFieldPathSet(SField const& field, STPathSet const&);
+
373 void
+
374 setFieldV256(SField const& field, STVector256 const& v);
+
375 void
+
376 setFieldArray(SField const& field, STArray const& v);
+
377 void
+
378 setFieldObject(SField const& field, STObject const& v);
+
379
+
380 template <class Tag>
+
381 void
+
382 setFieldH160(SField const& field, base_uint<160, Tag> const& v);
+
383
+
384 STObject&
+
385 peekFieldObject(SField const& field);
+
386 STArray&
+
387 peekFieldArray(SField const& field);
+
388
+
389 bool
+
390 isFieldPresent(SField const& field) const;
+
391 STBase*
+
392 makeFieldPresent(SField const& field);
+
393 void
+
394 makeFieldAbsent(SField const& field);
+
395 bool
+
396 delField(SField const& field);
+
397 void
+
398 delField(int index);
399
-
400 bool
-
401 hasMatchingEntry(STBase const&);
+ +
401 getStyle(SField const& field) const;
402
403 bool
-
404 operator==(STObject const& o) const;
-
405 bool
-
406 operator!=(STObject const& o) const;
-
407
-
408 class FieldErr;
-
409
-
410private:
-
-
411 enum WhichFields : bool {
-
412 // These values are carefully chosen to do the right thing if passed
-
413 // to SField::shouldInclude (bool)
- -
415 withAllFields = true
-
416 };
+
404 hasMatchingEntry(STBase const&);
+
405
+
406 bool
+
407 operator==(STObject const& o) const;
+
408 bool
+
409 operator!=(STObject const& o) const;
+
410
+
411 class FieldErr;
+
412
+
413private:
+
+
414 enum WhichFields : bool {
+
415 // These values are carefully chosen to do the right thing if passed
+
416 // to SField::shouldInclude (bool)
+ +
418 withAllFields = true
+
419 };
-
417
-
418 void
-
419 add(Serializer& s, WhichFields whichFields) const;
420
-
421 // Sort the entries in an STObject into the order that they will be
-
422 // serialized. Note: they are not sorted into pointer value order, they
-
423 // are sorted by SField::fieldCode.
- -
425 getSortedFields(STObject const& objToSort, WhichFields whichFields);
-
426
-
427 // Implementation for getting (most) fields that return by value.
-
428 //
-
429 // The remove_cv and remove_reference are necessitated by the STBitString
-
430 // types. Their value() returns by const ref. We return those types
-
431 // by value.
-
432 template <
-
433 typename T,
-
434 typename V =
- -
436 V
-
437 getFieldByValue(SField const& field) const;
-
438
-
439 // Implementations for getting (most) fields that return by const reference.
-
440 //
-
441 // If an absent optional field is deserialized we don't have anything
-
442 // obvious to return. So we insist on having the call provide an
-
443 // 'empty' value we return in that circumstance.
-
444 template <typename T, typename V>
-
445 V const&
-
446 getFieldByConstRef(SField const& field, V const& empty) const;
-
447
-
448 // Implementation for setting most fields with a setValue() method.
-
449 template <typename T, typename V>
-
450 void
-
451 setFieldUsingSetValue(SField const& field, V value);
-
452
-
453 // Implementation for setting fields using assignment
-
454 template <typename T>
-
455 void
-
456 setFieldUsingAssignment(SField const& field, T const& value);
-
457
-
458 // Implementation for peeking STObjects and STArrays
-
459 template <typename T>
-
460 T&
-
461 peekField(SField const& field);
-
462
-
463 STBase*
-
464 copy(std::size_t n, void* buf) const override;
-
465 STBase*
-
466 move(std::size_t n, void* buf) override;
-
467
-
468 friend class detail::STVar;
-
469};
-
+
421 void
+
422 add(Serializer& s, WhichFields whichFields) const;
+
423
+
424 // Sort the entries in an STObject into the order that they will be
+
425 // serialized. Note: they are not sorted into pointer value order, they
+
426 // are sorted by SField::fieldCode.
+ +
428 getSortedFields(STObject const& objToSort, WhichFields whichFields);
+
429
+
430 // Implementation for getting (most) fields that return by value.
+
431 //
+
432 // The remove_cv and remove_reference are necessitated by the STBitString
+
433 // types. Their value() returns by const ref. We return those types
+
434 // by value.
+
435 template <
+
436 typename T,
+
437 typename V =
+ +
439 V
+
440 getFieldByValue(SField const& field) const;
+
441
+
442 // Implementations for getting (most) fields that return by const reference.
+
443 //
+
444 // If an absent optional field is deserialized we don't have anything
+
445 // obvious to return. So we insist on having the call provide an
+
446 // 'empty' value we return in that circumstance.
+
447 template <typename T, typename V>
+
448 V const&
+
449 getFieldByConstRef(SField const& field, V const& empty) const;
+
450
+
451 // Implementation for setting most fields with a setValue() method.
+
452 template <typename T, typename V>
+
453 void
+
454 setFieldUsingSetValue(SField const& field, V value);
+
455
+
456 // Implementation for setting fields using assignment
+
457 template <typename T>
+
458 void
+
459 setFieldUsingAssignment(SField const& field, T const& value);
+
460
+
461 // Implementation for peeking STObjects and STArrays
+
462 template <typename T>
+
463 T&
+
464 peekField(SField const& field);
+
465
+
466 STBase*
+
467 copy(std::size_t n, void* buf) const override;
+
468 STBase*
+
469 move(std::size_t n, void* buf) override;
470
-
471//------------------------------------------------------------------------------
-
472
-
473template <class T>
-
- -
475{
-
476public:
-
477 using value_type = typename T::value_type;
-
478
- -
480 value() const;
+
471 friend class detail::STVar;
+
472};
+
+
473
+
474//------------------------------------------------------------------------------
+
475
+
476template <class T>
+
+ +
478{
+
479public:
+
480 using value_type = typename T::value_type;
481
-
483 operator*() const;
+
483 value() const;
484
-
487 T const*
-
488 operator->() const;
-
489
-
490protected:
- - - -
494
-
495 Proxy(Proxy const&) = default;
-
496
-
497 Proxy(STObject* st, TypedField<T> const* f);
-
498
-
499 T const*
-
500 find() const;
+ +
486 operator*() const;
+
487
+
490 T const*
+
491 operator->() const;
+
492
+
493protected:
+ + + +
497
+
498 Proxy(Proxy const&) = default;
+
499
+
500 Proxy(STObject* st, TypedField<T> const* f);
501
-
502 template <class U>
-
503 void
-
504 assign(U&& u);
-
505};
+
502 T const*
+
503 find() const;
+
504
+
505 template <class U>
+
506 void
+
507 assign(U&& u);
+
508};
-
506
-
507// Constraint += and -= ValueProxy operators
-
508// to value types that support arithmetic operations
-
509template <typename U>
- -
511template <typename U, typename Value = typename U::value_type, typename Unit = typename U::unit_type>
- - -
514template <typename U, typename Value = typename U::value_type>
- -
516template <typename U>
- -
518
-
519template <class T, class U>
-
520concept Addable = requires(T t, U u) { t = t + u; };
-
521template <typename T, typename U>
- -
523
-
524template <class T>
-
- -
526{
-
527private:
-
528 using value_type = typename T::value_type;
-
529
-
530public:
-
531 ValueProxy(ValueProxy const&) = default;
- -
533 operator=(ValueProxy const&) = delete;
-
534
-
535 template <class U>
- -
537 operator=(U&& u);
-
538
-
539 // Convenience operators for value types supporting
-
540 // arithmetic operations
-
541 template <IsArithmetic U>
- - -
544 operator+=(U const& u);
-
545
-
546 template <IsArithmetic U>
- - -
549 operator-=(U const& u);
-
550
-
551 operator value_type() const;
-
552
-
553 template <typename U>
-
554 friend bool
-
-
555 operator==(U const& lhs, STObject::ValueProxy<T> const& rhs)
-
556 {
-
557 return rhs.value() == lhs;
-
558 }
+
509
+
510// Constraint += and -= ValueProxy operators
+
511// to value types that support arithmetic operations
+
512template <typename U>
+ +
514template <typename U, typename Value = typename U::value_type, typename Unit = typename U::unit_type>
+ + +
517template <typename U, typename Value = typename U::value_type>
+ +
519template <typename U>
+ +
521
+
522template <class T, class U>
+
523concept Addable = requires(T t, U u) { t = t + u; };
+
524template <typename T, typename U>
+ +
526
+
527template <class T>
+
+ +
529{
+
530private:
+
531 using value_type = typename T::value_type;
+
532
+
533public:
+
534 ValueProxy(ValueProxy const&) = default;
+ +
536 operator=(ValueProxy const&) = delete;
+
537
+
538 template <class U>
+ +
540 operator=(U&& u);
+
541
+
542 // Convenience operators for value types supporting
+
543 // arithmetic operations
+
544 template <IsArithmetic U>
+ + +
547 operator+=(U const& u);
+
548
+
549 template <IsArithmetic U>
+ + +
552 operator-=(U const& u);
+
553
+
554 operator value_type() const;
+
555
+
556 template <typename U>
+
557 friend bool
+
+
558 operator==(U const& lhs, STObject::ValueProxy<T> const& rhs)
+
559 {
+
560 return rhs.value() == lhs;
+
561 }
-
559
-
560private:
-
561 friend class STObject;
562
-
563 ValueProxy(STObject* st, TypedField<T> const* f);
-
564};
-
+
563private:
+
564 friend class STObject;
565
-
566template <class T>
-
- -
568{
-
569private:
-
570 using value_type = typename T::value_type;
-
571
- -
573
-
574public:
-
575 OptionalProxy(OptionalProxy const&) = default;
- -
577 operator=(OptionalProxy const&) = delete;
-
578
-
584 explicit
-
585 operator bool() const noexcept;
-
586
-
587 operator optional_type() const;
-
588
- -
591 operator~() const;
-
592
-
593 friend bool
-
-
594 operator==(OptionalProxy const& lhs, std::nullopt_t) noexcept
-
595 {
-
596 return !lhs.engaged();
-
597 }
+
566 ValueProxy(STObject* st, TypedField<T> const* f);
+
567};
-
598
-
599 friend bool
-
- -
601 {
-
602 return rhs == std::nullopt;
-
603 }
+
568
+
569template <class T>
+
+ +
571{
+
572private:
+
573 using value_type = typename T::value_type;
+
574
+ +
576
+
577public:
+
578 OptionalProxy(OptionalProxy const&) = default;
+ +
580 operator=(OptionalProxy const&) = delete;
+
581
+
587 explicit
+
588 operator bool() const noexcept;
+
589
+
590 operator optional_type() const;
+
591
+ +
594 operator~() const;
+
595
+
596 friend bool
+
+
597 operator==(OptionalProxy const& lhs, std::nullopt_t) noexcept
+
598 {
+
599 return !lhs.engaged();
+
600 }
-
604
-
605 friend bool
-
-
606 operator==(OptionalProxy const& lhs, optional_type const& rhs) noexcept
-
607 {
-
608 if (!lhs.engaged())
-
609 return !rhs;
-
610 if (!rhs)
-
611 return false;
-
612 return *lhs == *rhs;
-
613 }
+
601
+
602 friend bool
+
+ +
604 {
+
605 return rhs == std::nullopt;
+
606 }
-
614
-
615 friend bool
-
-
616 operator==(optional_type const& lhs, OptionalProxy const& rhs) noexcept
-
617 {
-
618 return rhs == lhs;
-
619 }
+
607
+
608 friend bool
+
+
609 operator==(OptionalProxy const& lhs, optional_type const& rhs) noexcept
+
610 {
+
611 if (!lhs.engaged())
+
612 return !rhs;
+
613 if (!rhs)
+
614 return false;
+
615 return *lhs == *rhs;
+
616 }
-
620
-
621 friend bool
-
-
622 operator==(OptionalProxy const& lhs, OptionalProxy const& rhs) noexcept
-
623 {
-
624 if (lhs.engaged() != rhs.engaged())
-
625 return false;
-
626 return !lhs.engaged() || *lhs == *rhs;
-
627 }
+
617
+
618 friend bool
+
+
619 operator==(optional_type const& lhs, OptionalProxy const& rhs) noexcept
+
620 {
+
621 return rhs == lhs;
+
622 }
-
628
-
629 friend bool
-
- -
631 {
-
632 return !(lhs == std::nullopt);
-
633 }
+
623
+
624 friend bool
+
+
625 operator==(OptionalProxy const& lhs, OptionalProxy const& rhs) noexcept
+
626 {
+
627 if (lhs.engaged() != rhs.engaged())
+
628 return false;
+
629 return !lhs.engaged() || *lhs == *rhs;
+
630 }
-
634
-
635 friend bool
-
- -
637 {
-
638 return !(rhs == std::nullopt);
-
639 }
+
631
+
632 friend bool
+
+ +
634 {
+
635 return !(lhs == std::nullopt);
+
636 }
-
640
-
641 friend bool
-
-
642 operator!=(OptionalProxy const& lhs, optional_type const& rhs) noexcept
-
643 {
-
644 return !(lhs == rhs);
-
645 }
+
637
+
638 friend bool
+
+ +
640 {
+
641 return !(rhs == std::nullopt);
+
642 }
-
646
-
647 friend bool
-
-
648 operator!=(optional_type const& lhs, OptionalProxy const& rhs) noexcept
-
649 {
-
650 return !(lhs == rhs);
-
651 }
+
643
+
644 friend bool
+
+
645 operator!=(OptionalProxy const& lhs, optional_type const& rhs) noexcept
+
646 {
+
647 return !(lhs == rhs);
+
648 }
-
652
-
653 friend bool
-
-
654 operator!=(OptionalProxy const& lhs, OptionalProxy const& rhs) noexcept
-
655 {
-
656 return !(lhs == rhs);
-
657 }
+
649
+
650 friend bool
+
+
651 operator!=(optional_type const& lhs, OptionalProxy const& rhs) noexcept
+
652 {
+
653 return !(lhs == rhs);
+
654 }
-
658
-
659 // Emulate std::optional::value_or
- -
661 value_or(value_type val) const;
-
662
- - - - - -
668 operator=(optional_type const& v);
-
669
-
670 template <class U>
- -
672 operator=(U&& u);
-
673
-
674private:
-
675 friend class STObject;
+
655
+
656 friend bool
+
+
657 operator!=(OptionalProxy const& lhs, OptionalProxy const& rhs) noexcept
+
658 {
+
659 return !(lhs == rhs);
+
660 }
+
+
661
+
662 // Emulate std::optional::value_or
+ +
664 value_or(value_type val) const;
+
665
+ + + + + +
671 operator=(optional_type const& v);
+
672
+
673 template <class U>
+ +
675 operator=(U&& u);
676
-
677 OptionalProxy(STObject* st, TypedField<T> const* f);
-
678
-
679 bool
-
680 engaged() const noexcept;
+
677private:
+
678 friend class STObject;
+
679
+
680 OptionalProxy(STObject* st, TypedField<T> const* f);
681
-
682 void
-
683 disengage();
+
682 bool
+
683 engaged() const noexcept;
684
- -
686 optional_value() const;
-
687};
+
685 void
+
686 disengage();
+
687
+ +
689 optional_value() const;
+
690};
-
688
-
-
689class STObject::FieldErr : public std::runtime_error
-
690{
- -
692};
+
691
+
+
692class STObject::FieldErr : public std::runtime_error
+
693{
+ +
695};
-
693
-
694template <class T>
-
- -
696{
-
697 if (st_->mType)
-
698 {
-
699 // STObject has associated template
-
700 if (!st_->peekAtPField(*f_))
-
701 Throw<STObject::FieldErr>("Template field error '" + this->f_->getName() + "'");
-
702 style_ = st_->mType->style(*f_);
-
703 }
-
704 else
-
705 {
- -
707 }
-
708}
+
696
+
697template <class T>
+
+ +
699{
+
700 if (st_->mType)
+
701 {
+
702 // STObject has associated template
+
703 if (!st_->peekAtPField(*f_))
+
704 Throw<STObject::FieldErr>("Template field error '" + this->f_->getName() + "'");
+
705 style_ = st_->mType->style(*f_);
+
706 }
+
707 else
+
708 {
+ +
710 }
+
711}
-
709
-
710template <class T>
-
711auto
-
- -
713{
-
714 auto const t = find();
-
715 if (t)
-
716 return t->value();
-
717 if (style_ == soeINVALID)
-
718 {
-
719 Throw<STObject::FieldErr>("Value requested from invalid STObject.");
-
720 }
-
721 if (style_ != soeDEFAULT)
-
722 {
-
723 Throw<STObject::FieldErr>("Missing field '" + this->f_->getName() + "'");
-
724 }
-
725 return value_type{};
-
726}
+
712
+
713template <class T>
+
714auto
+
+ +
716{
+
717 auto const t = find();
+
718 if (t)
+
719 return t->value();
+
720 if (style_ == soeINVALID)
+
721 {
+
722 Throw<STObject::FieldErr>("Value requested from invalid STObject.");
+
723 }
+
724 if (style_ != soeDEFAULT)
+
725 {
+
726 Throw<STObject::FieldErr>("Missing field '" + this->f_->getName() + "'");
+
727 }
+
728 return value_type{};
+
729}
-
727
-
728template <class T>
-
729auto
-
- -
731{
-
732 return this->value();
-
733}
+
730
+
731template <class T>
+
732auto
+
+ +
734{
+
735 return this->value();
+
736}
-
734
-
737template <class T>
-
738T const*
-
- -
740{
-
741 return this->find();
-
742}
+
737
+
740template <class T>
+
741T const*
+
+ +
743{
+
744 return this->find();
+
745}
-
743
-
744template <class T>
-
745inline T const*
-
- -
747{
-
748 return dynamic_cast<T const*>(st_->peekAtPField(*f_));
-
749}
+
746
+
747template <class T>
+
748inline T const*
+
+ +
750{
+
751 return dynamic_cast<T const*>(st_->peekAtPField(*f_));
+
752}
-
750
-
751template <class T>
-
752template <class U>
-
753void
-
- -
755{
-
756 if (style_ == soeDEFAULT && u == value_type{})
-
757 {
-
758 st_->makeFieldAbsent(*f_);
-
759 return;
-
760 }
-
761 T* t;
-
762 if (style_ == soeINVALID)
-
763 t = dynamic_cast<T*>(st_->getPField(*f_, true));
-
764 else
-
765 t = dynamic_cast<T*>(st_->makeFieldPresent(*f_));
-
766 XRPL_ASSERT(t, "xrpl::STObject::Proxy::assign : type cast succeeded");
-
767 *t = std::forward<U>(u);
-
768}
+
753
+
754template <class T>
+
755template <class U>
+
756void
+
+ +
758{
+
759 if (style_ == soeDEFAULT && u == value_type{})
+
760 {
+
761 st_->makeFieldAbsent(*f_);
+
762 return;
+
763 }
+
764 T* t;
+
765 if (style_ == soeINVALID)
+
766 t = dynamic_cast<T*>(st_->getPField(*f_, true));
+
767 else
+
768 t = dynamic_cast<T*>(st_->makeFieldPresent(*f_));
+
769 XRPL_ASSERT(t, "xrpl::STObject::Proxy::assign : type cast succeeded");
+
770 *t = std::forward<U>(u);
+
771}
-
769
-
770//------------------------------------------------------------------------------
-
771
-
772template <class T>
-
773template <class U>
- -
- -
776{
-
777 this->assign(std::forward<U>(u));
-
778 return *this;
-
779}
+
772
+
773//------------------------------------------------------------------------------
+
774
+
775template <class T>
+
776template <class U>
+ +
+ +
779{
+
780 this->assign(std::forward<U>(u));
+
781 return *this;
+
782}
-
780
-
781template <typename T>
-
782template <IsArithmetic U>
- - -
- -
786{
-
787 this->assign(this->value() + u);
-
788 return *this;
-
789}
+
783
+
784template <typename T>
+
785template <IsArithmetic U>
+ + +
+ +
789{
+
790 this->assign(this->value() + u);
+
791 return *this;
+
792}
-
790
-
791template <class T>
-
792template <IsArithmetic U>
- - -
- -
796{
-
797 this->assign(this->value() - u);
-
798 return *this;
-
799}
+
793
+
794template <class T>
+
795template <IsArithmetic U>
+ + +
+ +
799{
+
800 this->assign(this->value() - u);
+
801 return *this;
+
802}
-
800
-
801template <class T>
-
- -
803{
-
804 return this->value();
-
805}
+
803
+
804template <class T>
+
+ +
806operator value_type() const
+
807{
+
808 return this->value();
+
809}
-
806
-
807template <class T>
-
- -
809{
-
810}
+
810
+
811template <class T>
+
+ +
813{
+
814}
-
811
-
812//------------------------------------------------------------------------------
-
813
-
814template <class T>
-
- -
816{
-
817 return engaged();
-
818}
+
815
+
816//------------------------------------------------------------------------------
+
817
+
818template <class T>
+
+ +
820operator bool() const noexcept
+
821{
+
822 return engaged();
+
823}
-
819
-
820template <class T>
- -
822{
-
823 return optional_value();
-
824}
-
825
-
826template <class T>
- -
- -
829{
-
830 return optional_value();
-
831}
+
824
+
825template <class T>
+ +
827operator typename STObject::OptionalProxy<T>::optional_type() const
+
828{
+
829 return optional_value();
+
830}
+
831
+
832template <class T>
+ +
+ +
835{
+
836 return optional_value();
+
837}
-
832
-
833template <class T>
-
834auto
-
- -
836{
-
837 disengage();
-
838 return *this;
-
839}
+
838
+
839template <class T>
+
840auto
+
+ +
842{
+
843 disengage();
+
844 return *this;
+
845}
-
840
-
841template <class T>
-
842auto
-
- -
844{
-
845 if (v)
-
846 this->assign(std::move(*v));
-
847 else
-
848 disengage();
-
849 return *this;
-
850}
+
846
+
847template <class T>
+
848auto
+
+ +
850{
+
851 if (v)
+
852 this->assign(std::move(*v));
+
853 else
+
854 disengage();
+
855 return *this;
+
856}
-
851
-
852template <class T>
-
853auto
-
- -
855{
-
856 if (v)
-
857 this->assign(*v);
-
858 else
-
859 disengage();
-
860 return *this;
-
861}
+
857
+
858template <class T>
+
859auto
+
+ +
861{
+
862 if (v)
+
863 this->assign(*v);
+
864 else
+
865 disengage();
+
866 return *this;
+
867}
-
862
-
863template <class T>
-
864template <class U>
- -
- -
867{
-
868 this->assign(std::forward<U>(u));
-
869 return *this;
-
870}
+
868
+
869template <class T>
+
870template <class U>
+ +
+ +
873{
+
874 this->assign(std::forward<U>(u));
+
875 return *this;
+
876}
-
871
-
872template <class T>
-
- -
874{
-
875}
-
-
876
-
877template <class T>
-
878bool
+
877
+
878template <class T>
- +
880{
-
881 return this->style_ == soeDEFAULT || this->find() != nullptr;
-
882}
+
881}
-
883
-
884template <class T>
-
885void
-
- -
887{
-
888 if (this->style_ == soeREQUIRED || this->style_ == soeDEFAULT)
-
889 Throw<STObject::FieldErr>("Template field error '" + this->f_->getName() + "'");
-
890 if (this->style_ == soeINVALID)
-
891 this->st_->delField(*this->f_);
-
892 else
-
893 this->st_->makeFieldAbsent(*this->f_);
-
894}
+
882
+
883template <class T>
+
884bool
+
+ +
886{
+
887 return this->style_ == soeDEFAULT || this->find() != nullptr;
+
888}
-
895
-
896template <class T>
-
897auto
-
- -
899{
-
900 if (!engaged())
-
901 return std::nullopt;
-
902 return this->value();
-
903}
+
889
+
890template <class T>
+
891void
+
+ +
893{
+
894 if (this->style_ == soeREQUIRED || this->style_ == soeDEFAULT)
+
895 Throw<STObject::FieldErr>("Template field error '" + this->f_->getName() + "'");
+
896 if (this->style_ == soeINVALID)
+
897 this->st_->delField(*this->f_);
+
898 else
+
899 this->st_->makeFieldAbsent(*this->f_);
+
900}
-
904
-
905template <class T>
- -
- -
908{
-
909 return engaged() ? this->value() : val;
-
910}
+
901
+
902template <class T>
+
903auto
+
+ +
905{
+
906 if (!engaged())
+
907 return std::nullopt;
+
908 return this->value();
+
909}
-
911
-
912//------------------------------------------------------------------------------
-
913
-
914inline STBase const&
-
- -
916{
-
917 return e.get();
-
918}
+
910
+
911template <class T>
+ +
+ +
914{
+
915 return engaged() ? this->value() : val;
+
916}
+
917
+
918//------------------------------------------------------------------------------
919
-
920//------------------------------------------------------------------------------
-
921
-
-
922inline STObject::STObject(SerialIter&& sit, SField const& name) : STObject(sit, name)
-
923{
+
920inline STBase const&
+
+ +
922{
+
923 return e.get();
924}
925
- -
- -
928{
-
929 return iterator(v_.begin());
+
926//------------------------------------------------------------------------------
+
927
+
+
928inline STObject::STObject(SerialIter&& sit, SField const& name) : STObject(sit, name)
+
929{
930}
931
- +
934{
-
935 return iterator(v_.end());
+
935 return iterator(v_.begin());
936}
937
-
938inline bool
+
- +
940{
-
941 return v_.empty();
+
941 return iterator(v_.end());
942}
943
-
944inline void
+
944inline bool
- -
946{
-
947 v_.reserve(n);
+ +
946{
+
947 return v_.empty();
948}
949
-
950inline bool
+
950inline void
- -
952{
-
953 return mType == nullptr;
+ +
952{
+
953 v_.reserve(n);
954}
955
-
956inline void
+
956inline bool
- +
958{
- +
959 return mType == nullptr;
960}
961
-
962// VFALCO NOTE does this return an expensive copy of an object with a
-
963// dynamic buffer?
-
964// VFALCO TODO Remove this function and fix the few callers.
-
965inline Serializer
-
- -
967{
-
968 Serializer s;
-
969 add(s, withAllFields);
-
970 return s;
-
971}
+
962inline void
+ -
972
-
973template <class... Args>
-
974inline std::size_t
-
- -
976{
- -
978 return v_.size() - 1;
-
979}
+
967
+
968// VFALCO NOTE does this return an expensive copy of an object with a
+
969// dynamic buffer?
+
970// VFALCO TODO Remove this function and fix the few callers.
+
971inline Serializer
+
+ +
973{
+
974 Serializer s;
+
975 add(s, withAllFields);
+
976 return s;
+
977}
-
980
-
981inline int
-
- -
983{
-
984 return v_.size();
+
978
+
979template <class... Args>
+
980inline std::size_t
+
+ +
982{
+ +
984 return v_.size() - 1;
985}
986
-
987inline STBase const&
+
987inline int
-
988STObject::peekAtIndex(int offset) const
+
989{
-
990 return v_[offset].get();
+
990 return v_.size();
991}
992
-
993inline STBase&
+
993inline STBase const&
- -
995{
+
994STObject::peekAtIndex(int offset) const
+
995{
996 return v_[offset].get();
997}
998
-
999inline STBase const*
+
999inline STBase&
-
1000STObject::peekAtPIndex(int offset) const
-
1001{
-
1002 return &v_[offset].get();
+ +
1001{
+
1002 return v_[offset].get();
1003}
1004
-
1005inline STBase*
+
1005inline STBase const*
- -
1007{
+
1006STObject::peekAtPIndex(int offset) const
+
1007{
1008 return &v_[offset].get();
1009}
1010
-
1011template <class T>
-
1012typename T::value_type
-
- -
1014{
-
1015 return at(f);
-
1016}
+
1011inline STBase*
+
+ +
1013{
+
1014 return &v_[offset].get();
+
1015}
-
1017
-
1018template <class T>
- -
- -
1021{
-
1022 return at(of);
-
1023}
+
1016
+
1017template <class T>
+
1018typename T::value_type
+
+ +
1020{
+
1021 return at(f);
+
1022}
-
1024
-
1025template <class T>
-
1026inline auto
-
- -
1028{
-
1029 return at(f);
-
1030}
+
1023
+
1024template <class T>
+ +
+ +
1027{
+
1028 return at(of);
+
1029}
-
1031
-
1032template <class T>
-
1033inline auto
-
- -
1035{
-
1036 return at(of);
-
1037}
+
1030
+
1031template <class T>
+
1032inline auto
+
+ +
1034{
+
1035 return at(f);
+
1036}
-
1038
-
1039template <class T>
-
1040typename T::value_type
-
- -
1042{
-
1043 auto const b = peekAtPField(f);
-
1044 if (!b)
-
1045 // This is a free object (no constraints)
-
1046 // with no template
-
1047 Throw<STObject::FieldErr>("Missing field: " + f.getName());
-
1048
-
1049 if (auto const u = dynamic_cast<T const*>(b))
-
1050 return u->value();
-
1051
-
1052 XRPL_ASSERT(mType, "xrpl::STObject::at(TypedField auto) : field template non-null");
-
1053 XRPL_ASSERT(b->getSType() == STI_NOTPRESENT, "xrpl::STObject::at(TypedField auto) : type not present");
+
1037
+
1038template <class T>
+
1039inline auto
+
+ +
1041{
+
1042 return at(of);
+
1043}
+
+
1044
+
1045template <class T>
+
1046typename T::value_type
+
+ +
1048{
+
1049 auto const b = peekAtPField(f);
+
1050 if (!b)
+
1051 // This is a free object (no constraints)
+
1052 // with no template
+
1053 Throw<STObject::FieldErr>("Missing field: " + f.getName());
1054
-
1055 if (mType->style(f) == soeOPTIONAL)
-
1056 Throw<STObject::FieldErr>("Missing optional field: " + f.getName());
+
1055 if (auto const u = dynamic_cast<T const*>(b))
+
1056 return u->value();
1057
-
1058 XRPL_ASSERT(mType->style(f) == soeDEFAULT, "xrpl::STObject::at(TypedField auto) : template style is default");
-
1059
-
1060 // Used to help handle the case where value_type is a const reference,
-
1061 // otherwise we would return the address of a temporary.
-
1062 static std::decay_t<typename T::value_type> const dv{};
-
1063 return dv;
-
1064}
-
+
1058 XRPL_ASSERT(mType, "xrpl::STObject::at(TypedField auto) : field template non-null");
+
1059 XRPL_ASSERT(b->getSType() == STI_NOTPRESENT, "xrpl::STObject::at(TypedField auto) : type not present");
+
1060
+
1061 if (mType->style(f) == soeOPTIONAL)
+
1062 Throw<STObject::FieldErr>("Missing optional field: " + f.getName());
+
1063
+
1064 XRPL_ASSERT(mType->style(f) == soeDEFAULT, "xrpl::STObject::at(TypedField auto) : template style is default");
1065
-
1066template <class T>
- -
- -
1069{
-
1070 auto const b = peekAtPField(*of.f);
-
1071 if (!b)
-
1072 return std::nullopt;
-
1073 auto const u = dynamic_cast<T const*>(b);
-
1074 if (!u)
-
1075 {
-
1076 XRPL_ASSERT(
-
1077 mType,
-
1078 "xrpl::STObject::at(OptionaledField auto) : field template "
-
1079 "non-null");
-
1080 XRPL_ASSERT(b->getSType() == STI_NOTPRESENT, "xrpl::STObject::at(OptionaledField auto) : type not present");
-
1081 if (mType->style(*of.f) == soeOPTIONAL)
-
1082 return std::nullopt;
-
1083 XRPL_ASSERT(
-
1084 mType->style(*of.f) == soeDEFAULT,
-
1085 "xrpl::STObject::at(OptionaledField auto) : template style is "
-
1086 "default");
-
1087 return typename T::value_type{};
-
1088 }
-
1089 return u->value();
-
1090}
+
1066 // Used to help handle the case where value_type is a const reference,
+
1067 // otherwise we would return the address of a temporary.
+
1068 static std::decay_t<typename T::value_type> const dv{};
+
1069 return dv;
+
1070}
-
1091
-
1092template <class T>
-
1093inline auto
-
- -
1095{
-
1096 return ValueProxy<T>(this, &f);
-
1097}
+
1071
+
1072template <class T>
+ +
+ +
1075{
+
1076 auto const b = peekAtPField(*of.f);
+
1077 if (!b)
+
1078 return std::nullopt;
+
1079 auto const u = dynamic_cast<T const*>(b);
+
1080 if (!u)
+
1081 {
+
1082 XRPL_ASSERT(
+
1083 mType,
+
1084 "xrpl::STObject::at(OptionaledField auto) : field template "
+
1085 "non-null");
+
1086 XRPL_ASSERT(b->getSType() == STI_NOTPRESENT, "xrpl::STObject::at(OptionaledField auto) : type not present");
+
1087 if (mType->style(*of.f) == soeOPTIONAL)
+
1088 return std::nullopt;
+
1089 XRPL_ASSERT(
+
1090 mType->style(*of.f) == soeDEFAULT,
+
1091 "xrpl::STObject::at(OptionaledField auto) : template style is "
+
1092 "default");
+
1093 return typename T::value_type{};
+
1094 }
+
1095 return u->value();
+
1096}
-
1098
-
1099template <class T>
-
1100inline auto
-
- -
1102{
-
1103 return OptionalProxy<T>(this, of.f);
-
1104}
+
1097
+
1098template <class T>
+
1099inline auto
+
+ +
1101{
+
1102 return ValueProxy<T>(this, &f);
+
1103}
+
+
1104
+
1105template <class T>
+
1106inline auto
+
+ +
1108{
+
1109 return OptionalProxy<T>(this, of.f);
+
1110}
-
1105
-
1106template <class Tag>
-
1107void
-
- -
1109{
-
1110 STBase* rf = getPField(field, true);
1111
-
1112 if (!rf)
-
1113 throwFieldNotFound(field);
-
1114
-
1115 if (rf->getSType() == STI_NOTPRESENT)
-
1116 rf = makeFieldPresent(field);
+
1112template <class Tag>
+
1113void
+
+ +
1115{
+
1116 STBase* rf = getPField(field, true);
1117
-
1118 using Bits = STBitString<160>;
-
1119 if (auto cf = dynamic_cast<Bits*>(rf))
-
1120 cf->setValue(v);
-
1121 else
-
1122 Throw<std::runtime_error>("Wrong field type");
-
1123}
-
-
1124
-
1125inline bool
-
- -
1127{
-
1128 return !(*this == o);
+
1118 if (!rf)
+
1119 throwFieldNotFound(field);
+
1120
+
1121 if (rf->getSType() == STI_NOTPRESENT)
+
1122 rf = makeFieldPresent(field);
+
1123
+
1124 using Bits = STBitString<160>;
+
1125 if (auto cf = dynamic_cast<Bits*>(rf))
+
1126 cf->setValue(v);
+
1127 else
+
1128 Throw<std::runtime_error>("Wrong field type");
1129}
1130
-
1131template <typename T, typename V>
-
1132V
-
- -
1134{
-
1135 STBase const* rf = peekAtPField(field);
+
1131inline bool
+
+ +
1133{
+
1134 return !(*this == o);
+
1135}
+
1136
-
1137 if (!rf)
-
1138 throwFieldNotFound(field);
-
1139
-
1140 SerializedTypeID id = rf->getSType();
-
1141
-
1142 if (id == STI_NOTPRESENT)
-
1143 return V(); // optional field not present
-
1144
-
1145 T const* cf = dynamic_cast<T const*>(rf);
-
1146
-
1147 if (!cf)
-
1148 Throw<std::runtime_error>("Wrong field type");
-
1149
-
1150 return cf->value();
-
1151}
-
+
1137template <typename T, typename V>
+
1138V
+
+ +
1140{
+
1141 STBase const* rf = peekAtPField(field);
+
1142
+
1143 if (!rf)
+
1144 throwFieldNotFound(field);
+
1145
+
1146 SerializedTypeID id = rf->getSType();
+
1147
+
1148 if (id == STI_NOTPRESENT)
+
1149 return V(); // optional field not present
+
1150
+
1151 T const* cf = dynamic_cast<T const*>(rf);
1152
-
1153// Implementations for getting (most) fields that return by const reference.
-
1154//
-
1155// If an absent optional field is deserialized we don't have anything
-
1156// obvious to return. So we insist on having the call provide an
-
1157// 'empty' value we return in that circumstance.
-
1158template <typename T, typename V>
-
1159V const&
-
-
1160STObject::getFieldByConstRef(SField const& field, V const& empty) const
-
1161{
-
1162 STBase const* rf = peekAtPField(field);
-
1163
-
1164 if (!rf)
-
1165 throwFieldNotFound(field);
-
1166
-
1167 SerializedTypeID id = rf->getSType();
-
1168
-
1169 if (id == STI_NOTPRESENT)
-
1170 return empty; // optional field not present
-
1171
-
1172 T const* cf = dynamic_cast<T const*>(rf);
-
1173
-
1174 if (!cf)
-
1175 Throw<std::runtime_error>("Wrong field type");
-
1176
-
1177 return *cf;
-
1178}
+
1153 if (!cf)
+
1154 Throw<std::runtime_error>("Wrong field type");
+
1155
+
1156 return cf->value();
+
1157}
+
1158
+
1159// Implementations for getting (most) fields that return by const reference.
+
1160//
+
1161// If an absent optional field is deserialized we don't have anything
+
1162// obvious to return. So we insist on having the call provide an
+
1163// 'empty' value we return in that circumstance.
+
1164template <typename T, typename V>
+
1165V const&
+
+
1166STObject::getFieldByConstRef(SField const& field, V const& empty) const
+
1167{
+
1168 STBase const* rf = peekAtPField(field);
+
1169
+
1170 if (!rf)
+
1171 throwFieldNotFound(field);
+
1172
+
1173 SerializedTypeID id = rf->getSType();
+
1174
+
1175 if (id == STI_NOTPRESENT)
+
1176 return empty; // optional field not present
+
1177
+
1178 T const* cf = dynamic_cast<T const*>(rf);
1179
-
1180// Implementation for setting most fields with a setValue() method.
-
1181template <typename T, typename V>
-
1182void
-
- -
1184{
-
1185 static_assert(!std::is_lvalue_reference<V>::value, "");
-
1186
-
1187 STBase* rf = getPField(field, true);
-
1188
-
1189 if (!rf)
-
1190 throwFieldNotFound(field);
-
1191
-
1192 if (rf->getSType() == STI_NOTPRESENT)
-
1193 rf = makeFieldPresent(field);
+
1180 if (!cf)
+
1181 Throw<std::runtime_error>("Wrong field type");
+
1182
+
1183 return *cf;
+
1184}
+
+
1185
+
1186// Implementation for setting most fields with a setValue() method.
+
1187template <typename T, typename V>
+
1188void
+
+ +
1190{
+
1191 static_assert(!std::is_lvalue_reference<V>::value, "");
+
1192
+
1193 STBase* rf = getPField(field, true);
1194
-
1195 T* cf = dynamic_cast<T*>(rf);
-
1196
-
1197 if (!cf)
-
1198 Throw<std::runtime_error>("Wrong field type");
-
1199
-
1200 cf->setValue(std::move(value));
-
1201}
-
+
1195 if (!rf)
+
1196 throwFieldNotFound(field);
+
1197
+
1198 if (rf->getSType() == STI_NOTPRESENT)
+
1199 rf = makeFieldPresent(field);
+
1200
+
1201 T* cf = dynamic_cast<T*>(rf);
1202
-
1203// Implementation for setting fields using assignment
-
1204template <typename T>
-
1205void
-
-
1206STObject::setFieldUsingAssignment(SField const& field, T const& value)
-
1207{
-
1208 STBase* rf = getPField(field, true);
-
1209
-
1210 if (!rf)
-
1211 throwFieldNotFound(field);
-
1212
-
1213 if (rf->getSType() == STI_NOTPRESENT)
-
1214 rf = makeFieldPresent(field);
+
1203 if (!cf)
+
1204 Throw<std::runtime_error>("Wrong field type");
+
1205
+
1206 cf->setValue(std::move(value));
+
1207}
+
+
1208
+
1209// Implementation for setting fields using assignment
+
1210template <typename T>
+
1211void
+
+
1212STObject::setFieldUsingAssignment(SField const& field, T const& value)
+
1213{
+
1214 STBase* rf = getPField(field, true);
1215
-
1216 T* cf = dynamic_cast<T*>(rf);
-
1217
-
1218 if (!cf)
-
1219 Throw<std::runtime_error>("Wrong field type");
-
1220
-
1221 (*cf) = value;
-
1222}
-
+
1216 if (!rf)
+
1217 throwFieldNotFound(field);
+
1218
+
1219 if (rf->getSType() == STI_NOTPRESENT)
+
1220 rf = makeFieldPresent(field);
+
1221
+
1222 T* cf = dynamic_cast<T*>(rf);
1223
-
1224// Implementation for peeking STObjects and STArrays
-
1225template <typename T>
-
1226T&
-
- -
1228{
-
1229 STBase* rf = getPField(field, true);
-
1230
-
1231 if (!rf)
-
1232 throwFieldNotFound(field);
-
1233
-
1234 if (rf->getSType() == STI_NOTPRESENT)
-
1235 rf = makeFieldPresent(field);
-
1236
-
1237 T* cf = dynamic_cast<T*>(rf);
-
1238
-
1239 if (!cf)
-
1240 Throw<std::runtime_error>("Wrong field type");
-
1241
-
1242 return *cf;
-
1243}
+
1224 if (!cf)
+
1225 Throw<std::runtime_error>("Wrong field type");
+
1226
+
1227 (*cf) = value;
+
1228}
+
1229
+
1230// Implementation for peeking STObjects and STArrays
+
1231template <typename T>
+
1232T&
+
+ +
1234{
+
1235 STBase* rf = getPField(field, true);
+
1236
+
1237 if (!rf)
+
1238 throwFieldNotFound(field);
+
1239
+
1240 if (rf->getSType() == STI_NOTPRESENT)
+
1241 rf = makeFieldPresent(field);
+
1242
+
1243 T* cf = dynamic_cast<T*>(rf);
1244
-
1245} // namespace xrpl
+
1245 if (!cf)
+
1246 Throw<std::runtime_error>("Wrong field type");
+
1247
+
1248 return *cf;
+
1249}
+
+
1250
+
1251} // namespace xrpl
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
@@ -1413,78 +1419,78 @@ $(document).ready(function() { init_codefold(0); });
A serializable number.
Definition STNumber.h:35
- - -
friend bool operator!=(OptionalProxy const &lhs, optional_type const &rhs) noexcept
Definition STObject.h:642
-
friend bool operator==(OptionalProxy const &lhs, optional_type const &rhs) noexcept
Definition STObject.h:606
-
friend bool operator==(optional_type const &lhs, OptionalProxy const &rhs) noexcept
Definition STObject.h:616
-
friend bool operator==(OptionalProxy const &lhs, OptionalProxy const &rhs) noexcept
Definition STObject.h:622
+ + +
friend bool operator!=(OptionalProxy const &lhs, optional_type const &rhs) noexcept
Definition STObject.h:645
+
friend bool operator==(OptionalProxy const &lhs, optional_type const &rhs) noexcept
Definition STObject.h:609
+
friend bool operator==(optional_type const &lhs, OptionalProxy const &rhs) noexcept
Definition STObject.h:619
+
friend bool operator==(OptionalProxy const &lhs, OptionalProxy const &rhs) noexcept
Definition STObject.h:625
std::enable_if_t< std::is_assignable_v< T, U >, OptionalProxy & > operator=(U &&u)
OptionalProxy(OptionalProxy const &)=default
-
optional_type operator~() const
Explicit conversion to std::optional.
Definition STObject.h:828
-
std::optional< typename std::decay< value_type >::type > optional_type
Definition STObject.h:572
-
typename T::value_type value_type
Definition STObject.h:570
- -
friend bool operator!=(OptionalProxy const &lhs, std::nullopt_t) noexcept
Definition STObject.h:630
-
friend bool operator!=(std::nullopt_t, OptionalProxy const &rhs) noexcept
Definition STObject.h:636
+
optional_type operator~() const
Explicit conversion to std::optional.
Definition STObject.h:834
+
std::optional< typename std::decay< value_type >::type > optional_type
Definition STObject.h:575
+
typename T::value_type value_type
Definition STObject.h:573
+ +
friend bool operator!=(OptionalProxy const &lhs, std::nullopt_t) noexcept
Definition STObject.h:633
+
friend bool operator!=(std::nullopt_t, OptionalProxy const &rhs) noexcept
Definition STObject.h:639
OptionalProxy & operator=(OptionalProxy const &)=delete
-
bool engaged() const noexcept
Definition STObject.h:879
-
friend bool operator!=(optional_type const &lhs, OptionalProxy const &rhs) noexcept
Definition STObject.h:648
-
friend bool operator==(std::nullopt_t, OptionalProxy const &rhs) noexcept
Definition STObject.h:600
-
value_type value_or(value_type val) const
Definition STObject.h:907
-
optional_type optional_value() const
Definition STObject.h:898
-
friend bool operator!=(OptionalProxy const &lhs, OptionalProxy const &rhs) noexcept
Definition STObject.h:654
- - -
void assign(U &&u)
Definition STObject.h:754
+
bool engaged() const noexcept
Definition STObject.h:885
+
friend bool operator!=(optional_type const &lhs, OptionalProxy const &rhs) noexcept
Definition STObject.h:651
+
friend bool operator==(std::nullopt_t, OptionalProxy const &rhs) noexcept
Definition STObject.h:603
+
value_type value_or(value_type val) const
Definition STObject.h:913
+
optional_type optional_value() const
Definition STObject.h:904
+
friend bool operator!=(OptionalProxy const &lhs, OptionalProxy const &rhs) noexcept
Definition STObject.h:657
+ + +
void assign(U &&u)
Definition STObject.h:757
Proxy(Proxy const &)=default
-
value_type operator*() const
Definition STObject.h:730
-
T const * operator->() const
Do not use operator->() unless the field is required, or you've checked that it's set.
Definition STObject.h:739
-
value_type value() const
Definition STObject.h:712
-
T const * find() const
Definition STObject.h:746
-
typename T::value_type value_type
Definition STObject.h:477
-
TypedField< T > const * f_
Definition STObject.h:493
- - +
value_type operator*() const
Definition STObject.h:733
+
T const * operator->() const
Do not use operator->() unless the field is required, or you've checked that it's set.
Definition STObject.h:742
+
value_type value() const
Definition STObject.h:715
+
T const * find() const
Definition STObject.h:749
+
typename T::value_type value_type
Definition STObject.h:480
+
TypedField< T > const * f_
Definition STObject.h:496
+ +
ValueProxy(ValueProxy const &)=default
ValueProxy & operator=(ValueProxy const &)=delete
-
typename T::value_type value_type
Definition STObject.h:528
+
typename T::value_type value_type
Definition STObject.h:531
ValueProxy & operator+=(U const &u)
ValueProxy & operator-=(U const &u)
-
friend bool operator==(U const &lhs, STObject::ValueProxy< T > const &rhs)
Definition STObject.h:555
+
friend bool operator==(U const &lhs, STObject::ValueProxy< T > const &rhs)
Definition STObject.h:558
std::enable_if_t< std::is_assignable_v< T, U >, ValueProxy & > operator=(U &&u)
void setFieldU8(SField const &field, unsigned char)
Definition STObject.cpp:706
SField const & getFieldSType(int index) const
Definition STObject.cpp:406
uint192 getFieldH192(SField const &field) const
Definition STObject.cpp:600
-
bool isFree() const
Definition STObject.h:951
-
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1041
-
STBase const * peekAtPIndex(int offset) const
Definition STObject.h:1000
+
bool isFree() const
Definition STObject.h:957
+
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1047
+
STBase const * peekAtPIndex(int offset) const
Definition STObject.h:1006
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STObject.cpp:814
STCurrency const & getFieldCurrency(SField const &field) const
Definition STObject.cpp:670
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:624
-
void addWithoutSigningFields(Serializer &s) const
Definition STObject.h:957
+
void addWithoutSigningFields(Serializer &s) const
Definition STObject.h:963
void setFieldIssue(SField const &field, STIssue const &)
Definition STObject.cpp:784
uint128 getFieldH128(SField const &field) const
Definition STObject.cpp:588
-
iterator begin() const
Definition STObject.h:927
+
iterator begin() const
Definition STObject.h:933
bool operator==(STObject const &o) const
Definition STObject.cpp:827
bool isEquivalent(STBase const &t) const override
Definition STObject.cpp:327
-
bool empty() const
Definition STObject.h:939
+
bool empty() const
Definition STObject.h:945
void setFieldNumber(SField const &field, STNumber const &)
Definition STObject.cpp:790
void setFieldV256(SField const &field, STVector256 const &v)
Definition STObject.cpp:748
void setFieldU64(SField const &field, std::uint64_t)
Definition STObject.cpp:724
unsigned char getFieldU8(SField const &field) const
Definition STObject.cpp:564
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:576
-
STBase const & peekAtIndex(int offset) const
Definition STObject.h:988
+
STBase const & peekAtIndex(int offset) const
Definition STObject.h:994
bool hasMatchingEntry(STBase const &)
Definition STObject.cpp:266
V const & getFieldByConstRef(SField const &field, V const &empty) const
STNumber const & getFieldNumber(SField const &field) const
Definition STObject.cpp:677
OptionalProxy< T > at(OptionaledField< T > const &of)
Return a modifiable field value as std::optional.
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:760
-
void reserve(std::size_t n)
Definition STObject.h:945
-
T::value_type operator[](TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1013
-
std::size_t emplace_back(Args &&... args)
Definition STObject.h:975
-
iterator end() const
Definition STObject.h:933
+
void reserve(std::size_t n)
Definition STObject.h:951
+
T::value_type operator[](TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1019
+
std::size_t emplace_back(Args &&... args)
Definition STObject.h:981
+
iterator end() const
Definition STObject.h:939
void applyTemplate(SOTemplate const &type)
Definition STObject.cpp:148
int getFieldIndex(SField const &field) const
Definition STObject.cpp:368
uint256 getHash(HashPrefix prefix) const
Definition STObject.cpp:350
@@ -1503,12 +1509,12 @@ $(document).ready(function() { init_codefold(0); });
STBase & getField(SField const &field)
Definition STObject.cpp:395
void setFieldAmount(SField const &field, STAmount const &)
Definition STObject.cpp:772
void add(Serializer &s) const override
Definition STObject.cpp:117
-
Serializer getSerializer() const
Definition STObject.h:966
+
Serializer getSerializer() const
Definition STObject.h:972
bool isFlag(std::uint32_t) const
Definition STObject.cpp:486
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
STObject & operator=(STObject const &)=default
STObject(STObject const &)=default
-
int getCount() const
Definition STObject.h:982
+
int getCount() const
Definition STObject.h:988
ValueProxy< T > operator[](TypedField< T > const &f)
Get a modifiable field value.
SerializedTypeID getSType() const override
Definition STObject.cpp:105
void setFieldU16(SField const &field, std::uint16_t)
Definition STObject.cpp:712
@@ -1531,22 +1537,22 @@ $(document).ready(function() { init_codefold(0); });
void applyTemplateFromSField(SField const &)
Definition STObject.cpp:197
bool setFlag(std::uint32_t)
Definition STObject.cpp:462
void setFieldObject(SField const &field, STObject const &v)
Definition STObject.cpp:808
-
bool operator!=(STObject const &o) const
Definition STObject.h:1126
+
bool operator!=(STObject const &o) const
Definition STObject.h:1132
void setFieldH128(SField const &field, uint128 const &)
Definition STObject.cpp:730
STObject getFieldObject(SField const &field) const
Definition STObject.cpp:653
-
T & peekField(SField const &field)
Definition STObject.h:1227
-
void setFieldH160(SField const &field, base_uint< 160, Tag > const &v)
Definition STObject.h:1108
-
void setFieldUsingSetValue(SField const &field, V value)
Definition STObject.h:1183
+
T & peekField(SField const &field)
Definition STObject.h:1233
+
void setFieldH160(SField const &field, base_uint< 160, Tag > const &v)
Definition STObject.h:1114
+
void setFieldUsingSetValue(SField const &field, V value)
Definition STObject.h:1189
void setAccountID(SField const &field, AccountID const &)
Definition STObject.cpp:754
-
void setFieldUsingAssignment(SField const &field, T const &value)
Definition STObject.h:1206
+
void setFieldUsingAssignment(SField const &field, T const &value)
Definition STObject.h:1212
bool delField(SField const &field)
Definition STObject.cpp:540
- - - -
STBase & getIndex(int offset)
Definition STObject.h:994
+ + + +
STBase & getIndex(int offset)
Definition STObject.h:1000
uint160 getFieldH160(SField const &field) const
Definition STObject.cpp:594
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:618
-
STBase * getPIndex(int offset)
Definition STObject.h:1006
+
STBase * getPIndex(int offset)
Definition STObject.h:1012
STVector256 const & getFieldV256(SField const &field) const
Definition STObject.cpp:646
STPathSet const & getFieldPathSet(SField const &field) const
Definition STObject.cpp:639
bool isDefault() const override
Definition STObject.cpp:111
@@ -1568,12 +1574,12 @@ $(document).ready(function() { init_codefold(0); });
STBase & get()
Definition STVar.h:74
- - - - - - + + + + + +
T emplace_back(T... args)
T empty(T... args)
@@ -1605,7 +1611,7 @@ $(document).ready(function() { init_codefold(0); });
Indicate std::optional field semantics.
Definition SField.h:311
TypedField< T > const * f
Definition SField.h:312
-
STBase const & operator()(detail::STVar const &e) const
Definition STObject.h:915
+
STBase const & operator()(detail::STVar const &e) const
Definition STObject.h:921
A field with a type known at compile time.
Definition SField.h:301
diff --git a/STObject__test_8cpp_source.html b/STObject__test_8cpp_source.html index 663c1db4ea..368e704e04 100644 --- a/STObject__test_8cpp_source.html +++ b/STObject__test_8cpp_source.html @@ -605,7 +605,7 @@ $(document).ready(function() { init_codefold(0); });
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:760
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:718
void add(Serializer &s) const override
Definition STObject.cpp:117
-
Serializer getSerializer() const
Definition STObject.h:966
+
Serializer getSerializer() const
Definition STObject.h:972
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
uint256 getFieldH256(SField const &field) const
Definition STObject.cpp:606
STBase * makeFieldPresent(SField const &field)
Definition STObject.cpp:503
diff --git a/STParsedJSON_8cpp_source.html b/STParsedJSON_8cpp_source.html index e95d5cc2da..2bdace4cba 100644 --- a/STParsedJSON_8cpp_source.html +++ b/STParsedJSON_8cpp_source.html @@ -1217,17 +1217,17 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
bool isObjectOrNull() const
+
bool isObjectOrNull() const
Int asInt() const
-
bool isString() const
+
bool isString() const
UInt asUInt() const
-
Members getMemberNames() const
Return a list of the member names.
-
bool isObject() const
-
bool isValidIndex(UInt index) const
Return true if index < size().
+
Members getMemberNames() const
Return a list of the member names.
+
bool isObject() const
+
bool isValidIndex(UInt index) const
Return true if index < size().
std::string asString() const
Returns the unquoted string value.
-
bool isUInt() const
-
bool isArrayOrNull() const
-
bool isInt() const
+
bool isUInt() const
+
bool isArrayOrNull() const
+
bool isInt() const
KeyType findTypeByName(std::string const &name) const
Retrieve the type for a format specified by name.
static LedgerFormats const & getInstance()
std::optional< std::uint32_t > getGranularValue(std::string const &name) const
@@ -1238,7 +1238,7 @@ $(document).ready(function() { init_codefold(0); });
std::string const & getName() const
Definition SField.h:193
void push_back(STObject const &object)
Definition STArray.h:187
- +
Json::Value error
On failure, an appropriate set of error values.
@@ -1247,7 +1247,7 @@ $(document).ready(function() { init_codefold(0); });
void emplace_back(Args &&... args)
Definition STPathSet.h:376
-
void push_back(uint256 const &v)
+
void push_back(uint256 const &v)
static TxFormats const & getInstance()
Definition TxFormats.cpp:51
Integers of any length that is a multiple of 32-bits.
Definition base_uint.h:66
diff --git a/STParsedJSON__test_8cpp_source.html b/STParsedJSON__test_8cpp_source.html index fbc6428b64..a02f439977 100644 --- a/STParsedJSON__test_8cpp_source.html +++ b/STParsedJSON__test_8cpp_source.html @@ -2130,246 +2130,261 @@ $(document).ready(function() { init_codefold(0); });
2003 }
2004
2005 {
-
2006 std::string const goodJson(R"({"CloseResolution":19,"Method":250,)"
-
2007 R"("TransactionResult":"tecFROZEN"})");
-
2008
-
2009 Json::Value jv;
-
2010 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
-
2011 {
-
2012 STParsedJSONObject parsed("test", jv);
-
2013 if (BEAST_EXPECT(parsed.object))
-
2014 {
-
2015 std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none)));
-
2016 BEAST_EXPECT(serialized == goodJson);
-
2017 }
-
2018 }
-
2019 }
-
2020
-
2021 {
-
2022 std::string const goodJson(R"({"CloseResolution":19,"Method":"250",)"
-
2023 R"("TransactionResult":"tecFROZEN"})");
-
2024 std::string const expectedJson(R"({"CloseResolution":19,"Method":250,)"
-
2025 R"("TransactionResult":"tecFROZEN"})");
-
2026
-
2027 Json::Value jv;
-
2028 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
-
2029 {
-
2030 // Integer values are always parsed as int,
-
2031 // unless they're too big. We want a small uint.
-
2032 jv["CloseResolution"] = Json::UInt(19);
-
2033 STParsedJSONObject parsed("test", jv);
-
2034 if (BEAST_EXPECT(parsed.object))
-
2035 {
-
2036 std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none)));
-
2037 BEAST_EXPECT(serialized == expectedJson);
-
2038 }
-
2039 }
-
2040 }
-
2041
-
2042 {
-
2043 std::string const goodJson(R"({"CloseResolution":"19","Method":"250",)"
-
2044 R"("TransactionResult":"tecFROZEN"})");
-
2045 std::string const expectedJson(R"({"CloseResolution":19,"Method":250,)"
-
2046 R"("TransactionResult":"tecFROZEN"})");
-
2047
-
2048 Json::Value jv;
-
2049 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
-
2050 {
-
2051 // Integer values are always parsed as int,
-
2052 // unless they're too big. We want a small uint.
-
2053 jv["CloseResolution"] = Json::UInt(19);
-
2054 STParsedJSONObject parsed("test", jv);
-
2055 if (BEAST_EXPECT(parsed.object))
-
2056 {
-
2057 std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none)));
-
2058 BEAST_EXPECT(serialized == expectedJson);
-
2059 }
-
2060 }
-
2061 }
-
2062
-
2063 {
-
2064 std::string const json(R"({"CloseResolution":19,"Method":250,)"
-
2065 R"("TransactionResult":"terQUEUED"})");
-
2066
-
2067 Json::Value jv;
-
2068 if (BEAST_EXPECT(parseJSONString(json, jv)))
-
2069 {
-
2070 STParsedJSONObject parsed("test", jv);
-
2071 BEAST_EXPECT(!parsed.object);
-
2072 BEAST_EXPECT(parsed.error);
-
2073 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
-
2074 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransactionResult' is out of range.");
-
2075 }
-
2076 }
-
2077
-
2078 {
-
2079 std::string const json(R"({"CloseResolution":19,"Method":"pony",)"
-
2080 R"("TransactionResult":"tesSUCCESS"})");
-
2081
-
2082 Json::Value jv;
-
2083 if (BEAST_EXPECT(parseJSONString(json, jv)))
-
2084 {
-
2085 STParsedJSONObject parsed("test", jv);
-
2086 BEAST_EXPECT(!parsed.object);
-
2087 BEAST_EXPECT(parsed.error);
-
2088 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
-
2089 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.Method' has bad type.");
-
2090 }
-
2091 }
-
2092
-
2093 {
-
2094 std::string const json(R"({"CloseResolution":19,"Method":3294967296,)"
-
2095 R"("TransactionResult":"tesSUCCESS"})");
-
2096
-
2097 Json::Value jv;
-
2098 if (BEAST_EXPECT(parseJSONString(json, jv)))
-
2099 {
-
2100 STParsedJSONObject parsed("test", jv);
-
2101 BEAST_EXPECT(!parsed.object);
-
2102 BEAST_EXPECT(parsed.error);
-
2103 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
-
2104 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.Method' is out of range.");
-
2105 }
-
2106 }
-
2107
-
2108 {
-
2109 std::string const json(R"({"CloseResolution":-10,"Method":42,)"
-
2110 R"("TransactionResult":"tesSUCCESS"})");
-
2111
-
2112 Json::Value jv;
-
2113 if (BEAST_EXPECT(parseJSONString(json, jv)))
-
2114 {
-
2115 STParsedJSONObject parsed("test", jv);
-
2116 BEAST_EXPECT(!parsed.object);
-
2117 BEAST_EXPECT(parsed.error);
-
2118 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
-
2119 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.CloseResolution' is out of range.");
-
2120 }
-
2121 }
-
2122
-
2123 {
-
2124 std::string const json(R"({"CloseResolution":19,"Method":3.141592653,)"
-
2125 R"("TransactionResult":"tesSUCCESS"})");
-
2126
-
2127 Json::Value jv;
-
2128 if (BEAST_EXPECT(parseJSONString(json, jv)))
-
2129 {
-
2130 STParsedJSONObject parsed("test", jv);
-
2131 BEAST_EXPECT(!parsed.object);
-
2132 BEAST_EXPECT(parsed.error);
-
2133 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
-
2134 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.Method' has bad type.");
-
2135 }
-
2136 }
-
2137
-
2138 {
-
2139 std::string const goodJson(R"({"CloseResolution":19,"Method":250,)"
-
2140 R"("TransferFee":"65535"})");
-
2141 std::string const expectedJson(R"({"CloseResolution":19,"Method":250,)"
-
2142 R"("TransferFee":65535})");
-
2143
-
2144 Json::Value jv;
-
2145 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
-
2146 {
-
2147 STParsedJSONObject parsed("test", jv);
-
2148 if (BEAST_EXPECT(parsed.object))
-
2149 {
-
2150 std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none)));
-
2151 BEAST_EXPECT(serialized == expectedJson);
-
2152 }
-
2153 }
-
2154 }
-
2155
-
2156 {
-
2157 std::string const json(R"({"CloseResolution":19,"Method":250,)"
-
2158 R"("TransferFee":"65536"})");
-
2159
-
2160 Json::Value jv;
-
2161 if (BEAST_EXPECT(parseJSONString(json, jv)))
-
2162 {
-
2163 STParsedJSONObject parsed("test", jv);
-
2164 BEAST_EXPECT(!parsed.object);
-
2165 BEAST_EXPECT(parsed.error);
-
2166 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
-
2167 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransferFee' has invalid data.");
-
2168 }
-
2169 }
-
2170
-
2171 {
-
2172 std::string const json(R"({"CloseResolution":19,"Method":250,)"
-
2173 R"("TransferFee":"Payment"})");
-
2174
-
2175 Json::Value jv;
-
2176 if (BEAST_EXPECT(parseJSONString(json, jv)))
-
2177 {
-
2178 STParsedJSONObject parsed("test", jv);
-
2179 BEAST_EXPECT(!parsed.object);
-
2180 BEAST_EXPECT(parsed.error);
-
2181 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
-
2182 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransferFee' has invalid data.");
-
2183 }
-
2184 }
-
2185
-
2186 {
-
2187 std::string const json(R"({"CloseResolution":19,"Method":250,)"
-
2188 R"("TransferFee":true})");
-
2189
-
2190 Json::Value jv;
-
2191 if (BEAST_EXPECT(parseJSONString(json, jv)))
-
2192 {
-
2193 STParsedJSONObject parsed("test", jv);
-
2194 BEAST_EXPECT(!parsed.object);
-
2195 BEAST_EXPECT(parsed.error);
-
2196 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
-
2197 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransferFee' has bad type.");
-
2198 }
-
2199 }
-
2200 }
+
2006 std::string const goodJson(
+
2007 R"({"CloseResolution":19,"Method":250,)"
+
2008 R"("TransactionResult":"tecFROZEN"})");
+
2009
+
2010 Json::Value jv;
+
2011 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
+
2012 {
+
2013 STParsedJSONObject parsed("test", jv);
+
2014 if (BEAST_EXPECT(parsed.object))
+
2015 {
+
2016 std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none)));
+
2017 BEAST_EXPECT(serialized == goodJson);
+
2018 }
+
2019 }
+
2020 }
+
2021
+
2022 {
+
2023 std::string const goodJson(
+
2024 R"({"CloseResolution":19,"Method":"250",)"
+
2025 R"("TransactionResult":"tecFROZEN"})");
+
2026 std::string const expectedJson(
+
2027 R"({"CloseResolution":19,"Method":250,)"
+
2028 R"("TransactionResult":"tecFROZEN"})");
+
2029
+
2030 Json::Value jv;
+
2031 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
+
2032 {
+
2033 // Integer values are always parsed as int,
+
2034 // unless they're too big. We want a small uint.
+
2035 jv["CloseResolution"] = Json::UInt(19);
+
2036 STParsedJSONObject parsed("test", jv);
+
2037 if (BEAST_EXPECT(parsed.object))
+
2038 {
+
2039 std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none)));
+
2040 BEAST_EXPECT(serialized == expectedJson);
+
2041 }
+
2042 }
+
2043 }
+
2044
+
2045 {
+
2046 std::string const goodJson(
+
2047 R"({"CloseResolution":"19","Method":"250",)"
+
2048 R"("TransactionResult":"tecFROZEN"})");
+
2049 std::string const expectedJson(
+
2050 R"({"CloseResolution":19,"Method":250,)"
+
2051 R"("TransactionResult":"tecFROZEN"})");
+
2052
+
2053 Json::Value jv;
+
2054 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
+
2055 {
+
2056 // Integer values are always parsed as int,
+
2057 // unless they're too big. We want a small uint.
+
2058 jv["CloseResolution"] = Json::UInt(19);
+
2059 STParsedJSONObject parsed("test", jv);
+
2060 if (BEAST_EXPECT(parsed.object))
+
2061 {
+
2062 std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none)));
+
2063 BEAST_EXPECT(serialized == expectedJson);
+
2064 }
+
2065 }
+
2066 }
+
2067
+
2068 {
+
2069 std::string const json(
+
2070 R"({"CloseResolution":19,"Method":250,)"
+
2071 R"("TransactionResult":"terQUEUED"})");
+
2072
+
2073 Json::Value jv;
+
2074 if (BEAST_EXPECT(parseJSONString(json, jv)))
+
2075 {
+
2076 STParsedJSONObject parsed("test", jv);
+
2077 BEAST_EXPECT(!parsed.object);
+
2078 BEAST_EXPECT(parsed.error);
+
2079 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
+
2080 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransactionResult' is out of range.");
+
2081 }
+
2082 }
+
2083
+
2084 {
+
2085 std::string const json(
+
2086 R"({"CloseResolution":19,"Method":"pony",)"
+
2087 R"("TransactionResult":"tesSUCCESS"})");
+
2088
+
2089 Json::Value jv;
+
2090 if (BEAST_EXPECT(parseJSONString(json, jv)))
+
2091 {
+
2092 STParsedJSONObject parsed("test", jv);
+
2093 BEAST_EXPECT(!parsed.object);
+
2094 BEAST_EXPECT(parsed.error);
+
2095 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
+
2096 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.Method' has bad type.");
+
2097 }
+
2098 }
+
2099
+
2100 {
+
2101 std::string const json(
+
2102 R"({"CloseResolution":19,"Method":3294967296,)"
+
2103 R"("TransactionResult":"tesSUCCESS"})");
+
2104
+
2105 Json::Value jv;
+
2106 if (BEAST_EXPECT(parseJSONString(json, jv)))
+
2107 {
+
2108 STParsedJSONObject parsed("test", jv);
+
2109 BEAST_EXPECT(!parsed.object);
+
2110 BEAST_EXPECT(parsed.error);
+
2111 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
+
2112 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.Method' is out of range.");
+
2113 }
+
2114 }
+
2115
+
2116 {
+
2117 std::string const json(
+
2118 R"({"CloseResolution":-10,"Method":42,)"
+
2119 R"("TransactionResult":"tesSUCCESS"})");
+
2120
+
2121 Json::Value jv;
+
2122 if (BEAST_EXPECT(parseJSONString(json, jv)))
+
2123 {
+
2124 STParsedJSONObject parsed("test", jv);
+
2125 BEAST_EXPECT(!parsed.object);
+
2126 BEAST_EXPECT(parsed.error);
+
2127 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
+
2128 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.CloseResolution' is out of range.");
+
2129 }
+
2130 }
+
2131
+
2132 {
+
2133 std::string const json(
+
2134 R"({"CloseResolution":19,"Method":3.141592653,)"
+
2135 R"("TransactionResult":"tesSUCCESS"})");
+
2136
+
2137 Json::Value jv;
+
2138 if (BEAST_EXPECT(parseJSONString(json, jv)))
+
2139 {
+
2140 STParsedJSONObject parsed("test", jv);
+
2141 BEAST_EXPECT(!parsed.object);
+
2142 BEAST_EXPECT(parsed.error);
+
2143 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
+
2144 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.Method' has bad type.");
+
2145 }
+
2146 }
+
2147
+
2148 {
+
2149 std::string const goodJson(
+
2150 R"({"CloseResolution":19,"Method":250,)"
+
2151 R"("TransferFee":"65535"})");
+
2152 std::string const expectedJson(
+
2153 R"({"CloseResolution":19,"Method":250,)"
+
2154 R"("TransferFee":65535})");
+
2155
+
2156 Json::Value jv;
+
2157 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
+
2158 {
+
2159 STParsedJSONObject parsed("test", jv);
+
2160 if (BEAST_EXPECT(parsed.object))
+
2161 {
+
2162 std::string const& serialized(to_string(parsed.object->getJson(JsonOptions::none)));
+
2163 BEAST_EXPECT(serialized == expectedJson);
+
2164 }
+
2165 }
+
2166 }
+
2167
+
2168 {
+
2169 std::string const json(
+
2170 R"({"CloseResolution":19,"Method":250,)"
+
2171 R"("TransferFee":"65536"})");
+
2172
+
2173 Json::Value jv;
+
2174 if (BEAST_EXPECT(parseJSONString(json, jv)))
+
2175 {
+
2176 STParsedJSONObject parsed("test", jv);
+
2177 BEAST_EXPECT(!parsed.object);
+
2178 BEAST_EXPECT(parsed.error);
+
2179 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
+
2180 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransferFee' has invalid data.");
+
2181 }
+
2182 }
+
2183
+
2184 {
+
2185 std::string const json(
+
2186 R"({"CloseResolution":19,"Method":250,)"
+
2187 R"("TransferFee":"Payment"})");
+
2188
+
2189 Json::Value jv;
+
2190 if (BEAST_EXPECT(parseJSONString(json, jv)))
+
2191 {
+
2192 STParsedJSONObject parsed("test", jv);
+
2193 BEAST_EXPECT(!parsed.object);
+
2194 BEAST_EXPECT(parsed.error);
+
2195 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
+
2196 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransferFee' has invalid data.");
+
2197 }
+
2198 }
+
2199
+
2200 {
+
2201 std::string const json(
+
2202 R"({"CloseResolution":19,"Method":250,)"
+
2203 R"("TransferFee":true})");
+
2204
+
2205 Json::Value jv;
+
2206 if (BEAST_EXPECT(parseJSONString(json, jv)))
+
2207 {
+
2208 STParsedJSONObject parsed("test", jv);
+
2209 BEAST_EXPECT(!parsed.object);
+
2210 BEAST_EXPECT(parsed.error);
+
2211 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
+
2212 BEAST_EXPECT(parsed.error[jss::error_message] == "Field 'test.TransferFee' has bad type.");
+
2213 }
+
2214 }
+
2215 }
-
2201
-
2202 void
-
-
2203 run() override
-
2204 {
-
2205 // Instantiate a jtx::Env so debugLog writes are exercised.
-
2206 test::jtx::Env env(*this);
-
2207 testUInt8();
-
2208 testUInt16();
-
2209 testUInt32();
-
2210 testUInt64();
-
2211 testUInt128();
-
2212 testUInt160();
-
2213 testUInt192();
-
2214 testUInt256();
-
2215 testInt32();
-
2216 testBlob();
-
2217 testVector256();
-
2218 testAccount();
-
2219 testCurrency();
-
2220 testAmount();
-
2221 testPathSet();
-
2222 testIssue();
- -
2224 testNumber();
-
2225 testObject();
-
2226 testArray();
-
2227 testEdgeCases();
-
2228 }
+
2216
+
2217 void
+
+
2218 run() override
+
2219 {
+
2220 // Instantiate a jtx::Env so debugLog writes are exercised.
+
2221 test::jtx::Env env(*this);
+
2222 testUInt8();
+
2223 testUInt16();
+
2224 testUInt32();
+
2225 testUInt64();
+
2226 testUInt128();
+
2227 testUInt160();
+
2228 testUInt192();
+
2229 testUInt256();
+
2230 testInt32();
+
2231 testBlob();
+
2232 testVector256();
+
2233 testAccount();
+
2234 testCurrency();
+
2235 testAmount();
+
2236 testPathSet();
+
2237 testIssue();
+ +
2239 testNumber();
+
2240 testObject();
+
2241 testArray();
+
2242 testEdgeCases();
+
2243 }
-
2229};
+
2244};
-
2230
-
2231BEAST_DEFINE_TESTSUITE(STParsedJSON, protocol, xrpl);
-
2232
-
2233} // namespace xrpl
+
2245
+
2246BEAST_DEFINE_TESTSUITE(STParsedJSON, protocol, xrpl);
+
2247
+
2248} // namespace xrpl
T all_of(T... args)
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
-
bool isObject() const
+
Value & append(Value const &value)
Append value to array at the end.
+
bool isObject() const
A testsuite class.
Definition suite.h:51
bool unexpected(Condition shouldBeFalse, String const &reason)
Definition suite.h:482
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
@@ -2391,7 +2406,7 @@ $(document).ready(function() { init_codefold(0); }); -
void run() override
Runs the suite.
+
void run() override
Runs the suite.
diff --git a/STPathSet_8cpp_source.html b/STPathSet_8cpp_source.html index b78942efaf..9c80c35b20 100644 --- a/STPathSet_8cpp_source.html +++ b/STPathSet_8cpp_source.html @@ -334,7 +334,7 @@ $(document).ready(function() { init_codefold(0); });
227
228} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Identifies fields.
Definition SField.h:126
A type which can be exported to a well known binary format.
Definition STBase.h:115
SField const & getFName() const
Definition STBase.cpp:122
diff --git a/STTakesAsset_8cpp_source.html b/STTakesAsset_8cpp_source.html index a3e8cb3ae2..fd4e5f8600 100644 --- a/STTakesAsset_8cpp_source.html +++ b/STTakesAsset_8cpp_source.html @@ -131,8 +131,8 @@ $(document).ready(function() { init_codefold(0); });
virtual bool isDefault() const
Definition STBase.cpp:109
SOEStyle getStyle(SField const &field) const
Definition STObject.cpp:558
-
int getCount() const
Definition STObject.h:982
-
STBase & getIndex(int offset)
Definition STObject.h:994
+
int getCount() const
Definition STObject.h:988
+
STBase & getIndex(int offset)
Definition STObject.h:1000
void makeFieldAbsent(SField const &field)
Definition STObject.cpp:525
Intermediate class for any STBase-derived class to store an Asset.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/STTx_8cpp_source.html b/STTx_8cpp_source.html index ce991aadff..ab3066476e 100644 --- a/STTx_8cpp_source.html +++ b/STTx_8cpp_source.html @@ -944,22 +944,22 @@ $(document).ready(function() { init_codefold(0); });
static STBase * emplace(std::size_t n, void *buf, T &&val)
Definition STBase.h:213
-
STBase const * peekAtPIndex(int offset) const
Definition STObject.h:1000
+
STBase const * peekAtPIndex(int offset) const
Definition STObject.h:1006
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STObject.cpp:814
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:624
-
void addWithoutSigningFields(Serializer &s) const
Definition STObject.h:957
+
void addWithoutSigningFields(Serializer &s) const
Definition STObject.h:963
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:576
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:760
-
T::value_type operator[](TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1013
+
T::value_type operator[](TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1019
void applyTemplate(SOTemplate const &type)
Definition STObject.cpp:148
uint256 getHash(HashPrefix prefix) const
Definition STObject.cpp:350
std::string getFullText() const override
Definition STObject.cpp:277
STArray const & getFieldArray(SField const &field) const
Definition STObject.cpp:663
STObject & peekFieldObject(SField const &field)
Definition STObject.cpp:450
void add(Serializer &s) const override
Definition STObject.cpp:117
-
Serializer getSerializer() const
Definition STObject.h:966
+
Serializer getSerializer() const
Definition STObject.h:972
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
-
int getCount() const
Definition STObject.h:982
+
int getCount() const
Definition STObject.h:988
void setFieldU16(SField const &field, std::uint16_t)
Definition STObject.cpp:712
STBase const & peekAtField(SField const &field) const
Definition STObject.cpp:384
void set(SOTemplate const &)
Definition STObject.cpp:132
diff --git a/STTx__test_8cpp_source.html b/STTx__test_8cpp_source.html index dd732be5d8..faa5ec3b44 100644 --- a/STTx__test_8cpp_source.html +++ b/STTx__test_8cpp_source.html @@ -1423,14 +1423,14 @@ $(document).ready(function() { init_codefold(0); });
Defines the fields and their attributes within a STObject.
Definition SOTemplate.h:88
- +
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:760
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:718
void setFieldArray(SField const &field, STArray const &v)
Definition STObject.cpp:802
void setFieldAmount(SField const &field, STAmount const &)
Definition STObject.cpp:772
void add(Serializer &s) const override
Definition STObject.cpp:117
-
Serializer getSerializer() const
Definition STObject.h:966
+
Serializer getSerializer() const
Definition STObject.h:972
void setFieldU16(SField const &field, std::uint16_t)
Definition STObject.cpp:712
void setFieldPathSet(SField const &field, STPathSet const &)
Definition STObject.cpp:796
void setAccountID(SField const &field, AccountID const &)
Definition STObject.cpp:754
diff --git a/STVector256_8cpp_source.html b/STVector256_8cpp_source.html index 33ee9f872f..35e6775873 100644 --- a/STVector256_8cpp_source.html +++ b/STVector256_8cpp_source.html @@ -180,7 +180,7 @@ $(document).ready(function() { init_codefold(0); });
81
82} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Identifies fields.
Definition SField.h:126
A type which can be exported to a well known binary format.
Definition STBase.h:115
SField const & getFName() const
Definition STBase.cpp:122
diff --git a/STVector256_8h_source.html b/STVector256_8h_source.html index 0de20efa1c..fb6159049e 100644 --- a/STVector256_8h_source.html +++ b/STVector256_8h_source.html @@ -231,161 +231,162 @@ $(document).ready(function() { init_codefold(0); });
136
-
138inline STVector256::operator std::vector<uint256>() const
-
139{
-
140 return mValue;
-
141}
+
138inline STVector256::
+
139operator std::vector<uint256>() const
+
140{
+
141 return mValue;
+
142}
-
142
-
143inline std::size_t
-
- -
145{
-
146 return mValue.size();
-
147}
+
143
+
144inline std::size_t
+
+ +
146{
+
147 return mValue.size();
+
148}
-
148
-
149inline void
-
- -
151{
-
152 return mValue.resize(n);
-
153}
+
149
+
150inline void
+
+ +
152{
+
153 return mValue.resize(n);
+
154}
-
154
-
155inline bool
-
- -
157{
-
158 return mValue.empty();
-
159}
+
155
+
156inline bool
+
+ +
158{
+
159 return mValue.empty();
+
160}
-
160
- -
- -
163{
-
164 return mValue[n];
-
165}
+
161
+ + -
166
- -
- -
169{
-
170 return mValue[n];
-
171}
+
167
+ +
+ +
170{
+
171 return mValue[n];
+
172}
-
172
-
173inline std::vector<uint256> const&
-
- -
175{
-
176 return mValue;
-
177}
+
173
+
174inline std::vector<uint256> const&
+
+ +
176{
+
177 return mValue;
+
178}
-
178
- -
- -
181{
-
182 return mValue.insert(pos, value);
-
183}
+
179
+ +
+ +
182{
+
183 return mValue.insert(pos, value);
+
184}
-
184
- -
- -
187{
-
188 return mValue.insert(pos, std::move(value));
-
189}
+
185
+ +
+ +
188{
+
189 return mValue.insert(pos, std::move(value));
+
190}
-
190
-
191inline void
-
- -
193{
-
194 mValue.push_back(v);
-
195}
+
191
+
192inline void
+
+ +
194{
+
195 mValue.push_back(v);
+
196}
-
196
- -
- -
199{
-
200 return mValue.begin();
-
201}
+
197
+ +
+ +
200{
+
201 return mValue.begin();
+
202}
-
202
- -
- -
205{
-
206 return mValue.begin();
-
207}
+
203
+ +
+ +
206{
+
207 return mValue.begin();
+
208}
-
208
- -
- -
211{
-
212 return mValue.end();
-
213}
+
209
+ +
+ +
212{
+
213 return mValue.end();
+
214}
-
214
- -
- -
217{
-
218 return mValue.end();
-
219}
+
215
+ +
+ +
218{
+
219 return mValue.end();
+
220}
-
220
- -
- -
223{
-
224 return mValue.erase(position);
-
225}
+
221
+ +
+ +
224{
+
225 return mValue.erase(position);
+
226}
-
226
-
227inline void
-
- -
229{
-
230 return mValue.clear();
-
231}
+
227
+
228inline void
+
+ +
230{
+
231 return mValue.clear();
+
232}
-
232
-
233} // namespace xrpl
+
233
+
234} // namespace xrpl
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
Tracks the number of instances of an object.
Identifies fields.
Definition SField.h:126
A type which can be exported to a well known binary format.
Definition STBase.h:115
-
std::vector< uint256 >::iterator erase(std::vector< uint256 >::iterator position)
-
void clear() noexcept
+
std::vector< uint256 >::iterator erase(std::vector< uint256 >::iterator position)
+
void clear() noexcept
void setValue(STVector256 const &v)
-
std::size_t size() const
+
std::size_t size() const
void add(Serializer &s) const override
-
void resize(std::size_t n)
-
bool empty() const
+
void resize(std::size_t n)
+
bool empty() const
bool isEquivalent(STBase const &t) const override
friend class detail::STVar
-
std::vector< uint256 >::iterator insert(std::vector< uint256 >::const_iterator pos, uint256 const &value)
-
std::vector< uint256 > const & value() const
+
std::vector< uint256 >::iterator insert(std::vector< uint256 >::const_iterator pos, uint256 const &value)
+
std::vector< uint256 > const & value() const
STBase * move(std::size_t n, void *buf) override
std::vector< uint256 > mValue
Definition STVector256.h:12
SerializedTypeID getSType() const override
-
std::vector< uint256 >::iterator begin()
+
std::vector< uint256 >::iterator begin()
STVector256()=default
STVector256 & operator=(std::vector< uint256 > const &v)
-
std::vector< uint256 >::reference operator[](std::vector< uint256 >::size_type n)
+
std::vector< uint256 >::reference operator[](std::vector< uint256 >::size_type n)
bool isDefault() const override
-
void push_back(uint256 const &v)
+
void push_back(uint256 const &v)
STBase * copy(std::size_t n, void *buf) const override
-
std::vector< uint256 >::iterator end()
+
std::vector< uint256 >::iterator end()
Json::Value getJson(JsonOptions) const override
diff --git a/STXChainBridge_8cpp_source.html b/STXChainBridge_8cpp_source.html index 9f7d273b19..9c0501e512 100644 --- a/STXChainBridge_8cpp_source.html +++ b/STXChainBridge_8cpp_source.html @@ -313,10 +313,10 @@ $(document).ready(function() { init_codefold(0); });
196} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
const_iterator begin() const
-
const_iterator end() const
-
bool isString() const
-
bool isObject() const
+
const_iterator begin() const
+
const_iterator end() const
+
bool isString() const
+
bool isObject() const
std::string asString() const
Returns the unquoted string value.
A currency issued by an account.
Definition Issue.h:13
Identifies fields.
Definition SField.h:126
diff --git a/ServerDefinitions_8cpp_source.html b/ServerDefinitions_8cpp_source.html index 8f7d3fc75d..c2c4ddade8 100644 --- a/ServerDefinitions_8cpp_source.html +++ b/ServerDefinitions_8cpp_source.html @@ -392,7 +392,7 @@ $(document).ready(function() { init_codefold(0); });
Outputs a Value in JSON format without formatting (not human friendly).
Definition json_writer.h:34
std::string write(Value const &root) override
Represents a JSON value.
Definition json_value.h:130
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
static LedgerFormats const & getInstance()
static std::unordered_map< int, SField const * > const & getKnownCodeToField()
Definition SField.h:287
An immutable linear range of bytes.
Definition Slice.h:26
diff --git a/ServerHandler_8cpp_source.html b/ServerHandler_8cpp_source.html index 63e587efd7..0589d7abf0 100644 --- a/ServerHandler_8cpp_source.html +++ b/ServerHandler_8cpp_source.html @@ -1317,18 +1317,18 @@ $(document).ready(function() { init_codefold(0); });
std::string getFormattedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
Value & append(Value const &value)
Append value to array at the end.
+
bool isArray() const
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
bool isObjectOrNull() const
+
bool isObjectOrNull() const
Int asInt() const
-
bool isString() const
-
bool isObject() const
-
Value removeMember(char const *key)
Remove and return the named member.
+
bool isString() const
+
bool isObject() const
+
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
-
bool isNull() const
isNull() tests to see if this field is null.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
bool isInt() const
+
bool isNull() const
isNull() tests to see if this field is null.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isInt() const
A version-independent IP address and port combination.
Definition IPEndpoint.h:18
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:318
@@ -1430,7 +1430,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static std::map< std::string, std::string > build_map(boost::beast::http::fields const &h)
void parse_Port(ParsedPort &port, Section const &section, std::ostream &log)
Definition Port.cpp:191
-
void HTTPReply(int nStatus, std::string const &strMsg, Json::Output const &, beast::Journal j)
+
void HTTPReply(int nStatus, std::string const &strMsg, Json::Output const &, beast::Journal j)
static std::string buffers_to_string(ConstBufferSequence const &bs)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
static void setup_Client(ServerHandler::Setup &setup)
@@ -1458,7 +1458,7 @@ $(document).ready(function() { init_codefold(0); });
std::unique_ptr< Server > make_Server(Handler &handler, boost::asio::io_context &io_context, beast::Journal journal)
Create the HTTP server using the specified handler.
Definition Server.h:15
Json::Int constexpr server_overloaded
void logDuration(Json::Value const &request, T const &duration, beast::Journal &journal)
-
Overlay::Setup setup_Overlay(BasicConfig const &config)
+
Overlay::Setup setup_Overlay(BasicConfig const &config)
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:96
std::shared_ptr< boost::asio::ssl::context > make_SSLContextAuthed(std::string const &keyFile, std::string const &certFile, std::string const &chainFile, std::string const &cipherList)
Create an authenticated SSL context using the specified files.
static Json::Output makeOutput(Session &session)
diff --git a/ServerInfo_8cpp_source.html b/ServerInfo_8cpp_source.html index f1d60ef5c8..44c8748bc3 100644 --- a/ServerInfo_8cpp_source.html +++ b/ServerInfo_8cpp_source.html @@ -109,7 +109,7 @@ $(document).ready(function() { init_codefold(0); });
24} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
bool asBool() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Json::Value getServerInfo(bool human, bool admin, bool counters)=0
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/ServerState_8cpp_source.html b/ServerState_8cpp_source.html index 300f3f57a7..7871db0152 100644 --- a/ServerState_8cpp_source.html +++ b/ServerState_8cpp_source.html @@ -108,7 +108,7 @@ $(document).ready(function() { init_codefold(0); });
23} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
bool asBool() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Json::Value getServerInfo(bool human, bool admin, bool counters)=0
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/ServerStatus__test_8cpp_source.html b/ServerStatus__test_8cpp_source.html index 2b56bdfb98..35d23c412d 100644 --- a/ServerStatus__test_8cpp_source.html +++ b/ServerStatus__test_8cpp_source.html @@ -1193,7 +1193,7 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
Mix-in to support tests using asio coroutines.
Definition yield_to.h:28
boost::asio::io_context & get_io_context()
Return the io_context associated with the object.
Definition yield_to.h:59
void yield_to(F0 &&f0, FN &&... fn)
Run one or more functions, each in a coroutine.
Definition yield_to.h:98
diff --git a/SetAuth__test_8cpp_source.html b/SetAuth__test_8cpp_source.html index c9c3dc364b..f22006b2e4 100644 --- a/SetAuth__test_8cpp_source.html +++ b/SetAuth__test_8cpp_source.html @@ -166,7 +166,7 @@ $(document).ready(function() { init_codefold(0); });
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:214
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
diff --git a/SetOracle_8cpp_source.html b/SetOracle_8cpp_source.html index aa78248b16..08c8296969 100644 --- a/SetOracle_8cpp_source.html +++ b/SetOracle_8cpp_source.html @@ -442,7 +442,7 @@ $(document).ready(function() { init_codefold(0); });
static std::pair< Currency, Currency > tokenPairKey(STObject const &pair)
Definition SetOracle.cpp:13
std::size_t constexpr maxOracleDataSeries
The maximum size of a data series array inside an Oracle.
Definition Protocol.h:274
@ tefINTERNAL
Definition TER.h:153
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
static void setPriceDataInnerObjTemplate(STObject &obj)
std::size_t constexpr maxPriceScale
The maximum price scaling factor.
Definition Protocol.h:286
@@ -461,7 +461,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecTOKEN_PAIR_NOT_FOUND
Definition TER.h:336
std::size_t constexpr maxOracleProvider
The maximum length of a Provider inside an Oracle.
Definition Protocol.h:271
std::size_t constexpr maxOracleURI
The maximum length of a URI inside an Oracle.
Definition Protocol.h:268
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
diff --git a/SetSignerList_8cpp_source.html b/SetSignerList_8cpp_source.html index 822495229d..7c4fd978bb 100644 --- a/SetSignerList_8cpp_source.html +++ b/SetSignerList_8cpp_source.html @@ -500,7 +500,7 @@ $(document).ready(function() { init_codefold(0); });
size_type size() const
Definition STArray.h:223
STObject & back()
Definition STArray.h:168
-
void reserve(std::size_t n)
Definition STObject.h:945
+
void reserve(std::size_t n)
Definition STObject.h:951
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:72
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:618
@@ -548,7 +548,7 @@ $(document).ready(function() { init_codefold(0); });
@ tefBAD_LEDGER
Definition TER.h:150
@ tefINTERNAL
Definition TER.h:153
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:955
static TER removeSignersFromLedger(Application &app, ApplyView &view, Keylet const &accountKeylet, Keylet const &ownerDirKeylet, Keylet const &signerListKeylet, beast::Journal j)
diff --git a/SetTrust_8cpp_source.html b/SetTrust_8cpp_source.html index 9fe85f55dd..e39051d72a 100644 --- a/SetTrust_8cpp_source.html +++ b/SetTrust_8cpp_source.html @@ -738,7 +738,7 @@ $(document).ready(function() { init_codefold(0); });
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:118
std::string getFullText() const override
Definition STAmount.cpp:629
-
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:554
+
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:556
Currency const & getCurrency() const
Definition STAmount.h:460
bool native() const noexcept
Definition STAmount.h:416
AccountID const & getIssuer() const
Definition STAmount.h:466
@@ -775,7 +775,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:96
@ tefNO_AUTH_REQUIRED
Definition TER.h:154
@ tefINTERNAL
Definition TER.h:153
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Definition View.cpp:1020
void loadGranularPermission(std::shared_ptr< SLE const > const &delegate, TxType const &type, std::unordered_set< GranularPermissionType > &granularPermissions)
Load the granular permissions granted to the delegate account for the specified transaction type.
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:99
diff --git a/SetTrust__test_8cpp_source.html b/SetTrust__test_8cpp_source.html index 24968fb820..b303b3c127 100644 --- a/SetTrust__test_8cpp_source.html +++ b/SetTrust__test_8cpp_source.html @@ -740,7 +740,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
FeatureBitset testable_amendments()
Definition Env.h:76
diff --git a/SignHandler_8cpp_source.html b/SignHandler_8cpp_source.html index 5be6520dd9..d264efce8a 100644 --- a/SignHandler_8cpp_source.html +++ b/SignHandler_8cpp_source.html @@ -127,7 +127,7 @@ $(document).ready(function() { init_codefold(0); });
42} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
bool asBool() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
bool canSign() const
Definition Config.h:324
std::chrono::seconds getValidatedLedgerAge()
diff --git a/Sign_8cpp_source.html b/Sign_8cpp_source.html index 0461c0f8d7..9006487d3e 100644 --- a/Sign_8cpp_source.html +++ b/Sign_8cpp_source.html @@ -174,7 +174,7 @@ $(document).ready(function() { init_codefold(0); });
83} // namespace xrpl
A public key.
Definition PublicKey.h:42
-
void addWithoutSigningFields(Serializer &s) const
Definition STObject.h:957
+
void addWithoutSigningFields(Serializer &s) const
Definition STObject.h:963
A secret key.
Definition SecretKey.h:18
Slice slice() const noexcept
Definition Serializer.h:44
diff --git a/SignerEntries_8cpp_source.html b/SignerEntries_8cpp_source.html index 735761bd0c..8be727e4f1 100644 --- a/SignerEntries_8cpp_source.html +++ b/SignerEntries_8cpp_source.html @@ -146,7 +146,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ temMALFORMED
Definition TER.h:67
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
T reserve(T... args)
diff --git a/SignerUtils_8h_source.html b/SignerUtils_8h_source.html index f8bf1dd095..46e7f245f5 100644 --- a/SignerUtils_8h_source.html +++ b/SignerUtils_8h_source.html @@ -143,8 +143,8 @@ $(document).ready(function() { init_codefold(0); });
46} // namespace jtx
47} // namespace test
48} // namespace xrpl
-
const_iterator begin() const
-
const_iterator end() const
+
const_iterator begin() const
+
const_iterator end() const
Immutable cryptographic account descriptor.
Definition Account.h:19
Set the regular signature on a JTx.
Definition sig.h:15
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
diff --git a/Simulate_8cpp_source.html b/Simulate_8cpp_source.html index c62a9946e1..9fa1b29364 100644 --- a/Simulate_8cpp_source.html +++ b/Simulate_8cpp_source.html @@ -436,11 +436,11 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
UInt size() const
Number of values in array or object.
-
bool isObject() const
-
bool isBool() const
+
bool isObject() const
+
bool isBool() const
bool asBool() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
Stream debug() const
Definition Journal.h:300
virtual Config & config()=0
uint32_t NETWORK_ID
Definition Config.h:138
diff --git a/Simulate__test_8cpp_source.html b/Simulate__test_8cpp_source.html index 9af4be1b67..a1fd460a31 100644 --- a/Simulate__test_8cpp_source.html +++ b/Simulate__test_8cpp_source.html @@ -1254,12 +1254,12 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
Value removeMember(char const *key)
Remove and return the named member.
+
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:516
@@ -1319,7 +1319,7 @@ $(document).ready(function() { init_codefold(0); });
uint256 getNextID(jtx::Env const &env, jtx::Account const &issuer, std::uint32_t nfTokenTaxon, std::uint16_t flags, std::uint16_t xferFee)
Get the next NFTokenID that will be issued.
Definition token.cpp:49
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
std::uint32_t ownerCount(Env const &env, Account const &account)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition regkey.cpp:10
diff --git a/SlabAllocator_8h_source.html b/SlabAllocator_8h_source.html index 97316d119c..a815a2734c 100644 --- a/SlabAllocator_8h_source.html +++ b/SlabAllocator_8h_source.html @@ -293,7 +293,7 @@ $(document).ready(function() { init_codefold(0); });
215 // clang-format off
216 if (!buf) [[unlikely]]
217 return nullptr;
-
218 // clang-format on
+
218 // clang-format on
219
220#if BOOST_OS_LINUX
221 // When allocating large blocks, attempt to leverage Linux's
diff --git a/Status__test_8cpp_source.html b/Status__test_8cpp_source.html index 6eb9af065b..28f94abda0 100644 --- a/Status__test_8cpp_source.html +++ b/Status__test_8cpp_source.html @@ -294,7 +294,7 @@ $(document).ready(function() { init_codefold(0); });
187} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
void clear()
Remove all object members and array elements.
+
void clear()
Remove all object members and array elements.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:221
diff --git a/StepChecks_8h_source.html b/StepChecks_8h_source.html index d35cdbbece..2c955abdd9 100644 --- a/StepChecks_8h_source.html +++ b/StepChecks_8h_source.html @@ -184,7 +184,7 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_LINE
Definition TER.h:199
@ terNO_RIPPLE
Definition TER.h:204
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
TER checkFreeze(ReadView const &view, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition StepChecks.h:13
@ tecINTERNAL
Definition TER.h:291
TER checkNoRipple(ReadView const &view, AccountID const &prev, AccountID const &cur, AccountID const &next, Currency const &currency, beast::Journal j)
Definition StepChecks.h:59
diff --git a/Steps_8h_source.html b/Steps_8h_source.html index b7c6145235..26113faedc 100644 --- a/Steps_8h_source.html +++ b/Steps_8h_source.html @@ -542,7 +542,7 @@ $(document).ready(function() { init_codefold(0); });
StrandDirection
Definition Steps.h:24
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
QualityDirection
Definition Steps.h:23
diff --git a/Submit_8cpp_source.html b/Submit_8cpp_source.html index 243b4b3e6c..161a16397d 100644 --- a/Submit_8cpp_source.html +++ b/Submit_8cpp_source.html @@ -249,7 +249,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
bool asBool() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
virtual bool checkSigs() const =0
bool canSign() const
Definition Config.h:324
diff --git a/Subscribe_8cpp_source.html b/Subscribe_8cpp_source.html index f1c3c721cc..dc49fe008e 100644 --- a/Subscribe_8cpp_source.html +++ b/Subscribe_8cpp_source.html @@ -435,11 +435,11 @@ $(document).ready(function() { init_codefold(0); });
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
Value & append(Value const &value)
Append value to array at the end.
-
bool isString() const
+
bool isArray() const
+
Value & append(Value const &value)
Append value to array at the end.
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
Stream debug() const
Definition Journal.h:300
Stream info() const
Definition Journal.h:306
Stream trace() const
Severity stream access functions.
Definition Journal.h:294
diff --git a/Subscribe__test_8cpp_source.html b/Subscribe__test_8cpp_source.html index 6d888bd258..b6816b9394 100644 --- a/Subscribe__test_8cpp_source.html +++ b/Subscribe__test_8cpp_source.html @@ -1570,7 +1570,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
void clear()
Remove all object members and array elements.
+
void clear()
Remove all object members and array elements.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
virtual Config & config()=0
@@ -1643,7 +1643,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value cancelOffer(jtx::Account const &account, std::initializer_list< uint256 > const &nftokenOffers)
Cancel NFTokenOffers.
Definition token.cpp:132
Json::Value createOffer(jtx::Account const &account, uint256 const &nftokenID, STAmount const &amount)
Create an NFTokenOffer.
Definition token.cpp:87
uint256 getNextID(jtx::Env const &env, jtx::Account const &issuer, std::uint32_t nfTokenTaxon, std::uint16_t flags, std::uint16_t xferFee)
Get the next NFTokenID that will be issued.
Definition token.cpp:49
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
static autofill_t const autofill
Definition tags.h:22
FeatureBitset testable_amendments()
Definition Env.h:76
diff --git a/TER_8h_source.html b/TER_8h_source.html index cf519c47eb..a95e77e0b7 100644 --- a/TER_8h_source.html +++ b/TER_8h_source.html @@ -612,252 +612,246 @@ $(document).ready(function() { init_codefold(0); });
485template <typename L, typename R>
486constexpr auto
-
487operator==(L const& lhs, R const& rhs)
- -
489 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
-
490 bool>
-
491{
-
492 return TERtoInt(lhs) == TERtoInt(rhs);
-
493}
+
487operator==(L const& lhs, R const& rhs) -> std::enable_if_t<
+
488 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
+
489 bool>
+
490{
+
491 return TERtoInt(lhs) == TERtoInt(rhs);
+
492}
-
494
-
495template <typename L, typename R>
-
496constexpr auto
-
-
497operator!=(L const& lhs, R const& rhs)
- -
499 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
-
500 bool>
-
501{
-
502 return TERtoInt(lhs) != TERtoInt(rhs);
-
503}
+
493
+
494template <typename L, typename R>
+
495constexpr auto
+
+
496operator!=(L const& lhs, R const& rhs) -> std::enable_if_t<
+
497 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
+
498 bool>
+
499{
+
500 return TERtoInt(lhs) != TERtoInt(rhs);
+
501}
-
504
-
505template <typename L, typename R>
-
-
506constexpr auto
-
507operator<(L const& lhs, R const& rhs)
- -
509 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
-
510 bool>
-
511{
-
512 return TERtoInt(lhs) < TERtoInt(rhs);
-
513}
+
502
+
503template <typename L, typename R>
+
+
504constexpr auto
+
505operator<(L const& lhs, R const& rhs) -> std::enable_if_t<
+
506 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
+
507 bool>
+
508{
+
509 return TERtoInt(lhs) < TERtoInt(rhs);
+
510}
-
514
-
515template <typename L, typename R>
-
-
516constexpr auto
-
517operator<=(L const& lhs, R const& rhs)
- -
519 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
-
520 bool>
-
521{
-
522 return TERtoInt(lhs) <= TERtoInt(rhs);
-
523}
+
511
+
512template <typename L, typename R>
+
+
513constexpr auto
+
514operator<=(L const& lhs, R const& rhs) -> std::enable_if_t<
+
515 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
+
516 bool>
+
517{
+
518 return TERtoInt(lhs) <= TERtoInt(rhs);
+
519}
-
524
-
525template <typename L, typename R>
-
526constexpr auto
-
-
527operator>(L const& lhs, R const& rhs)
- -
529 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
-
530 bool>
-
531{
-
532 return TERtoInt(lhs) > TERtoInt(rhs);
-
533}
+
520
+
521template <typename L, typename R>
+
522constexpr auto
+
+
523operator>(L const& lhs, R const& rhs) -> std::enable_if_t<
+
524 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
+
525 bool>
+
526{
+
527 return TERtoInt(lhs) > TERtoInt(rhs);
+
528}
-
534
-
535template <typename L, typename R>
-
536constexpr auto
-
-
537operator>=(L const& lhs, R const& rhs)
- -
539 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
-
540 bool>
-
541{
-
542 return TERtoInt(lhs) >= TERtoInt(rhs);
-
543}
+
529
+
530template <typename L, typename R>
+
531constexpr auto
+
+
532operator>=(L const& lhs, R const& rhs) -> std::enable_if_t<
+
533 std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
+
534 bool>
+
535{
+
536 return TERtoInt(lhs) >= TERtoInt(rhs);
+
537}
-
544
-
545//------------------------------------------------------------------------------
-
546
-
547// Use traits to build a TERSubset that can convert from any of the TE*codes
-
548// enums *except* TECcodes: NotTEC
-
549
-
550// NOTE: NotTEC is useful for codes returned by preflight in transactors.
-
551// Preflight checks occur prior to signature checking. If preflight returned
-
552// a tec code, then a malicious user could submit a transaction with a very
-
553// large fee and have that fee charged against an account without using that
-
554// account's valid signature.
-
555template <typename FROM>
-
- -
557{
-
558};
+
538
+
539//------------------------------------------------------------------------------
+
540
+
541// Use traits to build a TERSubset that can convert from any of the TE*codes
+
542// enums *except* TECcodes: NotTEC
+
543
+
544// NOTE: NotTEC is useful for codes returned by preflight in transactors.
+
545// Preflight checks occur prior to signature checking. If preflight returned
+
546// a tec code, then a malicious user could submit a transaction with a very
+
547// large fee and have that fee charged against an account without using that
+
548// account's valid signature.
+
549template <typename FROM>
+
+ +
551{
+
552};
-
559template <>
-
- -
561{
-
562};
+
553template <>
+
+ +
555{
+
556};
-
563template <>
-
- -
565{
-
566};
+
557template <>
+
+ +
559{
+
560};
-
567template <>
-
- -
569{
-
570};
+
561template <>
+
+ +
563{
+
564};
-
571template <>
-
- -
573{
-
574};
+
565template <>
+
+ +
567{
+
568};
-
575template <>
-
- -
577{
-
578};
+
569template <>
+
+ +
571{
+
572};
-
579
- -
581
-
582//------------------------------------------------------------------------------
-
583
-
584// Use traits to build a TERSubset that can convert from any of the TE*codes
-
585// enums as well as from NotTEC.
-
586template <typename FROM>
-
- -
588{
-
589};
+
573
+ +
575
+
576//------------------------------------------------------------------------------
+
577
+
578// Use traits to build a TERSubset that can convert from any of the TE*codes
+
579// enums as well as from NotTEC.
+
580template <typename FROM>
+
+ +
582{
+
583};
-
590template <>
-
- -
592{
-
593};
+
584template <>
+
+ +
586{
+
587};
-
594template <>
-
- -
596{
-
597};
+
588template <>
+
+ +
590{
+
591};
-
598template <>
-
- -
600{
-
601};
+
592template <>
+
+ +
594{
+
595};
-
602template <>
-
- -
604{
-
605};
+
596template <>
+
+ +
598{
+
599};
-
606template <>
-
- -
608{
-
609};
+
600template <>
+
+ +
602{
+
603};
-
610template <>
-
- -
612{
-
613};
+
604template <>
+
+ +
606{
+
607};
-
614template <>
-
- -
616{
-
617};
+
608template <>
+
+ +
610{
+
611};
+
+
612
+
613// TER allows all of the subsets.
+ +
615
+
616//------------------------------------------------------------------------------
+
617
+
618inline bool
+
+
619isTelLocal(TER x) noexcept
+
620{
+
621 return (x >= telLOCAL_ERROR && x < temMALFORMED);
+
622}
-
618
-
619// TER allows all of the subsets.
- -
621
-
622//------------------------------------------------------------------------------
623
624inline bool
-
625isTelLocal(TER x) noexcept
+
626{
-
627 return (x >= telLOCAL_ERROR && x < temMALFORMED);
+
627 return (x >= temMALFORMED && x < tefFAILURE);
628}
629
630inline bool
- +
631isTefFailure(TER x) noexcept
632{
-
633 return (x >= temMALFORMED && x < tefFAILURE);
+
633 return (x >= tefFAILURE && x < terRETRY);
634}
635
636inline bool
-
637isTefFailure(TER x) noexcept
+
637isTerRetry(TER x) noexcept
638{
-
639 return (x >= tefFAILURE && x < terRETRY);
+
639 return (x >= terRETRY && x < tesSUCCESS);
640}
641
642inline bool
-
643isTerRetry(TER x) noexcept
+
643isTesSuccess(TER x) noexcept
644{
-
645 return (x >= terRETRY && x < tesSUCCESS);
-
646}
+
645 // Makes use of TERSubset::operator bool()
+
646 return !(x);
+
647}
-
647
-
648inline bool
-
-
649isTesSuccess(TER x) noexcept
-
650{
-
651 // Makes use of TERSubset::operator bool()
-
652 return !(x);
+
648
+
649inline bool
+
+
650isTecClaim(TER x) noexcept
+
651{
+
652 return ((x) >= tecCLAIM);
653}
654
-
655inline bool
-
-
656isTecClaim(TER x) noexcept
-
657{
-
658 return ((x) >= tecCLAIM);
-
659}
-
+ + +
657
+
658bool
+
659transResultInfo(TER code, std::string& token, std::string& text);
660
- - + +
662transToken(TER code);
663
-
664bool
-
665transResultInfo(TER code, std::string& token, std::string& text);
+ +
665transHuman(TER code);
666
- -
668transToken(TER code);
+ +
668transCode(std::string const& token);
669
- -
671transHuman(TER code);
-
672
- -
674transCode(std::string const& token);
-
675
-
676} // namespace xrpl
+
670} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
- - + +
constexpr auto operator=(T rhs) -> std::enable_if_t< Trait< T >::value, TERSubset & >
Definition TER.h:433
constexpr TERSubset & operator=(TERSubset const &rhs)=default
@@ -910,8 +904,8 @@ $(document).ready(function() { init_codefold(0); });
@ terPRE_TICKET
Definition TER.h:206
@ terRETRY
Definition TER.h:194
@ terQUEUED
Definition TER.h:205
-
bool isTerRetry(TER x) noexcept
Definition TER.h:643
-
bool operator>=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:601
+
bool isTerRetry(TER x) noexcept
Definition TER.h:637
+
bool operator>=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:603
TEFcodes
Definition TER.h:128
@ tefBAD_QUORUM
Definition TER.h:160
@ tefBAD_AUTH_MASTER
Definition TER.h:162
@@ -939,11 +933,11 @@ $(document).ready(function() { init_codefold(0); });
bool transResultInfo(TER code, std::string &token, std::string &text)
Definition TER.cpp:228
std::string transToken(TER code)
Definition TER.cpp:243
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition base_uint.h:552
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
-
bool isTefFailure(TER x) noexcept
Definition TER.h:637
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
+
bool isTefFailure(TER x) noexcept
Definition TER.h:631
bool operator!=(Buffer const &lhs, Buffer const &rhs) noexcept
Definition Buffer.h:209
-
bool operator<=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:595
-
bool isTelLocal(TER x) noexcept
Definition TER.h:625
+
bool operator<=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:597
+
bool isTelLocal(TER x) noexcept
Definition TER.h:619
std::unordered_map< TERUnderlyingType, std::pair< char const *const, char const *const > > const & transResults()
Definition TER.cpp:14
int TERUnderlyingType
Definition TER.h:18
constexpr TERUnderlyingType TERtoInt(TELcodes v)
Definition TER.h:355
@@ -998,7 +992,7 @@ $(document).ready(function() { init_codefold(0); });
@ temBAD_WEIGHT
Definition TER.h:97
@ temUNCERTAIN
Definition TER.h:103
@ temXCHAIN_BAD_PROOF
Definition TER.h:112
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
std::optional< TER > transCode(std::string const &token)
Definition TER.cpp:261
TECcodes
Definition TER.h:230
@ tecINVALID_UPDATE_TIME
Definition TER.h:335
@@ -1085,9 +1079,9 @@ $(document).ready(function() { init_codefold(0); });
@ tecNO_DST
Definition TER.h:271
@ tecUNFUNDED
Definition TER.h:276
@ tecINSUFFICIENT_PAYMENT
Definition TER.h:308
-
bool isTecClaim(TER x) noexcept
Definition TER.h:656
-
bool operator>(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:589
-
bool isTemMalformed(TER x) noexcept
Definition TER.h:631
+
bool isTecClaim(TER x) noexcept
Definition TER.h:650
+
bool operator>(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:591
+
bool isTemMalformed(TER x) noexcept
Definition TER.h:625
TEScodes
Definition TER.h:215
@ tesSUCCESS
Definition TER.h:225
diff --git a/TER__test_8cpp_source.html b/TER__test_8cpp_source.html index 65f368724a..55f29cf6da 100644 --- a/TER__test_8cpp_source.html +++ b/TER__test_8cpp_source.html @@ -355,24 +355,24 @@ $(document).ready(function() { init_codefold(0); });
@ telLOCAL_ERROR
Definition TER.h:32
bool operator<(Slice const &lhs, Slice const &rhs) noexcept
Definition Slice.h:198
@ terRETRY
Definition TER.h:194
-
bool isTerRetry(TER x) noexcept
Definition TER.h:643
-
bool operator>=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:601
+
bool isTerRetry(TER x) noexcept
Definition TER.h:637
+
bool operator>=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:603
@ tefFAILURE
Definition TER.h:146
std::string transHuman(TER code)
Definition TER.cpp:252
bool transResultInfo(TER code, std::string &token, std::string &text)
Definition TER.cpp:228
std::string transToken(TER code)
Definition TER.cpp:243
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition base_uint.h:552
-
bool isTefFailure(TER x) noexcept
Definition TER.h:637
+
bool isTefFailure(TER x) noexcept
Definition TER.h:631
bool operator!=(Buffer const &lhs, Buffer const &rhs) noexcept
Definition Buffer.h:209
-
bool operator<=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:595
-
bool isTelLocal(TER x) noexcept
Definition TER.h:625
+
bool operator<=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:597
+
bool isTelLocal(TER x) noexcept
Definition TER.h:619
constexpr TERUnderlyingType TERtoInt(TELcodes v)
Definition TER.h:355
@ temMALFORMED
Definition TER.h:67
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
std::optional< TER > transCode(std::string const &token)
Definition TER.cpp:261
@ tecCLAIM
Definition TER.h:262
-
bool isTecClaim(TER x) noexcept
Definition TER.h:656
-
bool isTemMalformed(TER x) noexcept
Definition TER.h:631
+
bool isTecClaim(TER x) noexcept
Definition TER.h:650
+
bool isTemMalformed(TER x) noexcept
Definition TER.h:625
@ tesSUCCESS
Definition TER.h:225
void testTransResultInfo()
Definition TER_test.cpp:12
diff --git a/TestHelpers_8cpp_source.html b/TestHelpers_8cpp_source.html index a790e110c6..4d124787a6 100644 --- a/TestHelpers_8cpp_source.html +++ b/TestHelpers_8cpp_source.html @@ -595,8 +595,8 @@ $(document).ready(function() { init_codefold(0); });
432} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
Value & append(Value const &value)
Append value to array at the end.
+
bool isArray() const
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
constexpr value_type const & value() const
Definition Asset.h:154
A currency issued by an account.
Definition Issue.h:13
diff --git a/TestHelpers_8h_source.html b/TestHelpers_8h_source.html index 4c98136dfd..e7ed4e4eac 100644 --- a/TestHelpers_8h_source.html +++ b/TestHelpers_8h_source.html @@ -909,8 +909,8 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
-
bool isNull() const
isNull() tests to see if this field is null.
+
Value & append(Value const &value)
Append value to array at the end.
+
bool isNull() const
isNull() tests to see if this field is null.
A currency issued by an account.
Definition Issue.h:13
std::uint32_t rep
Definition chrono.h:42
std::chrono::time_point< NetClock > time_point
Definition chrono.h:45
diff --git a/TheoreticalQuality__test_8cpp_source.html b/TheoreticalQuality__test_8cpp_source.html index 15deea330d..7e706a589e 100644 --- a/TheoreticalQuality__test_8cpp_source.html +++ b/TheoreticalQuality__test_8cpp_source.html @@ -612,7 +612,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
static Sink & getNullSink()
Returns a Sink which does nothing.
@@ -670,7 +670,7 @@ $(document).ready(function() { init_codefold(0); });
T is_same_v
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
FeatureBitset testable_amendments()
Definition Env.h:76
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
diff --git a/Ticket__test_8cpp_source.html b/Ticket__test_8cpp_source.html index 7cffb7b760..3346ab3f6a 100644 --- a/Ticket__test_8cpp_source.html +++ b/Ticket__test_8cpp_source.html @@ -926,9 +926,9 @@ $(document).ready(function() { init_codefold(0); });
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
+
bool isArray() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:516
diff --git a/TransactionEntry_8cpp_source.html b/TransactionEntry_8cpp_source.html index e28218b96b..6100425d1c 100644 --- a/TransactionEntry_8cpp_source.html +++ b/TransactionEntry_8cpp_source.html @@ -176,9 +176,9 @@ $(document).ready(function() { init_codefold(0); });
91} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isNull() const
isNull() tests to see if this field is null.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
bool isNull() const
isNull() tests to see if this field is null.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
uint256 getHashBySeq(std::uint32_t index)
Get a ledger's hash by sequence number using the cache.
std::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
bool isValidated(ReadView const &ledger)
diff --git a/TransactionEntry__test_8cpp_source.html b/TransactionEntry__test_8cpp_source.html index fbda2291ec..bd8b80f45e 100644 --- a/TransactionEntry__test_8cpp_source.html +++ b/TransactionEntry__test_8cpp_source.html @@ -438,8 +438,8 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
const_iterator begin() const
-
const_iterator end() const
+
const_iterator begin() const
+
const_iterator end() const
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
diff --git a/TransactionHistory__test_8cpp_source.html b/TransactionHistory__test_8cpp_source.html index 198f54e108..39f21a475f 100644 --- a/TransactionHistory__test_8cpp_source.html +++ b/TransactionHistory__test_8cpp_source.html @@ -242,7 +242,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
UInt size() const
Number of values in array or object.
-
Value removeMember(char const *key)
Remove and return the named member.
+
Value removeMember(char const *key)
Remove and return the named member.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
diff --git a/TransactionSign_8cpp_source.html b/TransactionSign_8cpp_source.html index 8446144ba6..aa778073f0 100644 --- a/TransactionSign_8cpp_source.html +++ b/TransactionSign_8cpp_source.html @@ -1465,12 +1465,12 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
Int asInt() const
-
bool isString() const
-
bool isObject() const
-
Value removeMember(char const *key)
Remove and return the named member.
+
bool isString() const
+
bool isObject() const
+
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
bool asBool() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
virtual bool checkSigs() const =0
@@ -1522,10 +1522,10 @@ $(document).ready(function() { init_codefold(0); });
static SField const & getField(int fieldCode)
Definition SField.cpp:94
Issue const & issue() const
Definition STAmount.h:454
-
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:554
+
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:556
bool native() const noexcept
Definition STAmount.h:416
- +
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:760
void setFieldArray(SField const &field, STArray const &v)
Definition STObject.cpp:802
@@ -1611,7 +1611,7 @@ $(document).ready(function() { init_codefold(0); });
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig) noexcept
Verify a signature on a message.
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
Definition TxQ.h:815
bool transResultInfo(TER code, std::string &token, std::string &text)
Definition TER.cpp:228
diff --git a/Transaction_8cpp_source.html b/Transaction_8cpp_source.html index 92d945512f..95e5270a4a 100644 --- a/Transaction_8cpp_source.html +++ b/Transaction_8cpp_source.html @@ -262,7 +262,7 @@ $(document).ready(function() { init_codefold(0); });
165} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isObject() const
+
bool isObject() const
std::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
virtual std::variant< AccountTx, TxSearched > getTransaction(uint256 const &id, std::optional< ClosedInterval< uint32_t > > const &range, error_code_i &ec)=0
getTransaction Returns the transaction with the given hash.
diff --git a/Transaction__ordering__test_8cpp_source.html b/Transaction__ordering__test_8cpp_source.html index 5cad048132..a5d62c5210 100644 --- a/Transaction__ordering__test_8cpp_source.html +++ b/Transaction__ordering__test_8cpp_source.html @@ -241,7 +241,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:792
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
T emplace_back(T... args)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/Transaction__test_8cpp_source.html b/Transaction__test_8cpp_source.html index 5e4bde331a..632b155656 100644 --- a/Transaction__test_8cpp_source.html +++ b/Transaction__test_8cpp_source.html @@ -918,9 +918,9 @@ $(document).ready(function() { init_codefold(0); });
T bind_front(T... args)
Represents a JSON value.
Definition json_value.h:130
-
const_iterator begin() const
-
const_iterator end() const
-
Value removeMember(char const *key)
Remove and return the named member.
+
const_iterator begin() const
+
const_iterator end() const
+
Value removeMember(char const *key)
Remove and return the named member.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
diff --git a/Transactor_8cpp_source.html b/Transactor_8cpp_source.html index 7ac86cc1c6..d03b9e7911 100644 --- a/Transactor_8cpp_source.html +++ b/Transactor_8cpp_source.html @@ -1396,7 +1396,7 @@ $(document).ready(function() { init_codefold(0); });
State information when applying a tx.
std::size_t size()
Get the number of unapplied changes.
STTx const & tx
-
void destroyXRP(XRPAmount const &fee)
+
void destroyXRP(XRPAmount const &fee)
ApplyFlags const & flags() const
void discard()
Discard changes and start fresh.
std::optional< TxMeta > apply(TER)
Apply the transaction result to the base.
@@ -1411,7 +1411,7 @@ $(document).ready(function() { init_codefold(0); });
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
uint32_t NETWORK_ID
Definition Config.h:138
RAII class to set and restore the current transaction rules.
Definition Rules.h:87
-
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:190
+
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:192
A public key.
Definition PublicKey.h:42
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
@@ -1571,7 +1571,7 @@ $(document).ready(function() { init_codefold(0); });
@ temBAD_SIGNATURE
Definition TER.h:85
@ temBAD_SIGNER
Definition TER.h:95
XRPAmount scaleFeeLoad(XRPAmount fee, LoadFeeTrack const &feeTrack, Fees const &fees, bool bUnlimited)
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
std::size_t constexpr unfundedOfferRemoveLimit
The maximum number of unfunded offers to delete at once.
Definition Protocol.h:28
NotTEC preflight0(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the txid.
@ tecINSUFF_FEE
Definition TER.h:283
@@ -1580,10 +1580,10 @@ $(document).ready(function() { init_codefold(0); });
@ tecEXPIRED
Definition TER.h:295
@ tecKILLED
Definition TER.h:297
@ tecOVERSIZE
Definition TER.h:292
-
bool isTecClaim(TER x) noexcept
Definition TER.h:656
+
bool isTecClaim(TER x) noexcept
Definition TER.h:650
@ lsfDisableMaster
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:213
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
Definition STTx.cpp:776
@ tesSUCCESS
Definition TER.h:225
std::string to_short_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:604
diff --git a/Transactor_8h_source.html b/Transactor_8h_source.html index 9ce1cc3f96..8948400577 100644 --- a/Transactor_8h_source.html +++ b/Transactor_8h_source.html @@ -571,12 +571,12 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
base_uint< 256 > uint256
Definition base_uint.h:526
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
ApplyFlags
Definition ApplyView.h:10
@ tapBATCH
Definition ApplyView.h:25
@ temDISABLED
Definition TER.h:94
NotTEC preflight0(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the txid.
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
diff --git a/TrustAndBalance__test_8cpp_source.html b/TrustAndBalance__test_8cpp_source.html index 17b57ebad1..2c62c6d513 100644 --- a/TrustAndBalance__test_8cpp_source.html +++ b/TrustAndBalance__test_8cpp_source.html @@ -540,7 +540,7 @@ $(document).ready(function() { init_codefold(0); });
435} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
diff --git a/TrustLine_8cpp_source.html b/TrustLine_8cpp_source.html index 68c1f552aa..bd2cf3781f 100644 --- a/TrustLine_8cpp_source.html +++ b/TrustLine_8cpp_source.html @@ -194,7 +194,7 @@ $(document).ready(function() { init_codefold(0); });
static std::vector< RPCTrustLine > getItems(AccountID const &accountID, ReadView const &view)
Definition TrustLine.cpp:81
static std::optional< RPCTrustLine > makeItem(AccountID const &accountID, std::shared_ptr< SLE const > const &sle)
Definition TrustLine.cpp:73
A view into a ledger.
Definition ReadView.h:31
-
void negate()
Definition STAmount.h:530
+
void negate()
Definition STAmount.h:532
AccountID const & getIssuer() const
Definition STAmount.h:466
Wraps a trust line SLE for convenience.
Definition TrustLine.h:34
diff --git a/TxHistory_8cpp_source.html b/TxHistory_8cpp_source.html index 3b17acd2ed..a4e782bcb5 100644 --- a/TxHistory_8cpp_source.html +++ b/TxHistory_8cpp_source.html @@ -135,9 +135,9 @@ $(document).ready(function() { init_codefold(0); });
50
51} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
UInt asUInt() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
bool useTxTables() const
Definition Config.h:318
virtual std::vector< std::shared_ptr< Transaction > > getTxHistory(LedgerIndex startIndex)=0
getTxHistory Returns the 20 most recent transactions starting from the given number.
diff --git a/TxMeta_8cpp_source.html b/TxMeta_8cpp_source.html index d0e9495aa6..2fb4890798 100644 --- a/TxMeta_8cpp_source.html +++ b/TxMeta_8cpp_source.html @@ -326,7 +326,7 @@ $(document).ready(function() { init_codefold(0); });
void setFieldU8(SField const &field, unsigned char)
Definition STObject.cpp:706
unsigned char getFieldU8(SField const &field) const
Definition STObject.cpp:564
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:576
-
std::size_t emplace_back(Args &&... args)
Definition STObject.h:975
+
std::size_t emplace_back(Args &&... args)
Definition STObject.h:981
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:718
STArray const & getFieldArray(SField const &field) const
Definition STObject.cpp:663
void setFieldAmount(SField const &field, STAmount const &)
Definition STObject.cpp:772
diff --git a/TxQ_8cpp_source.html b/TxQ_8cpp_source.html index 3fc8cfd99a..01bfe74409 100644 --- a/TxQ_8cpp_source.html +++ b/TxQ_8cpp_source.html @@ -1951,7 +1951,7 @@ $(document).ready(function() { init_codefold(0); });
Section & section(std::string const &name)
Returns the section with the given name.
bool standalone() const
Definition Config.h:312
-
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:190
+
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:192
std::shared_ptr< OpenView const > current() const
Returns a view to the current open ledger.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:45
std::size_t txCount() const
Return the number of tx inserted since creation.
Definition OpenView.cpp:95
@@ -2072,7 +2072,7 @@ $(document).ready(function() { init_codefold(0); });
std::string transToken(TER code)
Definition TER.cpp:243
@ current
This was a new validation and was added.
static FeeLevel64 getFeeLevelPaid(ReadView const &view, STTx const &tx)
Definition TxQ.cpp:20
-
bool isTefFailure(TER x) noexcept
Definition TER.h:637
+
bool isTefFailure(TER x) noexcept
Definition TER.h:631
auto constexpr muldiv_max
Definition mulDiv.h:8
FeeLevel64 toFeeLevel(XRPAmount const &drops, XRPAmount const &baseFee)
Definition TxQ.h:821
ApplyResult doApply(PreclaimResult const &preclaimResult, Application &app, OpenView &view)
Apply a prechecked transaction to an OpenView.
@@ -2081,11 +2081,11 @@ $(document).ready(function() { init_codefold(0); });
@ tapNONE
Definition ApplyView.h:11
@ tapFAIL_HARD
Definition ApplyView.h:15
@ transactionID
transaction plus signature to give transaction ID
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
PreclaimResult preclaim(PreflightResult const &preflightResult, Application &app, OpenView const &view)
Gate a transaction based on static ledger information.
void erase(STObject &st, TypedField< U > const &f)
Remove a field in an STObject.
Definition STExchange.h:148
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
-
bool isTemMalformed(TER x) noexcept
Definition TER.h:631
+
bool isTemMalformed(TER x) noexcept
Definition TER.h:625
@ tesSUCCESS
Definition TER.h:225
T next(T... args)
diff --git a/TxQ__test_8cpp_source.html b/TxQ__test_8cpp_source.html index 1a67be33b0..892050029b 100644 --- a/TxQ__test_8cpp_source.html +++ b/TxQ__test_8cpp_source.html @@ -4731,7 +4731,7 @@ $(document).ready(function() { init_codefold(0); });
auto const data
General field definitions, or fields used in multiple transaction namespaces.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
std::unique_ptr< Config > makeConfig(std::map< std::string, std::string > extraTxQ={}, std::map< std::string, std::string > extraVoting={})
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
void checkMetrics(Suite &test, jtx::Env &env, std::size_t expectedCount, std::optional< std::size_t > expectedMaxCount, std::size_t expectedInLedger, std::size_t expectedPerLedger, std::uint64_t expectedMinFeeLevel=baseFeeLevel.fee(), std::uint64_t expectedMedFeeLevel=minEscalationFeeLevel.fee(), std::source_location const location=std::source_location::current())
diff --git a/Tx_8cpp_source.html b/Tx_8cpp_source.html index d2d8c56d57..141541f8e9 100644 --- a/Tx_8cpp_source.html +++ b/Tx_8cpp_source.html @@ -401,7 +401,7 @@ $(document).ready(function() { init_codefold(0); });
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
bool asBool() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
uint32_t NETWORK_ID
Definition Config.h:138
bool useTxTables() const
Definition Config.h:318
diff --git a/UnlList_8cpp_source.html b/UnlList_8cpp_source.html index fb3e9fa11f..72a8654339 100644 --- a/UnlList_8cpp_source.html +++ b/UnlList_8cpp_source.html @@ -111,7 +111,7 @@ $(document).ready(function() { init_codefold(0); });
26
27} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A public key.
Definition PublicKey.h:42
virtual ValidatorList & validators()=0
void for_each_listed(std::function< void(PublicKey const &, bool)> func) const
Invokes the callback once for every listed validation public key.
diff --git a/Unsubscribe_8cpp_source.html b/Unsubscribe_8cpp_source.html index 0c3648a5e1..f79502fe49 100644 --- a/Unsubscribe_8cpp_source.html +++ b/Unsubscribe_8cpp_source.html @@ -321,10 +321,10 @@ $(document).ready(function() { init_codefold(0); });
236} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
bool isString() const
+
bool isArray() const
+
bool isString() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
Stream debug() const
Definition Journal.h:300
Stream info() const
Definition Journal.h:306
Specifies an order book.
Definition Book.h:16
diff --git a/ValidationCreate_8cpp_source.html b/ValidationCreate_8cpp_source.html index 3af6865129..19f9e5a7b5 100644 --- a/ValidationCreate_8cpp_source.html +++ b/ValidationCreate_8cpp_source.html @@ -133,7 +133,7 @@ $(document).ready(function() { init_codefold(0); });
46
47} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/ValidatorList_8cpp_source.html b/ValidatorList_8cpp_source.html index b856a85c51..fbe7824725 100644 --- a/ValidatorList_8cpp_source.html +++ b/ValidatorList_8cpp_source.html @@ -1772,388 +1772,389 @@ $(document).ready(function() { init_codefold(0); });
1596
1597void
-
1598ValidatorList::for_each_available(std::function<void(
-
1599 std::string const& manifest,
-
1600 std::uint32_t version,
- -
1602 PublicKey const& pubKey,
-
1603 std::size_t maxSequence,
-
1604 uint256 const& hash)> func) const
-
1605{
-
1606 std::shared_lock read_lock{mutex_};
-
1607
-
1608 for (auto const& [key, plCollection] : publisherLists_)
-
1609 {
-
1610 if (plCollection.status != PublisherStatus::available)
-
1611 continue;
-
1612 XRPL_ASSERT(plCollection.maxSequence != 0, "xrpl::ValidatorList::for_each_available : nonzero maxSequence");
-
1613 func(
-
1614 plCollection.rawManifest,
-
1615 plCollection.rawVersion,
-
1616 buildBlobInfos(plCollection),
-
1617 key,
-
1618 plCollection.maxSequence.value_or(0),
-
1619 plCollection.fullHash);
-
1620 }
-
1621}
+
1598ValidatorList::for_each_available(
+
1599 std::function<void(
+
1600 std::string const& manifest,
+
1601 std::uint32_t version,
+ +
1603 PublicKey const& pubKey,
+
1604 std::size_t maxSequence,
+
1605 uint256 const& hash)> func) const
+
1606{
+
1607 std::shared_lock read_lock{mutex_};
+
1608
+
1609 for (auto const& [key, plCollection] : publisherLists_)
+
1610 {
+
1611 if (plCollection.status != PublisherStatus::available)
+
1612 continue;
+
1613 XRPL_ASSERT(plCollection.maxSequence != 0, "xrpl::ValidatorList::for_each_available : nonzero maxSequence");
+
1614 func(
+
1615 plCollection.rawManifest,
+
1616 plCollection.rawVersion,
+
1617 buildBlobInfos(plCollection),
+
1618 key,
+
1619 plCollection.maxSequence.value_or(0),
+
1620 plCollection.fullHash);
+
1621 }
+
1622}
-
1622
- -
-
1624ValidatorList::getAvailable(std::string_view pubKey, std::optional<std::uint32_t> forceVersion /* = {} */)
-
1625{
-
1626 std::shared_lock read_lock{mutex_};
-
1627
-
1628 auto const keyBlob = strViewUnHex(pubKey);
-
1629
-
1630 if (!keyBlob || !publicKeyType(makeSlice(*keyBlob)))
-
1631 {
-
1632 JLOG(j_.warn()) << "Invalid requested validator list publisher key: " << pubKey;
-
1633 return {};
-
1634 }
-
1635
-
1636 auto id = PublicKey(makeSlice(*keyBlob));
-
1637
-
1638 auto const iter = publisherLists_.find(id);
-
1639
-
1640 if (iter == publisherLists_.end() || iter->second.status != PublisherStatus::available)
-
1641 return {};
-
1642
-
1643 Json::Value value = buildFileData(std::string{pubKey}, iter->second, forceVersion, j_);
-
1644
-
1645 return value;
-
1646}
+
1623
+ +
+
1625ValidatorList::getAvailable(std::string_view pubKey, std::optional<std::uint32_t> forceVersion /* = {} */)
+
1626{
+
1627 std::shared_lock read_lock{mutex_};
+
1628
+
1629 auto const keyBlob = strViewUnHex(pubKey);
+
1630
+
1631 if (!keyBlob || !publicKeyType(makeSlice(*keyBlob)))
+
1632 {
+
1633 JLOG(j_.warn()) << "Invalid requested validator list publisher key: " << pubKey;
+
1634 return {};
+
1635 }
+
1636
+
1637 auto id = PublicKey(makeSlice(*keyBlob));
+
1638
+
1639 auto const iter = publisherLists_.find(id);
+
1640
+
1641 if (iter == publisherLists_.end() || iter->second.status != PublisherStatus::available)
+
1642 return {};
+
1643
+
1644 Json::Value value = buildFileData(std::string{pubKey}, iter->second, forceVersion, j_);
+
1645
+
1646 return value;
+
1647}
-
1647
- -
-
1649ValidatorList::calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize, std::size_t seenSize)
-
1650{
-
1651 // Use quorum if specified via command line.
-
1652 if (minimumQuorum_ > 0)
-
1653 {
-
1654 JLOG(j_.warn()) << "Using potentially unsafe quorum of " << *minimumQuorum_
-
1655 << " as specified on the command line";
-
1656 return *minimumQuorum_;
-
1657 }
-
1658
-
1659 if (!publisherLists_.empty())
-
1660 {
-
1661 // Do not use achievable quorum until lists from a sufficient number of
-
1662 // configured publishers are available
- -
1664 for (auto const& list : publisherLists_)
-
1665 {
-
1666 if (list.second.status != PublisherStatus::available)
-
1667 unavailable += 1;
-
1668 }
-
1669 // There are two, subtly different, sides to list threshold:
-
1670 //
-
1671 // 1. The minimum required intersection between lists listThreshold_
-
1672 // for a validator to be included in trustedMasterKeys_.
-
1673 // If this many (or more) publishers are unavailable, we are likely
-
1674 // to NOT include a validator which otherwise would have been used.
-
1675 // We disable quorum if this happens.
-
1676 // 2. The minimum number of publishers which, when unavailable, will
-
1677 // prevent us from hitting the above threshold on ANY validator.
-
1678 // This is calculated as:
-
1679 // N - M + 1
-
1680 // where
-
1681 // N: number of publishers i.e. publisherLists_.size()
-
1682 // M: minimum required intersection i.e. listThreshold_
-
1683 // If this happens, we still have this local validator and we do not
-
1684 // want it to form a quorum of 1, so we disable quorum as well.
-
1685 //
-
1686 // We disable quorum if the number of unavailable publishers exceeds
-
1687 // either of the above thresholds
-
1688 auto const errorThreshold = std::min(
-
1689 listThreshold_, //
-
1690 publisherLists_.size() - listThreshold_ + 1);
-
1691 XRPL_ASSERT(errorThreshold > 0, "xrpl::ValidatorList::calculateQuorum : nonzero error threshold");
-
1692 if (unavailable >= errorThreshold)
- -
1694 }
-
1695
-
1696 // Use an 80% quorum to balance fork safety, liveness, and required UNL
-
1697 // overlap.
-
1698 //
-
1699 // Theorem 8 of the Analysis of the XRP Ledger Consensus Protocol
-
1700 // (https://arxiv.org/abs/1802.07242) says:
-
1701 // XRP LCP guarantees fork safety if Oi,j > nj/2 + ni − qi + ti,j
-
1702 // for every pair of nodes Pi, Pj.
-
1703 //
-
1704 // ni: size of Pi's UNL
-
1705 // nj: size of Pj's UNL
-
1706 // Oi,j: number of validators in both UNLs
-
1707 // qi: validation quorum for Pi's UNL
-
1708 // ti, tj: maximum number of allowed Byzantine faults in Pi and Pj's
-
1709 // UNLs ti,j: min{ti, tj, Oi,j}
-
1710 //
-
1711 // Assume ni < nj, meaning and ti,j = ti
-
1712 //
-
1713 // For qi = .8*ni, we make ti <= .2*ni
-
1714 // (We could make ti lower and tolerate less UNL overlap. However in
-
1715 // order to prioritize safety over liveness, we need ti >= ni - qi)
-
1716 //
-
1717 // An 80% quorum allows two UNLs to safely have < .2*ni unique
-
1718 // validators between them:
-
1719 //
-
1720 // pi = ni - Oi,j
-
1721 // pj = nj - Oi,j
-
1722 //
-
1723 // Oi,j > nj/2 + ni − qi + ti,j
-
1724 // ni - pi > (ni - pi + pj)/2 + ni − .8*ni + .2*ni
-
1725 // pi + pj < .2*ni
-
1726 //
-
1727 // Note that the negative UNL protocol introduced the
-
1728 // AbsoluteMinimumQuorum which is 60% of the original UNL size. The
-
1729 // effective quorum should not be lower than it.
-
1730 return static_cast<std::size_t>(std::max(std::ceil(effectiveUnlSize * 0.8f), std::ceil(unlSize * 0.6f)));
-
1731}
+
1648
+ +
+
1650ValidatorList::calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize, std::size_t seenSize)
+
1651{
+
1652 // Use quorum if specified via command line.
+
1653 if (minimumQuorum_ > 0)
+
1654 {
+
1655 JLOG(j_.warn()) << "Using potentially unsafe quorum of " << *minimumQuorum_
+
1656 << " as specified on the command line";
+
1657 return *minimumQuorum_;
+
1658 }
+
1659
+
1660 if (!publisherLists_.empty())
+
1661 {
+
1662 // Do not use achievable quorum until lists from a sufficient number of
+
1663 // configured publishers are available
+ +
1665 for (auto const& list : publisherLists_)
+
1666 {
+
1667 if (list.second.status != PublisherStatus::available)
+
1668 unavailable += 1;
+
1669 }
+
1670 // There are two, subtly different, sides to list threshold:
+
1671 //
+
1672 // 1. The minimum required intersection between lists listThreshold_
+
1673 // for a validator to be included in trustedMasterKeys_.
+
1674 // If this many (or more) publishers are unavailable, we are likely
+
1675 // to NOT include a validator which otherwise would have been used.
+
1676 // We disable quorum if this happens.
+
1677 // 2. The minimum number of publishers which, when unavailable, will
+
1678 // prevent us from hitting the above threshold on ANY validator.
+
1679 // This is calculated as:
+
1680 // N - M + 1
+
1681 // where
+
1682 // N: number of publishers i.e. publisherLists_.size()
+
1683 // M: minimum required intersection i.e. listThreshold_
+
1684 // If this happens, we still have this local validator and we do not
+
1685 // want it to form a quorum of 1, so we disable quorum as well.
+
1686 //
+
1687 // We disable quorum if the number of unavailable publishers exceeds
+
1688 // either of the above thresholds
+
1689 auto const errorThreshold = std::min(
+
1690 listThreshold_, //
+
1691 publisherLists_.size() - listThreshold_ + 1);
+
1692 XRPL_ASSERT(errorThreshold > 0, "xrpl::ValidatorList::calculateQuorum : nonzero error threshold");
+
1693 if (unavailable >= errorThreshold)
+ +
1695 }
+
1696
+
1697 // Use an 80% quorum to balance fork safety, liveness, and required UNL
+
1698 // overlap.
+
1699 //
+
1700 // Theorem 8 of the Analysis of the XRP Ledger Consensus Protocol
+
1701 // (https://arxiv.org/abs/1802.07242) says:
+
1702 // XRP LCP guarantees fork safety if Oi,j > nj/2 + ni − qi + ti,j
+
1703 // for every pair of nodes Pi, Pj.
+
1704 //
+
1705 // ni: size of Pi's UNL
+
1706 // nj: size of Pj's UNL
+
1707 // Oi,j: number of validators in both UNLs
+
1708 // qi: validation quorum for Pi's UNL
+
1709 // ti, tj: maximum number of allowed Byzantine faults in Pi and Pj's
+
1710 // UNLs ti,j: min{ti, tj, Oi,j}
+
1711 //
+
1712 // Assume ni < nj, meaning and ti,j = ti
+
1713 //
+
1714 // For qi = .8*ni, we make ti <= .2*ni
+
1715 // (We could make ti lower and tolerate less UNL overlap. However in
+
1716 // order to prioritize safety over liveness, we need ti >= ni - qi)
+
1717 //
+
1718 // An 80% quorum allows two UNLs to safely have < .2*ni unique
+
1719 // validators between them:
+
1720 //
+
1721 // pi = ni - Oi,j
+
1722 // pj = nj - Oi,j
+
1723 //
+
1724 // Oi,j > nj/2 + ni − qi + ti,j
+
1725 // ni - pi > (ni - pi + pj)/2 + ni − .8*ni + .2*ni
+
1726 // pi + pj < .2*ni
+
1727 //
+
1728 // Note that the negative UNL protocol introduced the
+
1729 // AbsoluteMinimumQuorum which is 60% of the original UNL size. The
+
1730 // effective quorum should not be lower than it.
+
1731 return static_cast<std::size_t>(std::max(std::ceil(effectiveUnlSize * 0.8f), std::ceil(unlSize * 0.6f)));
+
1732}
-
1732
- -
-
1734ValidatorList::updateTrusted(
-
1735 hash_set<NodeID> const& seenValidators,
-
1736 NetClock::time_point closeTime,
-
1737 NetworkOPs& ops,
-
1738 Overlay& overlay,
-
1739 HashRouter& hashRouter)
-
1740{
-
1741 using namespace std::chrono_literals;
-
1742 if (timeKeeper_.now() > closeTime + 30s)
-
1743 closeTime = timeKeeper_.now();
-
1744
-
1745 std::lock_guard lock{mutex_};
-
1746
-
1747 // Rotate pending and remove expired published lists
-
1748 bool good = true;
-
1749 // localPublisherList is not processed here. This is because the
-
1750 // Validators specified in the local config file do not expire nor do
-
1751 // they have a "remaining" section of PublisherList.
-
1752 for (auto& [pubKey, collection] : publisherLists_)
-
1753 {
-
1754 {
-
1755 auto& remaining = collection.remaining;
-
1756 auto const firstIter = remaining.begin();
-
1757 auto iter = firstIter;
-
1758 if (iter != remaining.end() && iter->second.validFrom <= closeTime)
-
1759 {
-
1760 // Find the LAST candidate that is ready to go live.
-
1761 for (auto next = std::next(iter); next != remaining.end() && next->second.validFrom <= closeTime;
-
1762 ++iter, ++next)
-
1763 {
-
1764 XRPL_ASSERT(
-
1765 std::next(iter) == next,
-
1766 "xrpl::ValidatorList::updateTrusted : sequential "
-
1767 "remaining");
-
1768 }
-
1769 XRPL_ASSERT(
-
1770 iter != remaining.end(),
-
1771 "xrpl::ValidatorList::updateTrusted : non-end of "
-
1772 "remaining");
-
1773
-
1774 // Rotate the pending list in to current
-
1775 auto sequence = iter->first;
-
1776 auto& candidate = iter->second;
-
1777 auto& current = collection.current;
-
1778 XRPL_ASSERT(candidate.validFrom <= closeTime, "xrpl::ValidatorList::updateTrusted : maximum time");
-
1779
-
1780 auto const oldList = current.list;
-
1781 current = std::move(candidate);
-
1782 if (collection.status != PublisherStatus::available)
-
1783 collection.status = PublisherStatus::available;
-
1784 XRPL_ASSERT(current.sequence == sequence, "xrpl::ValidatorList::updateTrusted : sequence match");
-
1785 // If the list is expired, remove the validators so they don't
-
1786 // get processed in. The expiration check below will do the rest
-
1787 // of the work
-
1788 if (current.validUntil <= closeTime)
-
1789 current.list.clear();
-
1790
-
1791 updatePublisherList(pubKey, current, oldList, lock);
-
1792
-
1793 // Only broadcast the current, which will consequently only
-
1794 // send to peers that don't understand v2, or which are
-
1795 // unknown (unlikely). Those that do understand v2 should
-
1796 // already have this list and are in the process of
-
1797 // switching themselves.
-
1798 broadcastBlobs(pubKey, collection, sequence, current.hash, overlay, hashRouter, j_);
-
1799
-
1800 // Erase any candidates that we skipped over, plus this one
-
1801 remaining.erase(firstIter, std::next(iter));
-
1802 }
-
1803 }
-
1804 // Remove if expired
-
1805 // ValidatorLists specified in the local config file never expire.
-
1806 // Hence, the below steps are not relevant for localPublisherList
-
1807 if (collection.status == PublisherStatus::available && collection.current.validUntil <= closeTime)
-
1808 {
-
1809 removePublisherList(lock, pubKey, PublisherStatus::expired);
-
1810 ops.setUNLBlocked();
-
1811 }
-
1812 if (collection.status != PublisherStatus::available)
-
1813 good = false;
-
1814 }
-
1815 if (good)
-
1816 ops.clearUNLBlocked();
-
1817
-
1818 TrustChanges trustChanges;
-
1819
-
1820 auto it = trustedMasterKeys_.cbegin();
-
1821 while (it != trustedMasterKeys_.cend())
-
1822 {
-
1823 auto const kit = keyListings_.find(*it);
-
1824 if (kit == keyListings_.end() || //
-
1825 kit->second < listThreshold_ || //
-
1826 validatorManifests_.revoked(*it))
-
1827 {
-
1828 trustChanges.removed.insert(calcNodeID(*it));
-
1829 it = trustedMasterKeys_.erase(it);
-
1830 }
-
1831 else
-
1832 {
-
1833 XRPL_ASSERT(kit->second >= listThreshold_, "xrpl::ValidatorList::updateTrusted : count meets threshold");
-
1834 ++it;
-
1835 }
-
1836 }
-
1837
-
1838 for (auto const& val : keyListings_)
-
1839 {
-
1840 if (val.second >= listThreshold_ && !validatorManifests_.revoked(val.first) &&
-
1841 trustedMasterKeys_.emplace(val.first).second)
-
1842 trustChanges.added.insert(calcNodeID(val.first));
-
1843 }
-
1844
-
1845 // If there were any changes, we need to update the ephemeral signing
-
1846 // keys:
-
1847 if (!trustChanges.added.empty() || !trustChanges.removed.empty())
-
1848 {
-
1849 trustedSigningKeys_.clear();
-
1850
-
1851 // trustedMasterKeys_ contain non-revoked manifests only. Hence the
-
1852 // manifests must contain a valid signingKey
-
1853 for (auto const& k : trustedMasterKeys_)
-
1854 {
-
1855 std::optional<PublicKey> const signingKey = validatorManifests_.getSigningKey(k);
-
1856 XRPL_ASSERT(signingKey, "xrpl::ValidatorList::updateTrusted : found signing key");
-
1857 trustedSigningKeys_.insert(*signingKey);
-
1858 }
-
1859 }
-
1860
-
1861 JLOG(j_.debug()) << trustedMasterKeys_.size() << " of " << keyListings_.size()
-
1862 << " listed validators eligible for inclusion in the trusted set";
-
1863
-
1864 auto const unlSize = trustedMasterKeys_.size();
-
1865 auto effectiveUnlSize = unlSize;
-
1866 auto seenSize = seenValidators.size();
-
1867 if (!negativeUNL_.empty())
-
1868 {
-
1869 for (auto const& k : trustedMasterKeys_)
-
1870 {
-
1871 if (negativeUNL_.count(k))
-
1872 --effectiveUnlSize;
-
1873 }
-
1874 hash_set<NodeID> negUnlNodeIDs;
-
1875 for (auto const& k : negativeUNL_)
-
1876 {
-
1877 negUnlNodeIDs.emplace(calcNodeID(k));
-
1878 }
-
1879 for (auto const& nid : seenValidators)
-
1880 {
-
1881 if (negUnlNodeIDs.count(nid))
-
1882 --seenSize;
-
1883 }
-
1884 }
-
1885 quorum_ = calculateQuorum(unlSize, effectiveUnlSize, seenSize);
-
1886
-
1887 JLOG(j_.debug()) << "Using quorum of " << quorum_ << " for new set of " << unlSize << " trusted validators ("
-
1888 << trustChanges.added.size() << " added, " << trustChanges.removed.size() << " removed)";
-
1889
-
1890 if (unlSize < quorum_)
-
1891 {
-
1892 JLOG(j_.warn()) << "New quorum of " << quorum_ << " exceeds the number of trusted validators (" << unlSize
-
1893 << ")";
-
1894 }
-
1895
-
1896 if ((publisherLists_.size() || localPublisherList.list.size()) && unlSize == 0)
-
1897 {
-
1898 // No validators. Lock down.
-
1899 ops.setUNLBlocked();
-
1900 }
-
1901
-
1902 return trustChanges;
-
1903}
+
1733
+ +
+
1735ValidatorList::updateTrusted(
+
1736 hash_set<NodeID> const& seenValidators,
+
1737 NetClock::time_point closeTime,
+
1738 NetworkOPs& ops,
+
1739 Overlay& overlay,
+
1740 HashRouter& hashRouter)
+
1741{
+
1742 using namespace std::chrono_literals;
+
1743 if (timeKeeper_.now() > closeTime + 30s)
+
1744 closeTime = timeKeeper_.now();
+
1745
+
1746 std::lock_guard lock{mutex_};
+
1747
+
1748 // Rotate pending and remove expired published lists
+
1749 bool good = true;
+
1750 // localPublisherList is not processed here. This is because the
+
1751 // Validators specified in the local config file do not expire nor do
+
1752 // they have a "remaining" section of PublisherList.
+
1753 for (auto& [pubKey, collection] : publisherLists_)
+
1754 {
+
1755 {
+
1756 auto& remaining = collection.remaining;
+
1757 auto const firstIter = remaining.begin();
+
1758 auto iter = firstIter;
+
1759 if (iter != remaining.end() && iter->second.validFrom <= closeTime)
+
1760 {
+
1761 // Find the LAST candidate that is ready to go live.
+
1762 for (auto next = std::next(iter); next != remaining.end() && next->second.validFrom <= closeTime;
+
1763 ++iter, ++next)
+
1764 {
+
1765 XRPL_ASSERT(
+
1766 std::next(iter) == next,
+
1767 "xrpl::ValidatorList::updateTrusted : sequential "
+
1768 "remaining");
+
1769 }
+
1770 XRPL_ASSERT(
+
1771 iter != remaining.end(),
+
1772 "xrpl::ValidatorList::updateTrusted : non-end of "
+
1773 "remaining");
+
1774
+
1775 // Rotate the pending list in to current
+
1776 auto sequence = iter->first;
+
1777 auto& candidate = iter->second;
+
1778 auto& current = collection.current;
+
1779 XRPL_ASSERT(candidate.validFrom <= closeTime, "xrpl::ValidatorList::updateTrusted : maximum time");
+
1780
+
1781 auto const oldList = current.list;
+
1782 current = std::move(candidate);
+
1783 if (collection.status != PublisherStatus::available)
+
1784 collection.status = PublisherStatus::available;
+
1785 XRPL_ASSERT(current.sequence == sequence, "xrpl::ValidatorList::updateTrusted : sequence match");
+
1786 // If the list is expired, remove the validators so they don't
+
1787 // get processed in. The expiration check below will do the rest
+
1788 // of the work
+
1789 if (current.validUntil <= closeTime)
+
1790 current.list.clear();
+
1791
+
1792 updatePublisherList(pubKey, current, oldList, lock);
+
1793
+
1794 // Only broadcast the current, which will consequently only
+
1795 // send to peers that don't understand v2, or which are
+
1796 // unknown (unlikely). Those that do understand v2 should
+
1797 // already have this list and are in the process of
+
1798 // switching themselves.
+
1799 broadcastBlobs(pubKey, collection, sequence, current.hash, overlay, hashRouter, j_);
+
1800
+
1801 // Erase any candidates that we skipped over, plus this one
+
1802 remaining.erase(firstIter, std::next(iter));
+
1803 }
+
1804 }
+
1805 // Remove if expired
+
1806 // ValidatorLists specified in the local config file never expire.
+
1807 // Hence, the below steps are not relevant for localPublisherList
+
1808 if (collection.status == PublisherStatus::available && collection.current.validUntil <= closeTime)
+
1809 {
+
1810 removePublisherList(lock, pubKey, PublisherStatus::expired);
+
1811 ops.setUNLBlocked();
+
1812 }
+
1813 if (collection.status != PublisherStatus::available)
+
1814 good = false;
+
1815 }
+
1816 if (good)
+
1817 ops.clearUNLBlocked();
+
1818
+
1819 TrustChanges trustChanges;
+
1820
+
1821 auto it = trustedMasterKeys_.cbegin();
+
1822 while (it != trustedMasterKeys_.cend())
+
1823 {
+
1824 auto const kit = keyListings_.find(*it);
+
1825 if (kit == keyListings_.end() || //
+
1826 kit->second < listThreshold_ || //
+
1827 validatorManifests_.revoked(*it))
+
1828 {
+
1829 trustChanges.removed.insert(calcNodeID(*it));
+
1830 it = trustedMasterKeys_.erase(it);
+
1831 }
+
1832 else
+
1833 {
+
1834 XRPL_ASSERT(kit->second >= listThreshold_, "xrpl::ValidatorList::updateTrusted : count meets threshold");
+
1835 ++it;
+
1836 }
+
1837 }
+
1838
+
1839 for (auto const& val : keyListings_)
+
1840 {
+
1841 if (val.second >= listThreshold_ && !validatorManifests_.revoked(val.first) &&
+
1842 trustedMasterKeys_.emplace(val.first).second)
+
1843 trustChanges.added.insert(calcNodeID(val.first));
+
1844 }
+
1845
+
1846 // If there were any changes, we need to update the ephemeral signing
+
1847 // keys:
+
1848 if (!trustChanges.added.empty() || !trustChanges.removed.empty())
+
1849 {
+
1850 trustedSigningKeys_.clear();
+
1851
+
1852 // trustedMasterKeys_ contain non-revoked manifests only. Hence the
+
1853 // manifests must contain a valid signingKey
+
1854 for (auto const& k : trustedMasterKeys_)
+
1855 {
+
1856 std::optional<PublicKey> const signingKey = validatorManifests_.getSigningKey(k);
+
1857 XRPL_ASSERT(signingKey, "xrpl::ValidatorList::updateTrusted : found signing key");
+
1858 trustedSigningKeys_.insert(*signingKey);
+
1859 }
+
1860 }
+
1861
+
1862 JLOG(j_.debug()) << trustedMasterKeys_.size() << " of " << keyListings_.size()
+
1863 << " listed validators eligible for inclusion in the trusted set";
+
1864
+
1865 auto const unlSize = trustedMasterKeys_.size();
+
1866 auto effectiveUnlSize = unlSize;
+
1867 auto seenSize = seenValidators.size();
+
1868 if (!negativeUNL_.empty())
+
1869 {
+
1870 for (auto const& k : trustedMasterKeys_)
+
1871 {
+
1872 if (negativeUNL_.count(k))
+
1873 --effectiveUnlSize;
+
1874 }
+
1875 hash_set<NodeID> negUnlNodeIDs;
+
1876 for (auto const& k : negativeUNL_)
+
1877 {
+
1878 negUnlNodeIDs.emplace(calcNodeID(k));
+
1879 }
+
1880 for (auto const& nid : seenValidators)
+
1881 {
+
1882 if (negUnlNodeIDs.count(nid))
+
1883 --seenSize;
+
1884 }
+
1885 }
+
1886 quorum_ = calculateQuorum(unlSize, effectiveUnlSize, seenSize);
+
1887
+
1888 JLOG(j_.debug()) << "Using quorum of " << quorum_ << " for new set of " << unlSize << " trusted validators ("
+
1889 << trustChanges.added.size() << " added, " << trustChanges.removed.size() << " removed)";
+
1890
+
1891 if (unlSize < quorum_)
+
1892 {
+
1893 JLOG(j_.warn()) << "New quorum of " << quorum_ << " exceeds the number of trusted validators (" << unlSize
+
1894 << ")";
+
1895 }
+
1896
+
1897 if ((publisherLists_.size() || localPublisherList.list.size()) && unlSize == 0)
+
1898 {
+
1899 // No validators. Lock down.
+
1900 ops.setUNLBlocked();
+
1901 }
+
1902
+
1903 return trustChanges;
+
1904}
-
1904
- -
-
1906ValidatorList::getTrustedMasterKeys() const
-
1907{
-
1908 std::shared_lock read_lock{mutex_};
-
1909 return trustedMasterKeys_;
-
1910}
+
1905
+ +
+
1907ValidatorList::getTrustedMasterKeys() const
+
1908{
+
1909 std::shared_lock read_lock{mutex_};
+
1910 return trustedMasterKeys_;
+
1911}
-
1911
- -
-
1913ValidatorList::getListThreshold() const
-
1914{
-
1915 std::shared_lock read_lock{mutex_};
-
1916 return listThreshold_;
-
1917}
+
1912
+ +
+
1914ValidatorList::getListThreshold() const
+
1915{
+
1916 std::shared_lock read_lock{mutex_};
+
1917 return listThreshold_;
+
1918}
-
1918
- -
-
1920ValidatorList::getNegativeUNL() const
-
1921{
-
1922 std::shared_lock read_lock{mutex_};
-
1923 return negativeUNL_;
-
1924}
+
1919
+ +
+
1921ValidatorList::getNegativeUNL() const
+
1922{
+
1923 std::shared_lock read_lock{mutex_};
+
1924 return negativeUNL_;
+
1925}
-
1925
-
1926void
-
-
1927ValidatorList::setNegativeUNL(hash_set<PublicKey> const& negUnl)
-
1928{
-
1929 std::lock_guard lock{mutex_};
-
1930 negativeUNL_ = negUnl;
-
1931}
+
1926
+
1927void
+
+
1928ValidatorList::setNegativeUNL(hash_set<PublicKey> const& negUnl)
+
1929{
+
1930 std::lock_guard lock{mutex_};
+
1931 negativeUNL_ = negUnl;
+
1932}
-
1932
- -
-
1934ValidatorList::negativeUNLFilter(std::vector<std::shared_ptr<STValidation>>&& validations) const
-
1935{
-
1936 // Remove validations that are from validators on the negative UNL.
-
1937 auto ret = std::move(validations);
-
1938
-
1939 std::shared_lock read_lock{mutex_};
-
1940 if (!negativeUNL_.empty())
-
1941 {
-
1942 ret.erase(
- -
1944 ret.begin(),
-
1945 ret.end(),
-
1946 [&](auto const& v) -> bool {
-
1947 if (auto const masterKey = getTrustedKey(read_lock, v->getSignerPublic()); masterKey)
-
1948 {
-
1949 return negativeUNL_.count(*masterKey);
-
1950 }
-
1951 else
-
1952 {
-
1953 return false;
-
1954 }
-
1955 }),
-
1956 ret.end());
-
1957 }
-
1958
-
1959 return ret;
-
1960}
+
1933
+ +
+
1935ValidatorList::negativeUNLFilter(std::vector<std::shared_ptr<STValidation>>&& validations) const
+
1936{
+
1937 // Remove validations that are from validators on the negative UNL.
+
1938 auto ret = std::move(validations);
+
1939
+
1940 std::shared_lock read_lock{mutex_};
+
1941 if (!negativeUNL_.empty())
+
1942 {
+
1943 ret.erase(
+ +
1945 ret.begin(),
+
1946 ret.end(),
+
1947 [&](auto const& v) -> bool {
+
1948 if (auto const masterKey = getTrustedKey(read_lock, v->getSignerPublic()); masterKey)
+
1949 {
+
1950 return negativeUNL_.count(*masterKey);
+
1951 }
+
1952 else
+
1953 {
+
1954 return false;
+
1955 }
+
1956 }),
+
1957 ret.end());
+
1958 }
+
1959
+
1960 return ret;
+
1961}
-
1961
-
1962} // namespace xrpl
+
1962
+
1963} // namespace xrpl
T accumulate(T... args)
T at(T... args)
T back(T... args)
@@ -2164,15 +2165,15 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
Value & append(Value const &value)
Append value to array at the end.
+
bool isArray() const
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
std::string toStyledString() const
-
bool isString() const
+
std::string toStyledString() const
+
bool isString() const
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
bool isInt() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isInt() const
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:318
Stream debug() const
Definition Journal.h:300
diff --git a/ValidatorList_8h_source.html b/ValidatorList_8h_source.html index 4c1eb70512..4433cb9c89 100644 --- a/ValidatorList_8h_source.html +++ b/ValidatorList_8h_source.html @@ -446,218 +446,219 @@ $(document).ready(function() { init_codefold(0); });
569 for_each_listed(std::function<void(PublicKey const&, bool)> func) const;
570
598 void
- -
600 std::string const& manifest,
-
601 std::uint32_t version,
- -
603 PublicKey const& pubKey,
-
604 std::size_t maxSequence,
-
605 uint256 const& hash)> func) const;
-
606
- - -
612
- -
615 count() const;
-
616
- -
627 expires() const;
-
628
- -
635 getJson() const;
-
636
- - -
- -
644 {
-
645 shared_lock read_lock{mutex_};
- -
647 }
+ +
600 std::function<void(
+
601 std::string const& manifest,
+
602 std::uint32_t version,
+ +
604 PublicKey const& pubKey,
+
605 std::size_t maxSequence,
+
606 uint256 const& hash)> func) const;
+
607
+ + +
613
+ +
616 count() const;
+
617
+ +
628 expires() const;
+
629
+ +
636 getJson() const;
+
637
+ + +
+ +
645 {
+
646 shared_lock read_lock{mutex_};
+ +
648 }
-
648
- -
654 getTrustedMasterKeys() const;
-
655
- -
661 getListThreshold() const;
-
662
- -
668 getNegativeUNL() const;
-
669
-
674 void
-
675 setNegativeUNL(hash_set<PublicKey> const& negUnl);
-
676
- - -
685
-
686private:
- -
689 count(shared_lock const&) const;
-
690
-
699 bool
-
700 trusted(shared_lock const&, PublicKey const& identity) const;
-
701
- -
713 getTrustedKey(shared_lock const&, PublicKey const& identity) const;
-
714
- -
725 expires(shared_lock const&) const;
-
726
-
749 PublisherListStats
-
750 applyList(
-
751 std::string const& globalManifest,
-
752 std::optional<std::string> const& localManifest,
-
753 std::string const& blob,
-
754 std::string const& signature,
-
755 std::uint32_t version,
-
756 std::string siteUri,
-
757 std::optional<uint256> const& hash,
-
758 lock_guard const&);
-
759
-
760 // This function updates the keyListings_ counts for all the trusted
-
761 // master keys
-
762 void
- -
764 PublicKey const& pubKey,
-
765 PublisherList const& current,
-
766 std::vector<PublicKey> const& oldList,
-
767 lock_guard const&);
-
768
-
769 static void
-
770 buildBlobInfos(std::map<std::size_t, ValidatorBlobInfo>& blobInfos, PublisherListCollection const& lists);
-
771
- -
773 buildBlobInfos(PublisherListCollection const& lists);
-
774
-
775 static void
- -
777 PublicKey const& publisherKey,
-
778 PublisherListCollection const& lists,
-
779 std::size_t maxSequence,
-
780 uint256 const& hash,
-
781 Overlay& overlay,
-
782 HashRouter& hashRouter,
- -
784
-
785 static void
- -
787 Peer& peer,
-
788 std::uint64_t peerSequence,
-
789 PublicKey const& publisherKey,
-
790 std::size_t maxSequence,
-
791 std::uint32_t rawVersion,
-
792 std::string const& rawManifest,
- - -
795 HashRouter& hashRouter,
- -
797
-
800 boost::filesystem::path
-
801 getCacheFileName(lock_guard const&, PublicKey const& pubKey) const;
-
802
-
806 static Json::Value
-
807 buildFileData(std::string const& pubKey, PublisherListCollection const& pubCollection, beast::Journal j);
-
808
-
812 static Json::Value
- -
814 std::string const& pubKey,
-
815 PublisherListCollection const& pubCollection,
-
816 std::optional<std::uint32_t> forceVersion,
- -
818
-
819 template <class Hasher>
-
820 friend void
-
- -
822 {
-
823 using beast::hash_append;
- -
825 }
+
649
+ +
655 getTrustedMasterKeys() const;
+
656
+ +
662 getListThreshold() const;
+
663
+ +
669 getNegativeUNL() const;
+
670
+
675 void
+
676 setNegativeUNL(hash_set<PublicKey> const& negUnl);
+
677
+ + +
686
+
687private:
+ +
690 count(shared_lock const&) const;
+
691
+
700 bool
+
701 trusted(shared_lock const&, PublicKey const& identity) const;
+
702
+ +
714 getTrustedKey(shared_lock const&, PublicKey const& identity) const;
+
715
+ +
726 expires(shared_lock const&) const;
+
727
+
750 PublisherListStats
+
751 applyList(
+
752 std::string const& globalManifest,
+
753 std::optional<std::string> const& localManifest,
+
754 std::string const& blob,
+
755 std::string const& signature,
+
756 std::uint32_t version,
+
757 std::string siteUri,
+
758 std::optional<uint256> const& hash,
+
759 lock_guard const&);
+
760
+
761 // This function updates the keyListings_ counts for all the trusted
+
762 // master keys
+
763 void
+ +
765 PublicKey const& pubKey,
+
766 PublisherList const& current,
+
767 std::vector<PublicKey> const& oldList,
+
768 lock_guard const&);
+
769
+
770 static void
+
771 buildBlobInfos(std::map<std::size_t, ValidatorBlobInfo>& blobInfos, PublisherListCollection const& lists);
+
772
+ +
774 buildBlobInfos(PublisherListCollection const& lists);
+
775
+
776 static void
+ +
778 PublicKey const& publisherKey,
+
779 PublisherListCollection const& lists,
+
780 std::size_t maxSequence,
+
781 uint256 const& hash,
+
782 Overlay& overlay,
+
783 HashRouter& hashRouter,
+ +
785
+
786 static void
+ +
788 Peer& peer,
+
789 std::uint64_t peerSequence,
+
790 PublicKey const& publisherKey,
+
791 std::size_t maxSequence,
+
792 std::uint32_t rawVersion,
+
793 std::string const& rawManifest,
+ + +
796 HashRouter& hashRouter,
+ +
798
+
801 boost::filesystem::path
+
802 getCacheFileName(lock_guard const&, PublicKey const& pubKey) const;
+
803
+
807 static Json::Value
+
808 buildFileData(std::string const& pubKey, PublisherListCollection const& pubCollection, beast::Journal j);
+
809
+
813 static Json::Value
+ +
815 std::string const& pubKey,
+
816 PublisherListCollection const& pubCollection,
+
817 std::optional<std::uint32_t> forceVersion,
+ +
819
+
820 template <class Hasher>
+
821 friend void
+
+ +
823 {
+
824 using beast::hash_append;
+ +
826 }
-
826
-
829 void
-
830 cacheValidatorFile(lock_guard const& lock, PublicKey const& pubKey) const;
-
831
- -
841 verify(
-
842 lock_guard const&,
-
843 Json::Value& list,
- -
845 std::string const& blob,
-
846 std::string const& signature);
-
847
-
858 bool
-
859 removePublisherList(lock_guard const&, PublicKey const& publisherKey, PublisherStatus reason);
-
860
- -
872 calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize, std::size_t seenSize);
-
873};
+
827
+
830 void
+
831 cacheValidatorFile(lock_guard const& lock, PublicKey const& pubKey) const;
+
832
+ +
842 verify(
+
843 lock_guard const&,
+
844 Json::Value& list,
+ +
846 std::string const& blob,
+
847 std::string const& signature);
+
848
+
859 bool
+
860 removePublisherList(lock_guard const&, PublicKey const& publisherKey, PublisherStatus reason);
+
861
+ +
873 calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize, std::size_t seenSize);
+
874};
-
874
-
875// hashing helpers
-
876template <class Hasher>
-
877void
-
-
878hash_append(Hasher& h, ValidatorBlobInfo const& blobInfo)
-
879{
-
880 using beast::hash_append;
-
881 hash_append(h, blobInfo.blob, blobInfo.signature);
-
882 if (blobInfo.manifest)
-
883 {
-
884 hash_append(h, *blobInfo.manifest);
-
885 }
-
886}
+
875
+
876// hashing helpers
+
877template <class Hasher>
+
878void
+
+
879hash_append(Hasher& h, ValidatorBlobInfo const& blobInfo)
+
880{
+
881 using beast::hash_append;
+
882 hash_append(h, blobInfo.blob, blobInfo.signature);
+
883 if (blobInfo.manifest)
+
884 {
+
885 hash_append(h, *blobInfo.manifest);
+
886 }
+
887}
-
887
-
888template <class Hasher>
-
889void
-
- -
891{
-
892 for (auto const& item : blobs)
-
893 hash_append(h, item);
-
894}
+
888
+
889template <class Hasher>
+
890void
+
+ +
892{
+
893 for (auto const& item : blobs)
+
894 hash_append(h, item);
+
895}
-
895
-
896template <class Hasher>
-
897void
-
- -
899{
-
900 for (auto const& [_, item] : blobs)
-
901 {
-
902 (void)_;
-
903 hash_append(h, item);
-
904 }
-
905}
+
896
+
897template <class Hasher>
+
898void
+
+ +
900{
+
901 for (auto const& [_, item] : blobs)
+
902 {
+
903 (void)_;
+
904 hash_append(h, item);
+
905 }
+
906}
-
906
-
907} // namespace xrpl
-
908
-
909namespace protocol {
-
910
-
911template <class Hasher>
-
912void
-
-
913hash_append(Hasher& h, TMValidatorList const& msg)
-
914{
-
915 using beast::hash_append;
-
916 hash_append(h, msg.manifest(), msg.blob(), msg.signature(), msg.version());
-
917}
+
907
+
908} // namespace xrpl
+
909
+
910namespace protocol {
+
911
+
912template <class Hasher>
+
913void
+
+
914hash_append(Hasher& h, TMValidatorList const& msg)
+
915{
+
916 using beast::hash_append;
+
917 hash_append(h, msg.manifest(), msg.blob(), msg.signature(), msg.version());
+
918}
-
918
-
919template <class Hasher>
-
920void
-
-
921hash_append(Hasher& h, TMValidatorListCollection const& msg)
-
922{
-
923 using beast::hash_append;
-
924 hash_append(h, msg.manifest(), xrpl::ValidatorList::parseBlobs(msg), msg.version());
-
925}
+
919
+
920template <class Hasher>
+
921void
+
+
922hash_append(Hasher& h, TMValidatorListCollection const& msg)
+
923{
+
924 using beast::hash_append;
+
925 hash_append(h, msg.manifest(), xrpl::ValidatorList::parseBlobs(msg), msg.version());
+
926}
-
926
-
927} // namespace protocol
+
927
+
928} // namespace protocol
@@ -683,20 +684,20 @@ $(document).ready(function() { init_codefold(0); });
bool trustedPublisher(PublicKey const &identity) const
Returns true if public key is a trusted publisher.
static constexpr std::size_t maxSupportedBlobs
hash_set< PublicKey > trustedSigningKeys_
-
std::size_t calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize, std::size_t seenSize)
Return quorum for trusted validator set.
+
std::size_t calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize, std::size_t seenSize)
Return quorum for trusted validator set.
~ValidatorList()=default
static Json::Value buildFileData(std::string const &pubKey, PublisherListCollection const &pubCollection, beast::Journal j)
Build a Json representation of the collection, suitable for writing to a cache file,...
std::vector< std::string > loadLists()
Json::Value getJson() const
Return a JSON representation of the state of the validator list.
void for_each_listed(std::function< void(PublicKey const &, bool)> func) const
Invokes the callback once for every listed validation public key.
-
hash_set< PublicKey > getTrustedMasterKeys() const
get the trusted master public keys
+
hash_set< PublicKey > getTrustedMasterKeys() const
get the trusted master public keys
std::optional< PublicKey > localPubKey_
std::atomic< std::size_t > quorum_
-
QuorumKeys getQuorumKeys() const
Get the quorum and all of the trusted keys.
+
QuorumKeys getQuorumKeys() const
Get the quorum and all of the trusted keys.
void for_each_available(std::function< void(std::string const &manifest, std::uint32_t version, std::map< std::size_t, ValidatorBlobInfo > const &blobInfos, PublicKey const &pubKey, std::size_t maxSequence, uint256 const &hash)> func) const
Invokes the callback once for every available publisher list's raw data members.
std::pair< ListDisposition, std::optional< PublicKey > > verify(lock_guard const &, Json::Value &list, Manifest manifest, std::string const &blob, std::string const &signature)
Check response for trusted valid published list.
beast::Journal const j_
-
TrustChanges updateTrusted(hash_set< NodeID > const &seenValidators, NetClock::time_point closeTime, NetworkOPs &ops, Overlay &overlay, HashRouter &hashRouter)
Update trusted nodes.
+
TrustChanges updateTrusted(hash_set< NodeID > const &seenValidators, NetClock::time_point closeTime, NetworkOPs &ops, Overlay &overlay, HashRouter &hashRouter)
Update trusted nodes.
boost::filesystem::path const dataPath_
std::shared_mutex mutex_
bool load(std::optional< PublicKey > const &localSigningKey, std::vector< std::string > const &configKeys, std::vector< std::string > const &publisherKeys, std::optional< std::size_t > listThreshold={})
Load configured trusted keys.
@@ -708,23 +709,23 @@ $(document).ready(function() { init_codefold(0); });
std::optional< PublicKey > getListedKey(PublicKey const &identity) const
Returns listed master public if public key is included on any lists.
void cacheValidatorFile(lock_guard const &lock, PublicKey const &pubKey) const
Write a JSON UNL to a cache file.
std::lock_guard< decltype(mutex_)> lock_guard
-
hash_set< PublicKey > getNegativeUNL() const
get the master public keys of Negative UNL validators
+
hash_set< PublicKey > getNegativeUNL() const
get the master public keys of Negative UNL validators
std::optional< std::size_t > minimumQuorum_
static std::vector< ValidatorBlobInfo > parseBlobs(std::uint32_t version, Json::Value const &body)
Pull the blob/signature/manifest information out of the appropriate Json body fields depending on the...
ManifestCache & publisherManifests_
hash_map< PublicKey, std::size_t > keyListings_
-
std::size_t getListThreshold() const
get the validator list threshold
+
std::size_t getListThreshold() const
get the validator list threshold
std::optional< TimeKeeper::time_point > expires() const
Return the time when the validator list will expire.
static void buildBlobInfos(std::map< std::size_t, ValidatorBlobInfo > &blobInfos, PublisherListCollection const &lists)
ManifestCache & validatorManifests_
hash_set< PublicKey > negativeUNL_
std::size_t listThreshold_
-
void setNegativeUNL(hash_set< PublicKey > const &negUnl)
set the Negative UNL with validators' master public keys
+
void setNegativeUNL(hash_set< PublicKey > const &negUnl)
set the Negative UNL with validators' master public keys
static void broadcastBlobs(PublicKey const &publisherKey, PublisherListCollection const &lists, std::size_t maxSequence, uint256 const &hash, Overlay &overlay, HashRouter &hashRouter, beast::Journal j)
std::size_t count() const
Return the number of configured validator list sites.
std::optional< PublicKey > getTrustedKey(PublicKey const &identity) const
Returns master public key if public key is trusted.
-
std::vector< std::shared_ptr< STValidation > > negativeUNLFilter(std::vector< std::shared_ptr< STValidation > > &&validations) const
Remove validations that are from validators on the negative UNL.
-
std::optional< Json::Value > getAvailable(std::string_view pubKey, std::optional< std::uint32_t > forceVersion={})
Returns the current valid list for the given publisher key, if available, as a Json object.
+
std::vector< std::shared_ptr< STValidation > > negativeUNLFilter(std::vector< std::shared_ptr< STValidation > > &&validations) const
Remove validations that are from validators on the negative UNL.
+
std::optional< Json::Value > getAvailable(std::string_view pubKey, std::optional< std::uint32_t > forceVersion={})
Returns the current valid list for the given publisher key, if available, as a Json object.
void updatePublisherList(PublicKey const &pubKey, PublisherList const &current, std::vector< PublicKey > const &oldList, lock_guard const &)
PublisherListStats applyListsAndBroadcast(std::string const &manifest, std::uint32_t version, std::vector< ValidatorBlobInfo > const &blobs, std::string siteUri, uint256 const &hash, Overlay &overlay, HashRouter &hashRouter, NetworkOPs &networkOPs)
Apply multiple published lists of public keys, then broadcast it to all peers that have not seen it o...
boost::filesystem::path getCacheFileName(lock_guard const &, PublicKey const &pubKey) const
Get the filename used for caching UNLs.
@@ -732,7 +733,7 @@ $(document).ready(function() { init_codefold(0); });
static std::string const filePrefix_
bool listed(PublicKey const &identity) const
Returns true if public key is included on any lists.
hash_map< PublicKey, PublisherListCollection > publisherLists_
-
friend void hash_append(Hasher &h, PublisherListCollection pl)
+
friend void hash_append(Hasher &h, PublisherListCollection pl)
@@ -742,7 +743,7 @@ $(document).ready(function() { init_codefold(0); });
std::enable_if_t< is_contiguously_hashable< T, Hasher >::value > hash_append(Hasher &h, T const &t) noexcept
Logically concatenate input data to a Hasher.
-
void hash_append(Hasher &h, TMValidatorList const &msg)
+
void hash_append(Hasher &h, TMValidatorList const &msg)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
ListDisposition
diff --git a/ValidatorSite_8cpp_source.html b/ValidatorSite_8cpp_source.html index 9bbbe28365..9dbae7a65b 100644 --- a/ValidatorSite_8cpp_source.html +++ b/ValidatorSite_8cpp_source.html @@ -733,15 +733,15 @@ $(document).ready(function() { init_codefold(0); });
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
-
bool isString() const
+
Value & append(Value const &value)
Append value to array at the end.
+
bool isString() const
UInt asUInt() const
-
bool isObject() const
+
bool isObject() const
Json::Int Int
Definition json_value.h:138
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
bool isNumeric() const
-
bool isInt() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isNumeric() const
+
bool isInt() const
Stream error() const
Definition Journal.h:318
Stream debug() const
Definition Journal.h:300
Stream warn() const
Definition Journal.h:312
diff --git a/Value_8cpp_source.html b/Value_8cpp_source.html index 6e4c40deb7..8319a039e7 100644 --- a/Value_8cpp_source.html +++ b/Value_8cpp_source.html @@ -1482,30 +1482,30 @@ $(document).ready(function() { init_codefold(0); });
UInt index() const
Return the index of the referenced Value. -1 if it is not an arrayValue.
Iterator for object and array value.
Definition json_value.h:618
Represents a JSON value.
Definition json_value.h:130
-
bool isArray() const
-
Value & append(Value const &value)
Append value to array at the end.
+
bool isArray() const
+
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
-
bool isDouble() const
+
bool isDouble() const
char const * asCString() const
Int asInt() const
UInt asAbsUInt() const
Correct absolute value from int or unsigned int.
-
bool isString() const
+
bool isString() const
UInt asUInt() const
-
Members getMemberNames() const
Return a list of the member names.
+
Members getMemberNames() const
Return a list of the member names.
ValueType type() const
-
bool isObject() const
-
Value removeMember(char const *key)
Remove and return the named member.
-
bool isValidIndex(UInt index) const
Return true if index < size().
+
bool isObject() const
+
Value removeMember(char const *key)
Remove and return the named member.
+
bool isValidIndex(UInt index) const
Return true if index < size().
std::string asString() const
Returns the unquoted string value.
-
bool isBool() const
+
bool isBool() const
bool asBool() const
-
bool isUInt() const
-
bool isNull() const
isNull() tests to see if this field is null.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
-
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
+
bool isUInt() const
+
bool isNull() const
isNull() tests to see if this field is null.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
+
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
bool isConvertibleTo(ValueType other) const
double asDouble() const
-
bool isInt() const
+
bool isInt() const
T count_if(T... args)
T empty(T... args)
diff --git a/VaultClawback_8cpp_source.html b/VaultClawback_8cpp_source.html index f4f4351668..be85c2f3ae 100644 --- a/VaultClawback_8cpp_source.html +++ b/VaultClawback_8cpp_source.html @@ -514,7 +514,7 @@ $(document).ready(function() { init_codefold(0); });
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
Asset const & asset() const
Definition STAmount.h:441
-
STAmount const & value() const noexcept
Definition STAmount.h:560
+
STAmount const & value() const noexcept
Definition STAmount.h:562
AccountID const account_
Definition Transactor.h:112
beast::Journal const j_
Definition Transactor.h:110
@@ -546,11 +546,11 @@ $(document).ready(function() { init_codefold(0); });
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2446
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:3149
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
@ ahIGNORE_AUTH
Definition View.h:61
@ temMALFORMED
Definition TER.h:67
@ temBAD_AMOUNT
Definition TER.h:69
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:3174
@ tecWRONG_ASSET
Definition TER.h:341
@ tecNO_ENTRY
Definition TER.h:287
@@ -566,7 +566,7 @@ $(document).ready(function() { init_codefold(0); });
@ lsfMPTCanClawback
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
STAmount clawbackAmount(std::shared_ptr< SLE const > const &vault, std::optional< STAmount > const &maybeAmount, AccountID const &account)
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
diff --git a/VaultCreate_8cpp_source.html b/VaultCreate_8cpp_source.html index 9ed1e8b9f7..9ddd1fbf37 100644 --- a/VaultCreate_8cpp_source.html +++ b/VaultCreate_8cpp_source.html @@ -366,7 +366,7 @@ $(document).ready(function() { init_codefold(0); });
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:194
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
Definition View.cpp:961
@ temMALFORMED
Definition TER.h:67
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
constexpr std::uint32_t const tfVaultPrivate
Definition TxFlags.h:250
@ tecWRONG_ASSET
Definition TER.h:341
@ tecLOCKED
Definition TER.h:339
diff --git a/VaultDelete_8cpp_source.html b/VaultDelete_8cpp_source.html index f59a05e369..e0f20cd89b 100644 --- a/VaultDelete_8cpp_source.html +++ b/VaultDelete_8cpp_source.html @@ -321,13 +321,13 @@ $(document).ready(function() { init_codefold(0); });
std::string transToken(TER code)
Definition TER.cpp:243
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
@ temMALFORMED
Definition TER.h:67
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecNO_ENTRY
Definition TER.h:287
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecNO_PERMISSION
Definition TER.h:286
@ tecHAS_OBLIGATIONS
Definition TER.h:298
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
uint256 key
Definition Keylet.h:20
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
diff --git a/VaultDeposit_8cpp_source.html b/VaultDeposit_8cpp_source.html index b0ba82c954..0f4b2a3ff4 100644 --- a/VaultDeposit_8cpp_source.html +++ b/VaultDeposit_8cpp_source.html @@ -394,7 +394,7 @@ $(document).ready(function() { init_codefold(0); });
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:194
@ temMALFORMED
Definition TER.h:67
@ temBAD_AMOUNT
Definition TER.h:69
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecWRONG_ASSET
Definition TER.h:341
@ tecLOCKED
Definition TER.h:339
@ tecNO_ENTRY
Definition TER.h:287
@@ -410,7 +410,7 @@ $(document).ready(function() { init_codefold(0); });
@ lsfMPTLocked
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
Definition View.cpp:2819
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
diff --git a/VaultInfo_8cpp_source.html b/VaultInfo_8cpp_source.html index f38907a018..c16734a876 100644 --- a/VaultInfo_8cpp_source.html +++ b/VaultInfo_8cpp_source.html @@ -180,7 +180,7 @@ $(document).ready(function() { init_codefold(0); });
93} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
static constexpr UInt maxUInt
Definition json_value.h:144
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:471
@@ -191,7 +191,7 @@ $(document).ready(function() { init_codefold(0); });
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:492
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Json::Value doVaultInfo(RPC::JsonContext &)
Definition VaultInfo.cpp:59
-
static Expected< uint256, Json::Value > parseVault(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
+
static Expected< uint256, Json::Value > parseVault(Json::Value const &params, Json::StaticString const fieldName, unsigned const apiVersion)
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:64
@ rpcACT_MALFORMED
Definition ErrorCodes.h:70
diff --git a/VaultWithdraw_8cpp_source.html b/VaultWithdraw_8cpp_source.html index e7cae85681..400b313233 100644 --- a/VaultWithdraw_8cpp_source.html +++ b/VaultWithdraw_8cpp_source.html @@ -357,7 +357,7 @@ $(document).ready(function() { init_codefold(0); });
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2914
@ temMALFORMED
Definition TER.h:67
@ temBAD_AMOUNT
Definition TER.h:69
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:3174
@ tecWRONG_ASSET
Definition TER.h:341
@ tecNO_ENTRY
Definition TER.h:287
@@ -368,7 +368,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecHAS_OBLIGATIONS
Definition TER.h:298
TER canWithdraw(ReadView const &view, AccountID const &from, AccountID const &to, SLE::const_ref toSle, STAmount const &amount, bool hasDestinationTag)
Checks that can withdraw funds from an object to itself or a destination.
Definition View.cpp:1163
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
diff --git a/Vault__test_8cpp_source.html b/Vault__test_8cpp_source.html index 4b2fab8cc3..77c2915ced 100644 --- a/Vault__test_8cpp_source.html +++ b/Vault__test_8cpp_source.html @@ -1060,4086 +1060,4088 @@ $(document).ready(function() { init_codefold(0); });
969 {
970 using namespace test::jtx;
971
-
972 auto testCase = [this](std::function<void(
-
973 Env & env,
-
974 Account const& issuer,
-
975 Account const& owner,
-
976 Account const& depositor,
-
977 Asset const& asset,
-
978 Vault& vault)> test) {
-
979 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
980 Account issuer{"issuer"};
-
981 Account owner{"owner"};
-
982 Account depositor{"depositor"};
-
983 env.fund(XRP(1000), issuer, owner, depositor);
-
984 env.close();
-
985 Vault vault{env};
-
986 Asset asset = xrpIssue();
-
987
-
988 test(env, issuer, owner, depositor, asset, vault);
-
989 };
-
990
-
991 testCase([this](
-
992 Env& env,
-
993 Account const& issuer,
-
994 Account const& owner,
-
995 Account const& depositor,
-
996 PrettyAsset const& asset,
-
997 Vault& vault) {
-
998 testcase("nothing to set");
-
999 auto tx = vault.set({.owner = owner, .id = keylet::skip().key});
-
1000 tx[sfAssetsMaximum] = asset(0).number();
-
1001 env(tx, ter(tecNO_ENTRY));
-
1002 });
-
1003
-
1004 testCase([this](
-
1005 Env& env,
-
1006 Account const& issuer,
-
1007 Account const& owner,
-
1008 Account const& depositor,
-
1009 PrettyAsset const& asset,
-
1010 Vault& vault) {
-
1011 testcase("nothing to deposit to");
-
1012 auto tx = vault.deposit({.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)});
-
1013 env(tx, ter(tecNO_ENTRY));
-
1014 });
-
1015
-
1016 testCase([this](
-
1017 Env& env,
-
1018 Account const& issuer,
-
1019 Account const& owner,
-
1020 Account const& depositor,
-
1021 PrettyAsset const& asset,
-
1022 Vault& vault) {
-
1023 testcase("nothing to withdraw from");
-
1024 auto tx = vault.withdraw({.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)});
-
1025 env(tx, ter(tecNO_ENTRY));
-
1026 });
-
1027
-
1028 testCase([this](
-
1029 Env& env,
-
1030 Account const& issuer,
-
1031 Account const& owner,
-
1032 Account const& depositor,
-
1033 Asset const& asset,
-
1034 Vault& vault) {
-
1035 testcase("nothing to delete");
-
1036 auto tx = vault.del({.owner = owner, .id = keylet::skip().key});
-
1037 env(tx, ter(tecNO_ENTRY));
-
1038 });
-
1039
-
1040 testCase([this](
-
1041 Env& env,
-
1042 Account const& issuer,
-
1043 Account const& owner,
-
1044 Account const& depositor,
-
1045 Asset const& asset,
-
1046 Vault& vault) {
-
1047 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1048 testcase("transaction is good");
-
1049 env(tx);
-
1050 });
-
1051
-
1052 testCase([this](
-
1053 Env& env,
-
1054 Account const& issuer,
-
1055 Account const& owner,
-
1056 Account const& depositor,
-
1057 Asset const& asset,
-
1058 Vault& vault) {
-
1059 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1060 tx[sfWithdrawalPolicy] = 1;
-
1061 testcase("explicitly select withdrawal policy");
-
1062 env(tx);
-
1063 });
-
1064
-
1065 testCase([this](
-
1066 Env& env,
-
1067 Account const& issuer,
-
1068 Account const& owner,
-
1069 Account const& depositor,
-
1070 Asset const& asset,
-
1071 Vault& vault) {
-
1072 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1073 testcase("insufficient fee");
-
1074 env(tx, fee(env.current()->fees().base - 1), ter(telINSUF_FEE_P));
-
1075 });
-
1076
-
1077 testCase([this](
-
1078 Env& env,
-
1079 Account const& issuer,
-
1080 Account const& owner,
-
1081 Account const& depositor,
-
1082 Asset const& asset,
-
1083 Vault& vault) {
-
1084 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1085 testcase("insufficient reserve");
-
1086 // It is possible to construct a complicated mathematical
-
1087 // expression for this amount, but it is sadly not easy.
-
1088 env(pay(owner, issuer, XRP(775)));
-
1089 env.close();
-
1090 env(tx, ter(tecINSUFFICIENT_RESERVE));
-
1091 });
-
1092
-
1093 testCase([this](
-
1094 Env& env,
-
1095 Account const& issuer,
-
1096 Account const& owner,
-
1097 Account const& depositor,
-
1098 Asset const& asset,
-
1099 Vault& vault) {
-
1100 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1101 tx[sfFlags] = tfVaultPrivate;
-
1102 tx[sfDomainID] = to_string(base_uint<256>(42ul));
-
1103 testcase("non-existing domain");
-
1104 env(tx, ter{tecOBJECT_NOT_FOUND});
-
1105 });
-
1106
-
1107 testCase([this](
-
1108 Env& env,
-
1109 Account const& issuer,
-
1110 Account const& owner,
-
1111 Account const& depositor,
-
1112 Asset const& asset,
-
1113 Vault& vault) {
-
1114 testcase("cannot set Scale=0");
-
1115 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1116 tx[sfScale] = 0;
-
1117 env(tx, ter{temMALFORMED});
-
1118 });
-
1119
-
1120 testCase([this](
-
1121 Env& env,
-
1122 Account const& issuer,
-
1123 Account const& owner,
-
1124 Account const& depositor,
-
1125 Asset const& asset,
-
1126 Vault& vault) {
-
1127 testcase("cannot set Scale=1");
-
1128 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1129 tx[sfScale] = 1;
-
1130 env(tx, ter{temMALFORMED});
-
1131 });
-
1132 }
+
972 auto testCase = [this](
+
973 std::function<void(
+
974 Env & env,
+
975 Account const& issuer,
+
976 Account const& owner,
+
977 Account const& depositor,
+
978 Asset const& asset,
+
979 Vault& vault)> test) {
+
980 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
981 Account issuer{"issuer"};
+
982 Account owner{"owner"};
+
983 Account depositor{"depositor"};
+
984 env.fund(XRP(1000), issuer, owner, depositor);
+
985 env.close();
+
986 Vault vault{env};
+
987 Asset asset = xrpIssue();
+
988
+
989 test(env, issuer, owner, depositor, asset, vault);
+
990 };
+
991
+
992 testCase([this](
+
993 Env& env,
+
994 Account const& issuer,
+
995 Account const& owner,
+
996 Account const& depositor,
+
997 PrettyAsset const& asset,
+
998 Vault& vault) {
+
999 testcase("nothing to set");
+
1000 auto tx = vault.set({.owner = owner, .id = keylet::skip().key});
+
1001 tx[sfAssetsMaximum] = asset(0).number();
+
1002 env(tx, ter(tecNO_ENTRY));
+
1003 });
+
1004
+
1005 testCase([this](
+
1006 Env& env,
+
1007 Account const& issuer,
+
1008 Account const& owner,
+
1009 Account const& depositor,
+
1010 PrettyAsset const& asset,
+
1011 Vault& vault) {
+
1012 testcase("nothing to deposit to");
+
1013 auto tx = vault.deposit({.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)});
+
1014 env(tx, ter(tecNO_ENTRY));
+
1015 });
+
1016
+
1017 testCase([this](
+
1018 Env& env,
+
1019 Account const& issuer,
+
1020 Account const& owner,
+
1021 Account const& depositor,
+
1022 PrettyAsset const& asset,
+
1023 Vault& vault) {
+
1024 testcase("nothing to withdraw from");
+
1025 auto tx = vault.withdraw({.depositor = depositor, .id = keylet::skip().key, .amount = asset(10)});
+
1026 env(tx, ter(tecNO_ENTRY));
+
1027 });
+
1028
+
1029 testCase([this](
+
1030 Env& env,
+
1031 Account const& issuer,
+
1032 Account const& owner,
+
1033 Account const& depositor,
+
1034 Asset const& asset,
+
1035 Vault& vault) {
+
1036 testcase("nothing to delete");
+
1037 auto tx = vault.del({.owner = owner, .id = keylet::skip().key});
+
1038 env(tx, ter(tecNO_ENTRY));
+
1039 });
+
1040
+
1041 testCase([this](
+
1042 Env& env,
+
1043 Account const& issuer,
+
1044 Account const& owner,
+
1045 Account const& depositor,
+
1046 Asset const& asset,
+
1047 Vault& vault) {
+
1048 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1049 testcase("transaction is good");
+
1050 env(tx);
+
1051 });
+
1052
+
1053 testCase([this](
+
1054 Env& env,
+
1055 Account const& issuer,
+
1056 Account const& owner,
+
1057 Account const& depositor,
+
1058 Asset const& asset,
+
1059 Vault& vault) {
+
1060 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1061 tx[sfWithdrawalPolicy] = 1;
+
1062 testcase("explicitly select withdrawal policy");
+
1063 env(tx);
+
1064 });
+
1065
+
1066 testCase([this](
+
1067 Env& env,
+
1068 Account const& issuer,
+
1069 Account const& owner,
+
1070 Account const& depositor,
+
1071 Asset const& asset,
+
1072 Vault& vault) {
+
1073 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1074 testcase("insufficient fee");
+
1075 env(tx, fee(env.current()->fees().base - 1), ter(telINSUF_FEE_P));
+
1076 });
+
1077
+
1078 testCase([this](
+
1079 Env& env,
+
1080 Account const& issuer,
+
1081 Account const& owner,
+
1082 Account const& depositor,
+
1083 Asset const& asset,
+
1084 Vault& vault) {
+
1085 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1086 testcase("insufficient reserve");
+
1087 // It is possible to construct a complicated mathematical
+
1088 // expression for this amount, but it is sadly not easy.
+
1089 env(pay(owner, issuer, XRP(775)));
+
1090 env.close();
+
1091 env(tx, ter(tecINSUFFICIENT_RESERVE));
+
1092 });
+
1093
+
1094 testCase([this](
+
1095 Env& env,
+
1096 Account const& issuer,
+
1097 Account const& owner,
+
1098 Account const& depositor,
+
1099 Asset const& asset,
+
1100 Vault& vault) {
+
1101 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1102 tx[sfFlags] = tfVaultPrivate;
+
1103 tx[sfDomainID] = to_string(base_uint<256>(42ul));
+
1104 testcase("non-existing domain");
+
1105 env(tx, ter{tecOBJECT_NOT_FOUND});
+
1106 });
+
1107
+
1108 testCase([this](
+
1109 Env& env,
+
1110 Account const& issuer,
+
1111 Account const& owner,
+
1112 Account const& depositor,
+
1113 Asset const& asset,
+
1114 Vault& vault) {
+
1115 testcase("cannot set Scale=0");
+
1116 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1117 tx[sfScale] = 0;
+
1118 env(tx, ter{temMALFORMED});
+
1119 });
+
1120
+
1121 testCase([this](
+
1122 Env& env,
+
1123 Account const& issuer,
+
1124 Account const& owner,
+
1125 Account const& depositor,
+
1126 Asset const& asset,
+
1127 Vault& vault) {
+
1128 testcase("cannot set Scale=1");
+
1129 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1130 tx[sfScale] = 1;
+
1131 env(tx, ter{temMALFORMED});
+
1132 });
+
1133 }
-
1133
-
1134 void
-
- -
1136 {
-
1137 using namespace test::jtx;
-
1138 {
-
1139 {
-
1140 testcase("IOU fail because MPT is disabled");
-
1141 Env env{*this, (testable_amendments() - featureMPTokensV1) | featureSingleAssetVault};
-
1142 Account issuer{"issuer"};
-
1143 Account owner{"owner"};
-
1144 env.fund(XRP(1000), issuer, owner);
-
1145 env.close();
-
1146
-
1147 Vault vault{env};
-
1148 Asset asset = issuer["IOU"].asset();
-
1149 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1150
-
1151 env(tx, ter(temDISABLED));
-
1152 env.close();
-
1153 }
-
1154
-
1155 {
-
1156 testcase("IOU fail create frozen");
-
1157 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1158 Account issuer{"issuer"};
-
1159 Account owner{"owner"};
-
1160 env.fund(XRP(1000), issuer, owner);
-
1161 env.close();
-
1162 env(fset(issuer, asfGlobalFreeze));
-
1163 env.close();
-
1164
-
1165 Vault vault{env};
-
1166 Asset asset = issuer["IOU"].asset();
-
1167 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1168
-
1169 env(tx, ter(tecFROZEN));
-
1170 env.close();
-
1171 }
-
1172
-
1173 {
-
1174 testcase("IOU fail create no ripling");
-
1175 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1176 Account issuer{"issuer"};
-
1177 Account owner{"owner"};
-
1178 env.fund(XRP(1000), issuer, owner);
-
1179 env.close();
-
1180 env(fclear(issuer, asfDefaultRipple));
-
1181 env.close();
-
1182
-
1183 Vault vault{env};
-
1184 Asset asset = issuer["IOU"].asset();
-
1185 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1186 env(tx, ter(terNO_RIPPLE));
-
1187 env.close();
-
1188 }
-
1189
-
1190 {
-
1191 testcase("IOU no issuer");
-
1192 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1193 Account issuer{"issuer"};
-
1194 Account owner{"owner"};
-
1195 env.fund(XRP(1000), owner);
-
1196 env.close();
-
1197
-
1198 Vault vault{env};
-
1199 Asset asset = issuer["IOU"].asset();
-
1200 {
-
1201 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1202 env(tx, ter(terNO_ACCOUNT));
-
1203 env.close();
-
1204 }
-
1205 }
-
1206 }
-
1207
-
1208 {
-
1209 testcase("IOU fail create vault for AMM LPToken");
-
1210 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1211 Account const gw("gateway");
-
1212 Account const alice("alice");
-
1213 Account const carol("carol");
-
1214 IOU const USD = gw["USD"];
-
1215
-
1216 auto const [asset1, asset2] = std::pair<STAmount, STAmount>(XRP(10000), USD(10000));
-
1217 auto toFund = [&](STAmount const& a) -> STAmount {
-
1218 if (a.native())
-
1219 {
-
1220 auto const defXRP = XRP(30000);
-
1221 if (a <= defXRP)
-
1222 return defXRP;
-
1223 return a + XRP(1000);
-
1224 }
-
1225 auto const defIOU = STAmount{a.issue(), 30000};
-
1226 if (a <= defIOU)
-
1227 return defIOU;
-
1228 return a + STAmount{a.issue(), 1000};
-
1229 };
-
1230 auto const toFund1 = toFund(asset1);
-
1231 auto const toFund2 = toFund(asset2);
-
1232 BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
-
1233
-
1234 if (!asset1.native() && !asset2.native())
-
1235 fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
-
1236 else if (asset1.native())
-
1237 fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
-
1238 else if (asset2.native())
-
1239 fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
-
1240
-
1241 AMM ammAlice(env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0});
-
1242
-
1243 Account const owner{"owner"};
-
1244 env.fund(XRP(1000000), owner);
-
1245
-
1246 Vault vault{env};
-
1247 auto [tx, k] = vault.create({.owner = owner, .asset = ammAlice.lptIssue()});
-
1248 env(tx, ter{tecWRONG_ASSET});
-
1249 env.close();
-
1250 }
-
1251 }
+
1134
+
1135 void
+
+ +
1137 {
+
1138 using namespace test::jtx;
+
1139 {
+
1140 {
+
1141 testcase("IOU fail because MPT is disabled");
+
1142 Env env{*this, (testable_amendments() - featureMPTokensV1) | featureSingleAssetVault};
+
1143 Account issuer{"issuer"};
+
1144 Account owner{"owner"};
+
1145 env.fund(XRP(1000), issuer, owner);
+
1146 env.close();
+
1147
+
1148 Vault vault{env};
+
1149 Asset asset = issuer["IOU"].asset();
+
1150 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1151
+
1152 env(tx, ter(temDISABLED));
+
1153 env.close();
+
1154 }
+
1155
+
1156 {
+
1157 testcase("IOU fail create frozen");
+
1158 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1159 Account issuer{"issuer"};
+
1160 Account owner{"owner"};
+
1161 env.fund(XRP(1000), issuer, owner);
+
1162 env.close();
+
1163 env(fset(issuer, asfGlobalFreeze));
+
1164 env.close();
+
1165
+
1166 Vault vault{env};
+
1167 Asset asset = issuer["IOU"].asset();
+
1168 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1169
+
1170 env(tx, ter(tecFROZEN));
+
1171 env.close();
+
1172 }
+
1173
+
1174 {
+
1175 testcase("IOU fail create no ripling");
+
1176 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1177 Account issuer{"issuer"};
+
1178 Account owner{"owner"};
+
1179 env.fund(XRP(1000), issuer, owner);
+
1180 env.close();
+
1181 env(fclear(issuer, asfDefaultRipple));
+
1182 env.close();
+
1183
+
1184 Vault vault{env};
+
1185 Asset asset = issuer["IOU"].asset();
+
1186 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1187 env(tx, ter(terNO_RIPPLE));
+
1188 env.close();
+
1189 }
+
1190
+
1191 {
+
1192 testcase("IOU no issuer");
+
1193 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1194 Account issuer{"issuer"};
+
1195 Account owner{"owner"};
+
1196 env.fund(XRP(1000), owner);
+
1197 env.close();
+
1198
+
1199 Vault vault{env};
+
1200 Asset asset = issuer["IOU"].asset();
+
1201 {
+
1202 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1203 env(tx, ter(terNO_ACCOUNT));
+
1204 env.close();
+
1205 }
+
1206 }
+
1207 }
+
1208
+
1209 {
+
1210 testcase("IOU fail create vault for AMM LPToken");
+
1211 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1212 Account const gw("gateway");
+
1213 Account const alice("alice");
+
1214 Account const carol("carol");
+
1215 IOU const USD = gw["USD"];
+
1216
+
1217 auto const [asset1, asset2] = std::pair<STAmount, STAmount>(XRP(10000), USD(10000));
+
1218 auto toFund = [&](STAmount const& a) -> STAmount {
+
1219 if (a.native())
+
1220 {
+
1221 auto const defXRP = XRP(30000);
+
1222 if (a <= defXRP)
+
1223 return defXRP;
+
1224 return a + XRP(1000);
+
1225 }
+
1226 auto const defIOU = STAmount{a.issue(), 30000};
+
1227 if (a <= defIOU)
+
1228 return defIOU;
+
1229 return a + STAmount{a.issue(), 1000};
+
1230 };
+
1231 auto const toFund1 = toFund(asset1);
+
1232 auto const toFund2 = toFund(asset2);
+
1233 BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
+
1234
+
1235 if (!asset1.native() && !asset2.native())
+
1236 fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
+
1237 else if (asset1.native())
+
1238 fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
+
1239 else if (asset2.native())
+
1240 fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
+
1241
+
1242 AMM ammAlice(env, alice, asset1, asset2, CreateArg{.log = false, .tfee = 0});
+
1243
+
1244 Account const owner{"owner"};
+
1245 env.fund(XRP(1000000), owner);
+
1246
+
1247 Vault vault{env};
+
1248 auto [tx, k] = vault.create({.owner = owner, .asset = ammAlice.lptIssue()});
+
1249 env(tx, ter{tecWRONG_ASSET});
+
1250 env.close();
+
1251 }
+
1252 }
-
1252
-
1253 void
-
- -
1255 {
-
1256 using namespace test::jtx;
-
1257
-
1258 auto testCase = [this](std::function<void(
-
1259 Env & env,
-
1260 Account const& issuer,
-
1261 Account const& owner,
-
1262 Account const& depositor,
-
1263 Asset const& asset,
-
1264 Vault& vault)> test) {
-
1265 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1266 Account issuer{"issuer"};
-
1267 Account owner{"owner"};
-
1268 Account depositor{"depositor"};
-
1269 env.fund(XRP(1000), issuer, owner, depositor);
-
1270 env.close();
-
1271 Vault vault{env};
-
1272 MPTTester mptt{env, issuer, mptInitNoFund};
-
1273 // Locked because that is the default flag.
-
1274 mptt.create();
-
1275 Asset asset = mptt.issuanceID();
-
1276
-
1277 test(env, issuer, owner, depositor, asset, vault);
-
1278 };
-
1279
-
1280 testCase([this](
-
1281 Env& env,
-
1282 Account const& issuer,
-
1283 Account const& owner,
-
1284 Account const& depositor,
-
1285 Asset const& asset,
-
1286 Vault& vault) {
-
1287 testcase("MPT no authorization");
-
1288 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1289 env(tx, ter(tecNO_AUTH));
-
1290 });
-
1291
-
1292 testCase([this](
-
1293 Env& env,
-
1294 Account const& issuer,
-
1295 Account const& owner,
-
1296 Account const& depositor,
-
1297 Asset const& asset,
-
1298 Vault& vault) {
-
1299 testcase("MPT cannot set Scale=0");
-
1300 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1301 tx[sfScale] = 0;
-
1302 env(tx, ter{temMALFORMED});
-
1303 });
-
1304
-
1305 testCase([this](
-
1306 Env& env,
-
1307 Account const& issuer,
-
1308 Account const& owner,
-
1309 Account const& depositor,
-
1310 Asset const& asset,
-
1311 Vault& vault) {
-
1312 testcase("MPT cannot set Scale=1");
-
1313 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1314 tx[sfScale] = 1;
-
1315 env(tx, ter{temMALFORMED});
-
1316 });
-
1317 }
+
1253
+
1254 void
+
+ +
1256 {
+
1257 using namespace test::jtx;
+
1258
+
1259 auto testCase = [this](
+
1260 std::function<void(
+
1261 Env & env,
+
1262 Account const& issuer,
+
1263 Account const& owner,
+
1264 Account const& depositor,
+
1265 Asset const& asset,
+
1266 Vault& vault)> test) {
+
1267 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1268 Account issuer{"issuer"};
+
1269 Account owner{"owner"};
+
1270 Account depositor{"depositor"};
+
1271 env.fund(XRP(1000), issuer, owner, depositor);
+
1272 env.close();
+
1273 Vault vault{env};
+
1274 MPTTester mptt{env, issuer, mptInitNoFund};
+
1275 // Locked because that is the default flag.
+
1276 mptt.create();
+
1277 Asset asset = mptt.issuanceID();
+
1278
+
1279 test(env, issuer, owner, depositor, asset, vault);
+
1280 };
+
1281
+
1282 testCase([this](
+
1283 Env& env,
+
1284 Account const& issuer,
+
1285 Account const& owner,
+
1286 Account const& depositor,
+
1287 Asset const& asset,
+
1288 Vault& vault) {
+
1289 testcase("MPT no authorization");
+
1290 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1291 env(tx, ter(tecNO_AUTH));
+
1292 });
+
1293
+
1294 testCase([this](
+
1295 Env& env,
+
1296 Account const& issuer,
+
1297 Account const& owner,
+
1298 Account const& depositor,
+
1299 Asset const& asset,
+
1300 Vault& vault) {
+
1301 testcase("MPT cannot set Scale=0");
+
1302 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1303 tx[sfScale] = 0;
+
1304 env(tx, ter{temMALFORMED});
+
1305 });
+
1306
+
1307 testCase([this](
+
1308 Env& env,
+
1309 Account const& issuer,
+
1310 Account const& owner,
+
1311 Account const& depositor,
+
1312 Asset const& asset,
+
1313 Vault& vault) {
+
1314 testcase("MPT cannot set Scale=1");
+
1315 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1316 tx[sfScale] = 1;
+
1317 env(tx, ter{temMALFORMED});
+
1318 });
+
1319 }
-
1318
-
1319 void
-
- -
1321 {
-
1322 using namespace test::jtx;
-
1323
-
1324 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1325 Account issuer{"issuer"};
-
1326 Account owner{"owner"};
-
1327 Account depositor{"depositor"};
-
1328 env.fund(XRP(1000), issuer, owner, depositor);
-
1329 env.close();
-
1330
-
1331 Vault vault{env};
-
1332 PrettyAsset asset = issuer["IOU"];
-
1333 env.trust(asset(1000), owner);
-
1334 env(pay(issuer, owner, asset(100)));
-
1335 env.trust(asset(1000), depositor);
-
1336 env(pay(issuer, depositor, asset(100)));
-
1337 env.close();
-
1338
-
1339 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1340 tx[sfFlags] = tfVaultShareNonTransferable;
-
1341 env(tx);
-
1342 env.close();
-
1343
-
1344 {
-
1345 testcase("nontransferable deposits");
-
1346 auto tx1 = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(40)});
-
1347 env(tx1);
-
1348
-
1349 auto tx2 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(60)});
-
1350 env(tx2);
-
1351 env.close();
-
1352 }
-
1353
-
1354 auto const vaultAccount = //
-
1355 [&env, key = keylet.key, this]() -> AccountID {
-
1356 auto jvVault = env.rpc("vault_info", strHex(key));
-
1357
-
1358 BEAST_EXPECT(jvVault[jss::result][jss::vault][sfAssetsTotal] == "100");
-
1359 BEAST_EXPECT(jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] == "100000000");
-
1360
-
1361 // Vault pseudo-account
-
1362 return parseBase58<AccountID>(jvVault[jss::result][jss::vault][jss::Account].asString()).value();
-
1363 }();
-
1364
-
1365 auto const MptID = makeMptID(1, vaultAccount);
-
1366 Asset shares = MptID;
-
1367
-
1368 {
-
1369 testcase("nontransferable shares cannot be moved");
-
1370 env(pay(owner, depositor, shares(10)), ter{tecNO_AUTH});
-
1371 env(pay(depositor, owner, shares(10)), ter{tecNO_AUTH});
-
1372 }
-
1373
-
1374 {
-
1375 testcase("nontransferable shares can be used to withdraw");
-
1376 auto tx1 = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(20)});
-
1377 env(tx1);
-
1378
-
1379 auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(30)});
-
1380 env(tx2);
-
1381 env.close();
-
1382 }
-
1383
-
1384 {
-
1385 testcase("nontransferable shares balance check");
-
1386 auto jvVault = env.rpc("vault_info", strHex(keylet.key));
-
1387 BEAST_EXPECT(jvVault[jss::result][jss::vault][sfAssetsTotal] == "50");
-
1388 BEAST_EXPECT(jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] == "50000000");
-
1389 }
-
1390
-
1391 {
-
1392 testcase("nontransferable shares withdraw rest");
-
1393 auto tx1 = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(20)});
-
1394 env(tx1);
-
1395
-
1396 auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(30)});
-
1397 env(tx2);
-
1398 env.close();
-
1399 }
-
1400
-
1401 {
-
1402 testcase("nontransferable shares delete empty vault");
-
1403 auto tx = vault.del({.owner = owner, .id = keylet.key});
-
1404 env(tx);
-
1405 BEAST_EXPECT(!env.le(keylet));
-
1406 }
-
1407 }
+
1320
+
1321 void
+
+ +
1323 {
+
1324 using namespace test::jtx;
+
1325
+
1326 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1327 Account issuer{"issuer"};
+
1328 Account owner{"owner"};
+
1329 Account depositor{"depositor"};
+
1330 env.fund(XRP(1000), issuer, owner, depositor);
+
1331 env.close();
+
1332
+
1333 Vault vault{env};
+
1334 PrettyAsset asset = issuer["IOU"];
+
1335 env.trust(asset(1000), owner);
+
1336 env(pay(issuer, owner, asset(100)));
+
1337 env.trust(asset(1000), depositor);
+
1338 env(pay(issuer, depositor, asset(100)));
+
1339 env.close();
+
1340
+
1341 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1342 tx[sfFlags] = tfVaultShareNonTransferable;
+
1343 env(tx);
+
1344 env.close();
+
1345
+
1346 {
+
1347 testcase("nontransferable deposits");
+
1348 auto tx1 = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(40)});
+
1349 env(tx1);
+
1350
+
1351 auto tx2 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(60)});
+
1352 env(tx2);
+
1353 env.close();
+
1354 }
+
1355
+
1356 auto const vaultAccount = //
+
1357 [&env, key = keylet.key, this]() -> AccountID {
+
1358 auto jvVault = env.rpc("vault_info", strHex(key));
+
1359
+
1360 BEAST_EXPECT(jvVault[jss::result][jss::vault][sfAssetsTotal] == "100");
+
1361 BEAST_EXPECT(jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] == "100000000");
+
1362
+
1363 // Vault pseudo-account
+
1364 return parseBase58<AccountID>(jvVault[jss::result][jss::vault][jss::Account].asString()).value();
+
1365 }();
+
1366
+
1367 auto const MptID = makeMptID(1, vaultAccount);
+
1368 Asset shares = MptID;
+
1369
+
1370 {
+
1371 testcase("nontransferable shares cannot be moved");
+
1372 env(pay(owner, depositor, shares(10)), ter{tecNO_AUTH});
+
1373 env(pay(depositor, owner, shares(10)), ter{tecNO_AUTH});
+
1374 }
+
1375
+
1376 {
+
1377 testcase("nontransferable shares can be used to withdraw");
+
1378 auto tx1 = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(20)});
+
1379 env(tx1);
+
1380
+
1381 auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(30)});
+
1382 env(tx2);
+
1383 env.close();
+
1384 }
+
1385
+
1386 {
+
1387 testcase("nontransferable shares balance check");
+
1388 auto jvVault = env.rpc("vault_info", strHex(keylet.key));
+
1389 BEAST_EXPECT(jvVault[jss::result][jss::vault][sfAssetsTotal] == "50");
+
1390 BEAST_EXPECT(jvVault[jss::result][jss::vault][jss::shares][sfOutstandingAmount] == "50000000");
+
1391 }
+
1392
+
1393 {
+
1394 testcase("nontransferable shares withdraw rest");
+
1395 auto tx1 = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(20)});
+
1396 env(tx1);
+
1397
+
1398 auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(30)});
+
1399 env(tx2);
+
1400 env.close();
+
1401 }
+
1402
+
1403 {
+
1404 testcase("nontransferable shares delete empty vault");
+
1405 auto tx = vault.del({.owner = owner, .id = keylet.key});
+
1406 env(tx);
+
1407 BEAST_EXPECT(!env.le(keylet));
+
1408 }
+
1409 }
-
1408
-
1409 void
-
- -
1411 {
-
1412 using namespace test::jtx;
-
1413
-
1414 struct CaseArgs
-
1415 {
-
1416 bool enableClawback = true;
-
1417 bool requireAuth = true;
-
1418 int initialXRP = 1000;
-
1419 };
-
1420
-
1421 auto testCase = [this](
-
1422 std::function<void(
-
1423 Env & env,
-
1424 Account const& issuer,
-
1425 Account const& owner,
-
1426 Account const& depositor,
-
1427 Asset const& asset,
-
1428 Vault& vault,
-
1429 MPTTester& mptt)> test,
-
1430 CaseArgs args = {}) {
-
1431 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
1432 Account issuer{"issuer"};
-
1433 Account owner{"owner"};
-
1434 Account depositor{"depositor"};
-
1435 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
-
1436 env.close();
-
1437 Vault vault{env};
-
1438
-
1439 MPTTester mptt{env, issuer, mptInitNoFund};
-
1440 auto const none = LedgerSpecificFlags(0);
-
1441 mptt.create(
-
1442 {.flags = tfMPTCanTransfer | tfMPTCanLock | (args.enableClawback ? tfMPTCanClawback : none) |
-
1443 (args.requireAuth ? tfMPTRequireAuth : none),
-
1444 .mutableFlags = tmfMPTCanMutateCanTransfer});
-
1445 PrettyAsset asset = mptt.issuanceID();
-
1446 mptt.authorize({.account = owner});
-
1447 mptt.authorize({.account = depositor});
-
1448 if (args.requireAuth)
-
1449 {
-
1450 mptt.authorize({.account = issuer, .holder = owner});
-
1451 mptt.authorize({.account = issuer, .holder = depositor});
-
1452 }
-
1453
-
1454 env(pay(issuer, depositor, asset(1000)));
-
1455 env.close();
-
1456
-
1457 test(env, issuer, owner, depositor, asset, vault, mptt);
-
1458 };
-
1459
-
1460 testCase([this](
-
1461 Env& env,
-
1462 Account const& issuer,
-
1463 Account const& owner,
-
1464 Account const& depositor,
-
1465 PrettyAsset const& asset,
-
1466 Vault& vault,
-
1467 MPTTester& mptt) {
-
1468 testcase("MPT nothing to clawback from");
-
1469 auto tx =
-
1470 vault.clawback({.issuer = issuer, .id = keylet::skip().key, .holder = depositor, .amount = asset(10)});
-
1471 env(tx, ter(tecNO_ENTRY));
-
1472 });
-
1473
-
1474 testCase([this](
-
1475 Env& env,
-
1476 Account const& issuer,
-
1477 Account const& owner,
-
1478 Account const& depositor,
-
1479 Asset const& asset,
-
1480 Vault& vault,
-
1481 MPTTester& mptt) {
-
1482 testcase("MPT global lock blocks create");
-
1483 mptt.set({.account = issuer, .flags = tfMPTLock});
-
1484 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1485 env(tx, ter(tecLOCKED));
-
1486 });
-
1487
-
1488 testCase([this](
-
1489 Env& env,
-
1490 Account const& issuer,
-
1491 Account const& owner,
-
1492 Account const& depositor,
-
1493 Asset const& asset,
-
1494 Vault& vault,
-
1495 MPTTester& mptt) {
-
1496 testcase("MPT global lock blocks deposit");
-
1497 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1498 env(tx);
-
1499 env.close();
-
1500
-
1501 mptt.set({.account = issuer, .flags = tfMPTLock});
-
1502 env.close();
-
1503
-
1504 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1505 env(tx, ter{tecLOCKED});
-
1506 env.close();
-
1507
-
1508 // Can delete empty vault, even if global lock
-
1509 tx = vault.del({.owner = owner, .id = keylet.key});
-
1510 env(tx);
-
1511 });
-
1512
-
1513 testCase([this](
-
1514 Env& env,
-
1515 Account const& issuer,
-
1516 Account const& owner,
-
1517 Account const& depositor,
-
1518 Asset const& asset,
-
1519 Vault& vault,
-
1520 MPTTester& mptt) {
-
1521 testcase("MPT global lock blocks withdrawal");
-
1522 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1523 env(tx);
-
1524 env.close();
-
1525 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1526 env(tx);
-
1527 env.close();
-
1528
-
1529 // Check that the OutstandingAmount field of MPTIssuance
-
1530 // accounts for the issued shares.
-
1531 auto v = env.le(keylet);
-
1532 BEAST_EXPECT(v);
-
1533 MPTID share = (*v)[sfShareMPTID];
-
1534 auto issuance = env.le(keylet::mptIssuance(share));
-
1535 BEAST_EXPECT(issuance);
-
1536 Number outstandingShares = issuance->at(sfOutstandingAmount);
-
1537 BEAST_EXPECT(outstandingShares == 100);
-
1538
-
1539 mptt.set({.account = issuer, .flags = tfMPTLock});
-
1540 env.close();
-
1541
-
1542 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1543 env(tx, ter(tecLOCKED));
-
1544
-
1545 tx[sfDestination] = issuer.human();
-
1546 env(tx, ter(tecLOCKED));
-
1547
-
1548 // Clawback is still permitted, even with global lock
-
1549 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
-
1550 env(tx);
-
1551 env.close();
-
1552
-
1553 // Clawback removed shares MPToken
-
1554 auto const mptSle = env.le(keylet::mptoken(share, depositor.id()));
-
1555 BEAST_EXPECT(mptSle == nullptr);
-
1556
-
1557 // Can delete empty vault, even if global lock
-
1558 tx = vault.del({.owner = owner, .id = keylet.key});
-
1559 env(tx);
-
1560 });
-
1561
-
1562 testCase([this](
-
1563 Env& env,
-
1564 Account const& issuer,
-
1565 Account const& owner,
-
1566 Account const& depositor,
-
1567 PrettyAsset const& asset,
-
1568 Vault& vault,
-
1569 MPTTester& mptt) {
-
1570 testcase("MPT only issuer can clawback");
-
1571
-
1572 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1573 env(tx);
-
1574 env.close();
-
1575
-
1576 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1577 env(tx);
-
1578 env.close();
-
1579
-
1580 {
-
1581 auto tx = vault.clawback({
-
1582 .issuer = depositor,
-
1583 .id = keylet.key,
-
1584 .holder = depositor,
-
1585 });
-
1586 env(tx, ter(tecNO_PERMISSION));
-
1587 }
-
1588
-
1589 {
-
1590 auto tx = vault.clawback({
-
1591 .issuer = owner,
-
1592 .id = keylet.key,
-
1593 .holder = depositor,
-
1594 });
-
1595 env(tx, ter(tecNO_PERMISSION));
-
1596 }
-
1597 });
-
1598
-
1599 testCase(
-
1600 [this](
-
1601 Env& env,
-
1602 Account const& issuer,
-
1603 Account const& owner,
-
1604 Account const& depositor,
-
1605 PrettyAsset const& asset,
-
1606 Vault& vault,
-
1607 MPTTester& mptt) {
-
1608 testcase("MPT depositor without MPToken, auth required");
-
1609
-
1610 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1611 env(tx);
-
1612 env.close();
-
1613
-
1614 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
-
1615 env(tx);
-
1616 env.close();
-
1617
-
1618 {
-
1619 // Remove depositor MPToken and it will not be re-created
-
1620 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
-
1621 env.close();
-
1622
-
1623 auto const mptoken = keylet::mptoken(mptt.issuanceID(), depositor);
-
1624 auto const sleMPT1 = env.le(mptoken);
-
1625 BEAST_EXPECT(sleMPT1 == nullptr);
-
1626
-
1627 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1628 env(tx, ter{tecNO_AUTH});
-
1629 env.close();
-
1630
-
1631 auto const sleMPT2 = env.le(mptoken);
-
1632 BEAST_EXPECT(sleMPT2 == nullptr);
-
1633 }
-
1634
-
1635 {
-
1636 // Set destination to 3rd party without MPToken
-
1637 Account charlie{"charlie"};
-
1638 env.fund(XRP(1000), charlie);
-
1639 env.close();
-
1640
-
1641 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1642 tx[sfDestination] = charlie.human();
-
1643 env(tx, ter(tecNO_AUTH));
-
1644 }
-
1645 },
-
1646 {.requireAuth = true});
-
1647
-
1648 testCase(
-
1649 [this](
-
1650 Env& env,
-
1651 Account const& issuer,
-
1652 Account const& owner,
-
1653 Account const& depositor,
-
1654 PrettyAsset const& asset,
-
1655 Vault& vault,
-
1656 MPTTester& mptt) {
-
1657 testcase("MPT depositor without MPToken, no auth required");
-
1658
-
1659 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1660 env(tx);
-
1661 env.close();
-
1662 auto v = env.le(keylet);
-
1663 BEAST_EXPECT(v);
-
1664
-
1665 tx = vault.deposit(
-
1666 {.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); // all assets held by depositor
-
1667 env(tx);
-
1668 env.close();
-
1669
-
1670 {
-
1671 // Remove depositor's MPToken and it will be re-created
-
1672 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
-
1673 env.close();
-
1674
-
1675 auto const mptoken = keylet::mptoken(mptt.issuanceID(), depositor);
-
1676 auto const sleMPT1 = env.le(mptoken);
-
1677 BEAST_EXPECT(sleMPT1 == nullptr);
-
1678
-
1679 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1680 env(tx);
-
1681 env.close();
-
1682
-
1683 auto const sleMPT2 = env.le(mptoken);
-
1684 BEAST_EXPECT(sleMPT2 != nullptr);
-
1685 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
-
1686 }
-
1687
-
1688 {
-
1689 // Remove 3rd party MPToken and it will not be re-created
-
1690 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
-
1691 env.close();
-
1692
-
1693 auto const mptoken = keylet::mptoken(mptt.issuanceID(), owner);
-
1694 auto const sleMPT1 = env.le(mptoken);
-
1695 BEAST_EXPECT(sleMPT1 == nullptr);
-
1696
-
1697 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1698 tx[sfDestination] = owner.human();
-
1699 env(tx, ter(tecNO_AUTH));
-
1700 env.close();
-
1701
-
1702 auto const sleMPT2 = env.le(mptoken);
-
1703 BEAST_EXPECT(sleMPT2 == nullptr);
-
1704 }
-
1705 },
-
1706 {.requireAuth = false});
-
1707
-
1708 auto const [acctReserve, incReserve] = [this]() -> std::pair<int, int> {
-
1709 Env env{*this, testable_amendments()};
-
1710 return {
-
1711 env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(),
-
1712 env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()};
-
1713 }();
-
1714
-
1715 testCase(
-
1716 [&, this](
-
1717 Env& env,
-
1718 Account const& issuer,
-
1719 Account const& owner,
-
1720 Account const& depositor,
-
1721 PrettyAsset const& asset,
-
1722 Vault& vault,
-
1723 MPTTester& mptt) {
-
1724 testcase("MPT fail reserve to re-create MPToken");
-
1725
-
1726 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1727 env(tx);
-
1728 env.close();
-
1729 auto v = env.le(keylet);
-
1730 BEAST_EXPECT(v);
-
1731
-
1732 env(pay(depositor, owner, asset(1000)));
-
1733 env.close();
-
1734
-
1735 tx = vault.deposit(
-
1736 {.depositor = owner, .id = keylet.key, .amount = asset(1000)}); // all assets held by owner
-
1737 env(tx);
-
1738 env.close();
-
1739
-
1740 {
-
1741 // Remove owners's MPToken and it will not be re-created
-
1742 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
-
1743 env.close();
-
1744
-
1745 auto const mptoken = keylet::mptoken(mptt.issuanceID(), owner);
-
1746 auto const sleMPT = env.le(mptoken);
-
1747 BEAST_EXPECT(sleMPT == nullptr);
-
1748
-
1749 // Use one reserve so the next transaction fails
-
1750 env(ticket::create(owner, 1));
-
1751 env.close();
-
1752
-
1753 // No reserve to create MPToken for asset in VaultWithdraw
-
1754 tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)});
-
1755 env(tx, ter{tecINSUFFICIENT_RESERVE});
-
1756 env.close();
-
1757
-
1758 env(pay(depositor, owner, XRP(incReserve)));
-
1759 env.close();
-
1760
-
1761 // Withdraw can now create asset MPToken, tx will succeed
-
1762 env(tx);
-
1763 env.close();
-
1764 }
-
1765 },
-
1766 {.requireAuth = false, .initialXRP = acctReserve + incReserve * 4 + 1});
-
1767
-
1768 testCase([this](
-
1769 Env& env,
-
1770 Account const& issuer,
-
1771 Account const& owner,
-
1772 Account const& depositor,
-
1773 PrettyAsset const& asset,
-
1774 Vault& vault,
-
1775 MPTTester& mptt) {
-
1776 testcase("MPT issuance deleted");
-
1777
-
1778 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1779 env(tx);
-
1780 env.close();
-
1781
-
1782 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
-
1783 env(tx);
-
1784 env.close();
-
1785
-
1786 {
-
1787 auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
-
1788 env(tx);
-
1789 }
-
1790
-
1791 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
-
1792 env.close();
-
1793
-
1794 {
-
1795 auto [tx, keylet] = vault.create({.owner = depositor, .asset = asset});
-
1796 env(tx, ter{tecOBJECT_NOT_FOUND});
-
1797 }
-
1798
-
1799 {
-
1800 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(10)});
-
1801 env(tx, ter{tecOBJECT_NOT_FOUND});
-
1802 }
-
1803
-
1804 {
-
1805 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)});
-
1806 env(tx, ter{tecOBJECT_NOT_FOUND});
-
1807 }
-
1808
-
1809 {
-
1810 auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
-
1811 env(tx, ter{tecOBJECT_NOT_FOUND});
-
1812 }
-
1813
-
1814 env(vault.del({.owner = owner, .id = keylet.key}));
-
1815 });
-
1816
-
1817 testCase([this](
-
1818 Env& env,
-
1819 Account const& issuer,
-
1820 Account const& owner,
-
1821 Account const& depositor,
-
1822 PrettyAsset const& asset,
-
1823 Vault& vault,
-
1824 MPTTester& mptt) {
-
1825 testcase("MPT vault owner can receive shares unless unauthorized");
-
1826
-
1827 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1828 env(tx);
-
1829 env.close();
-
1830
-
1831 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
-
1832 env(tx);
-
1833 env.close();
-
1834
-
1835 auto const issuanceId = [&env](xrpl::Keylet keylet) -> MPTID {
-
1836 auto const vault = env.le(keylet);
-
1837 return vault->at(sfShareMPTID);
-
1838 }(keylet);
-
1839 PrettyAsset shares = MPTIssue(issuanceId);
-
1840
-
1841 {
-
1842 // owner has MPToken for shares they did not explicitly create
-
1843 env(pay(depositor, owner, shares(1)));
-
1844 env.close();
-
1845
-
1846 tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = shares(1)});
-
1847 env(tx);
-
1848 env.close();
-
1849
-
1850 // owner's MPToken for vault shares not destroyed by withdraw
-
1851 env(pay(depositor, owner, shares(1)));
-
1852 env.close();
-
1853
-
1854 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)});
-
1855 env(tx);
-
1856 env.close();
-
1857
-
1858 // owner's MPToken for vault shares not destroyed by clawback
-
1859 env(pay(depositor, owner, shares(1)));
-
1860 env.close();
-
1861
-
1862 // pay back, so we can destroy owner's MPToken now
-
1863 env(pay(owner, depositor, shares(1)));
-
1864 env.close();
-
1865
-
1866 {
-
1867 // explicitly destroy vault owners MPToken with zero balance
-
1868 Json::Value jv;
-
1869 jv[sfAccount] = owner.human();
-
1870 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
-
1871 jv[sfFlags] = tfMPTUnauthorize;
-
1872 jv[sfTransactionType] = jss::MPTokenAuthorize;
-
1873 env(jv);
-
1874 env.close();
-
1875 }
-
1876
-
1877 // owner no longer has MPToken for vault shares
-
1878 tx = pay(depositor, owner, shares(1));
-
1879 env(tx, ter{tecNO_AUTH});
-
1880 env.close();
-
1881
-
1882 // destroy all remaining shares, so we can delete vault
-
1883 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
-
1884 env(tx);
-
1885 env.close();
-
1886
-
1887 // will soft fail destroying MPToken for vault owner
-
1888 env(vault.del({.owner = owner, .id = keylet.key}));
-
1889 env.close();
-
1890 }
-
1891 });
-
1892
-
1893 testCase(
-
1894 [this](
-
1895 Env& env,
-
1896 Account const& issuer,
-
1897 Account const& owner,
-
1898 Account const& depositor,
-
1899 PrettyAsset const& asset,
-
1900 Vault& vault,
-
1901 MPTTester& mptt) {
-
1902 testcase("MPT clawback disabled");
-
1903
-
1904 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1905 env(tx);
-
1906 env.close();
-
1907
-
1908 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
-
1909 env(tx);
-
1910 env.close();
-
1911
-
1912 {
-
1913 auto tx =
-
1914 vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
-
1915 env(tx, ter{tecNO_PERMISSION});
-
1916 }
-
1917 },
-
1918 {.enableClawback = false});
-
1919
-
1920 testCase([this](
-
1921 Env& env,
-
1922 Account const& issuer,
-
1923 Account const& owner,
-
1924 Account const& depositor,
-
1925 Asset const& asset,
-
1926 Vault& vault,
-
1927 MPTTester& mptt) {
-
1928 testcase("MPT un-authorization");
-
1929 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1930 env(tx);
-
1931 env.close();
-
1932 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
-
1933 env(tx);
-
1934 env.close();
-
1935
-
1936 mptt.authorize({.account = issuer, .holder = depositor, .flags = tfMPTUnauthorize});
-
1937 env.close();
-
1938
-
1939 {
-
1940 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1941 env(tx, ter(tecNO_AUTH));
-
1942
-
1943 // Withdrawal to other (authorized) accounts works
-
1944 tx[sfDestination] = issuer.human();
-
1945 env(tx);
-
1946 env.close();
-
1947
-
1948 tx[sfDestination] = owner.human();
-
1949 env(tx);
-
1950 env.close();
-
1951 }
-
1952
-
1953 {
-
1954 // Cannot deposit some more
-
1955 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1956 env(tx, ter(tecNO_AUTH));
-
1957 }
-
1958
-
1959 {
-
1960 // Cannot clawback if issuer is the holder
-
1961 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = issuer, .amount = asset(800)});
-
1962 env(tx, ter(tecNO_PERMISSION));
-
1963 }
-
1964 // Clawback works
-
1965 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(800)});
-
1966 env(tx);
-
1967 env.close();
-
1968
-
1969 env(vault.del({.owner = owner, .id = keylet.key}));
-
1970 });
-
1971
-
1972 testCase([this](
-
1973 Env& env,
-
1974 Account const& issuer,
-
1975 Account const& owner,
-
1976 Account const& depositor,
-
1977 Asset const& asset,
-
1978 Vault& vault,
-
1979 MPTTester& mptt) {
-
1980 testcase("MPT lock of vault pseudo-account");
-
1981 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
1982 env(tx);
-
1983 env.close();
-
1984
-
1985 auto const vaultAccount = [&env, keylet = keylet, this]() -> AccountID {
-
1986 auto const vault = env.le(keylet);
-
1987 BEAST_EXPECT(vault != nullptr);
-
1988 return vault->at(sfAccount);
-
1989 }();
-
1990
-
1991 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
1992 env(tx);
-
1993 env.close();
-
1994
-
1995 tx = [&]() {
-
1996 Json::Value jv;
-
1997 jv[jss::Account] = issuer.human();
-
1998 jv[sfMPTokenIssuanceID] = to_string(asset.get<MPTIssue>().getMptID());
-
1999 jv[jss::Holder] = toBase58(vaultAccount);
-
2000 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
-
2001 jv[jss::Flags] = tfMPTLock;
-
2002 return jv;
-
2003 }();
-
2004 env(tx);
-
2005 env.close();
-
2006
-
2007 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
2008 env(tx, ter(tecLOCKED));
-
2009
-
2010 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
2011 env(tx, ter(tecLOCKED));
-
2012
-
2013 // Clawback works, even when locked
-
2014 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(100)});
-
2015 env(tx);
-
2016
-
2017 // Can delete an empty vault even when asset is locked.
-
2018 tx = vault.del({.owner = owner, .id = keylet.key});
-
2019 env(tx);
-
2020 });
-
2021
-
2022 {
-
2023 testcase("MPT shares to a vault");
-
2024
-
2025 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
2026 Account owner{"owner"};
-
2027 Account issuer{"issuer"};
-
2028 env.fund(XRP(1000000), owner, issuer);
-
2029 env.close();
-
2030 Vault vault{env};
-
2031
-
2032 MPTTester mptt{env, issuer, mptInitNoFund};
- -
2034 mptt.authorize({.account = owner});
-
2035 mptt.authorize({.account = issuer, .holder = owner});
-
2036 PrettyAsset asset = mptt.issuanceID();
-
2037 env(pay(issuer, owner, asset(100)));
-
2038 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
-
2039 env(tx1);
-
2040 env.close();
-
2041
-
2042 auto const shares = [&env, keylet = k1, this]() -> Asset {
-
2043 auto const vault = env.le(keylet);
-
2044 BEAST_EXPECT(vault != nullptr);
-
2045 return MPTIssue(vault->at(sfShareMPTID));
-
2046 }();
-
2047
-
2048 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
-
2049 env(tx2, ter{tecWRONG_ASSET});
-
2050 env.close();
-
2051 }
-
2052
-
2053 testCase([this](
-
2054 Env& env,
-
2055 Account const&,
-
2056 Account const& owner,
-
2057 Account const& depositor,
-
2058 PrettyAsset const& asset,
-
2059 Vault& vault,
-
2060 MPTTester& mptt) {
-
2061 testcase("MPT non-transferable");
-
2062
-
2063 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2064 env(tx);
-
2065 env.close();
-
2066
-
2067 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
-
2068 env(tx);
-
2069 env.close();
-
2070
-
2071 // Remove CanTransfer
-
2072 mptt.set({.mutableFlags = tmfMPTClearCanTransfer});
-
2073 env.close();
-
2074
-
2075 env(tx, ter{tecNO_AUTH});
-
2076 env.close();
-
2077
-
2078 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1410
+
1411 void
+
+ +
1413 {
+
1414 using namespace test::jtx;
+
1415
+
1416 struct CaseArgs
+
1417 {
+
1418 bool enableClawback = true;
+
1419 bool requireAuth = true;
+
1420 int initialXRP = 1000;
+
1421 };
+
1422
+
1423 auto testCase = [this](
+
1424 std::function<void(
+
1425 Env & env,
+
1426 Account const& issuer,
+
1427 Account const& owner,
+
1428 Account const& depositor,
+
1429 Asset const& asset,
+
1430 Vault& vault,
+
1431 MPTTester& mptt)> test,
+
1432 CaseArgs args = {}) {
+
1433 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
1434 Account issuer{"issuer"};
+
1435 Account owner{"owner"};
+
1436 Account depositor{"depositor"};
+
1437 env.fund(XRP(args.initialXRP), issuer, owner, depositor);
+
1438 env.close();
+
1439 Vault vault{env};
+
1440
+
1441 MPTTester mptt{env, issuer, mptInitNoFund};
+
1442 auto const none = LedgerSpecificFlags(0);
+
1443 mptt.create(
+
1444 {.flags = tfMPTCanTransfer | tfMPTCanLock | (args.enableClawback ? tfMPTCanClawback : none) |
+
1445 (args.requireAuth ? tfMPTRequireAuth : none),
+
1446 .mutableFlags = tmfMPTCanMutateCanTransfer});
+
1447 PrettyAsset asset = mptt.issuanceID();
+
1448 mptt.authorize({.account = owner});
+
1449 mptt.authorize({.account = depositor});
+
1450 if (args.requireAuth)
+
1451 {
+
1452 mptt.authorize({.account = issuer, .holder = owner});
+
1453 mptt.authorize({.account = issuer, .holder = depositor});
+
1454 }
+
1455
+
1456 env(pay(issuer, depositor, asset(1000)));
+
1457 env.close();
+
1458
+
1459 test(env, issuer, owner, depositor, asset, vault, mptt);
+
1460 };
+
1461
+
1462 testCase([this](
+
1463 Env& env,
+
1464 Account const& issuer,
+
1465 Account const& owner,
+
1466 Account const& depositor,
+
1467 PrettyAsset const& asset,
+
1468 Vault& vault,
+
1469 MPTTester& mptt) {
+
1470 testcase("MPT nothing to clawback from");
+
1471 auto tx =
+
1472 vault.clawback({.issuer = issuer, .id = keylet::skip().key, .holder = depositor, .amount = asset(10)});
+
1473 env(tx, ter(tecNO_ENTRY));
+
1474 });
+
1475
+
1476 testCase([this](
+
1477 Env& env,
+
1478 Account const& issuer,
+
1479 Account const& owner,
+
1480 Account const& depositor,
+
1481 Asset const& asset,
+
1482 Vault& vault,
+
1483 MPTTester& mptt) {
+
1484 testcase("MPT global lock blocks create");
+
1485 mptt.set({.account = issuer, .flags = tfMPTLock});
+
1486 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1487 env(tx, ter(tecLOCKED));
+
1488 });
+
1489
+
1490 testCase([this](
+
1491 Env& env,
+
1492 Account const& issuer,
+
1493 Account const& owner,
+
1494 Account const& depositor,
+
1495 Asset const& asset,
+
1496 Vault& vault,
+
1497 MPTTester& mptt) {
+
1498 testcase("MPT global lock blocks deposit");
+
1499 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1500 env(tx);
+
1501 env.close();
+
1502
+
1503 mptt.set({.account = issuer, .flags = tfMPTLock});
+
1504 env.close();
+
1505
+
1506 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1507 env(tx, ter{tecLOCKED});
+
1508 env.close();
+
1509
+
1510 // Can delete empty vault, even if global lock
+
1511 tx = vault.del({.owner = owner, .id = keylet.key});
+
1512 env(tx);
+
1513 });
+
1514
+
1515 testCase([this](
+
1516 Env& env,
+
1517 Account const& issuer,
+
1518 Account const& owner,
+
1519 Account const& depositor,
+
1520 Asset const& asset,
+
1521 Vault& vault,
+
1522 MPTTester& mptt) {
+
1523 testcase("MPT global lock blocks withdrawal");
+
1524 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1525 env(tx);
+
1526 env.close();
+
1527 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1528 env(tx);
+
1529 env.close();
+
1530
+
1531 // Check that the OutstandingAmount field of MPTIssuance
+
1532 // accounts for the issued shares.
+
1533 auto v = env.le(keylet);
+
1534 BEAST_EXPECT(v);
+
1535 MPTID share = (*v)[sfShareMPTID];
+
1536 auto issuance = env.le(keylet::mptIssuance(share));
+
1537 BEAST_EXPECT(issuance);
+
1538 Number outstandingShares = issuance->at(sfOutstandingAmount);
+
1539 BEAST_EXPECT(outstandingShares == 100);
+
1540
+
1541 mptt.set({.account = issuer, .flags = tfMPTLock});
+
1542 env.close();
+
1543
+
1544 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1545 env(tx, ter(tecLOCKED));
+
1546
+
1547 tx[sfDestination] = issuer.human();
+
1548 env(tx, ter(tecLOCKED));
+
1549
+
1550 // Clawback is still permitted, even with global lock
+
1551 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
+
1552 env(tx);
+
1553 env.close();
+
1554
+
1555 // Clawback removed shares MPToken
+
1556 auto const mptSle = env.le(keylet::mptoken(share, depositor.id()));
+
1557 BEAST_EXPECT(mptSle == nullptr);
+
1558
+
1559 // Can delete empty vault, even if global lock
+
1560 tx = vault.del({.owner = owner, .id = keylet.key});
+
1561 env(tx);
+
1562 });
+
1563
+
1564 testCase([this](
+
1565 Env& env,
+
1566 Account const& issuer,
+
1567 Account const& owner,
+
1568 Account const& depositor,
+
1569 PrettyAsset const& asset,
+
1570 Vault& vault,
+
1571 MPTTester& mptt) {
+
1572 testcase("MPT only issuer can clawback");
+
1573
+
1574 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1575 env(tx);
+
1576 env.close();
+
1577
+
1578 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1579 env(tx);
+
1580 env.close();
+
1581
+
1582 {
+
1583 auto tx = vault.clawback({
+
1584 .issuer = depositor,
+
1585 .id = keylet.key,
+
1586 .holder = depositor,
+
1587 });
+
1588 env(tx, ter(tecNO_PERMISSION));
+
1589 }
+
1590
+
1591 {
+
1592 auto tx = vault.clawback({
+
1593 .issuer = owner,
+
1594 .id = keylet.key,
+
1595 .holder = depositor,
+
1596 });
+
1597 env(tx, ter(tecNO_PERMISSION));
+
1598 }
+
1599 });
+
1600
+
1601 testCase(
+
1602 [this](
+
1603 Env& env,
+
1604 Account const& issuer,
+
1605 Account const& owner,
+
1606 Account const& depositor,
+
1607 PrettyAsset const& asset,
+
1608 Vault& vault,
+
1609 MPTTester& mptt) {
+
1610 testcase("MPT depositor without MPToken, auth required");
+
1611
+
1612 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1613 env(tx);
+
1614 env.close();
+
1615
+
1616 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
+
1617 env(tx);
+
1618 env.close();
+
1619
+
1620 {
+
1621 // Remove depositor MPToken and it will not be re-created
+
1622 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
+
1623 env.close();
+
1624
+
1625 auto const mptoken = keylet::mptoken(mptt.issuanceID(), depositor);
+
1626 auto const sleMPT1 = env.le(mptoken);
+
1627 BEAST_EXPECT(sleMPT1 == nullptr);
+
1628
+
1629 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1630 env(tx, ter{tecNO_AUTH});
+
1631 env.close();
+
1632
+
1633 auto const sleMPT2 = env.le(mptoken);
+
1634 BEAST_EXPECT(sleMPT2 == nullptr);
+
1635 }
+
1636
+
1637 {
+
1638 // Set destination to 3rd party without MPToken
+
1639 Account charlie{"charlie"};
+
1640 env.fund(XRP(1000), charlie);
+
1641 env.close();
+
1642
+
1643 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1644 tx[sfDestination] = charlie.human();
+
1645 env(tx, ter(tecNO_AUTH));
+
1646 }
+
1647 },
+
1648 {.requireAuth = true});
+
1649
+
1650 testCase(
+
1651 [this](
+
1652 Env& env,
+
1653 Account const& issuer,
+
1654 Account const& owner,
+
1655 Account const& depositor,
+
1656 PrettyAsset const& asset,
+
1657 Vault& vault,
+
1658 MPTTester& mptt) {
+
1659 testcase("MPT depositor without MPToken, no auth required");
+
1660
+
1661 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1662 env(tx);
+
1663 env.close();
+
1664 auto v = env.le(keylet);
+
1665 BEAST_EXPECT(v);
+
1666
+
1667 tx = vault.deposit(
+
1668 {.depositor = depositor, .id = keylet.key, .amount = asset(1000)}); // all assets held by depositor
+
1669 env(tx);
+
1670 env.close();
+
1671
+
1672 {
+
1673 // Remove depositor's MPToken and it will be re-created
+
1674 mptt.authorize({.account = depositor, .flags = tfMPTUnauthorize});
+
1675 env.close();
+
1676
+
1677 auto const mptoken = keylet::mptoken(mptt.issuanceID(), depositor);
+
1678 auto const sleMPT1 = env.le(mptoken);
+
1679 BEAST_EXPECT(sleMPT1 == nullptr);
+
1680
+
1681 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1682 env(tx);
+
1683 env.close();
+
1684
+
1685 auto const sleMPT2 = env.le(mptoken);
+
1686 BEAST_EXPECT(sleMPT2 != nullptr);
+
1687 BEAST_EXPECT(sleMPT2->at(sfMPTAmount) == 100);
+
1688 }
+
1689
+
1690 {
+
1691 // Remove 3rd party MPToken and it will not be re-created
+
1692 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
+
1693 env.close();
+
1694
+
1695 auto const mptoken = keylet::mptoken(mptt.issuanceID(), owner);
+
1696 auto const sleMPT1 = env.le(mptoken);
+
1697 BEAST_EXPECT(sleMPT1 == nullptr);
+
1698
+
1699 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1700 tx[sfDestination] = owner.human();
+
1701 env(tx, ter(tecNO_AUTH));
+
1702 env.close();
+
1703
+
1704 auto const sleMPT2 = env.le(mptoken);
+
1705 BEAST_EXPECT(sleMPT2 == nullptr);
+
1706 }
+
1707 },
+
1708 {.requireAuth = false});
+
1709
+
1710 auto const [acctReserve, incReserve] = [this]() -> std::pair<int, int> {
+
1711 Env env{*this, testable_amendments()};
+
1712 return {
+
1713 env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(),
+
1714 env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()};
+
1715 }();
+
1716
+
1717 testCase(
+
1718 [&, this](
+
1719 Env& env,
+
1720 Account const& issuer,
+
1721 Account const& owner,
+
1722 Account const& depositor,
+
1723 PrettyAsset const& asset,
+
1724 Vault& vault,
+
1725 MPTTester& mptt) {
+
1726 testcase("MPT fail reserve to re-create MPToken");
+
1727
+
1728 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1729 env(tx);
+
1730 env.close();
+
1731 auto v = env.le(keylet);
+
1732 BEAST_EXPECT(v);
+
1733
+
1734 env(pay(depositor, owner, asset(1000)));
+
1735 env.close();
+
1736
+
1737 tx = vault.deposit(
+
1738 {.depositor = owner, .id = keylet.key, .amount = asset(1000)}); // all assets held by owner
+
1739 env(tx);
+
1740 env.close();
+
1741
+
1742 {
+
1743 // Remove owners's MPToken and it will not be re-created
+
1744 mptt.authorize({.account = owner, .flags = tfMPTUnauthorize});
+
1745 env.close();
+
1746
+
1747 auto const mptoken = keylet::mptoken(mptt.issuanceID(), owner);
+
1748 auto const sleMPT = env.le(mptoken);
+
1749 BEAST_EXPECT(sleMPT == nullptr);
+
1750
+
1751 // Use one reserve so the next transaction fails
+
1752 env(ticket::create(owner, 1));
+
1753 env.close();
+
1754
+
1755 // No reserve to create MPToken for asset in VaultWithdraw
+
1756 tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)});
+
1757 env(tx, ter{tecINSUFFICIENT_RESERVE});
+
1758 env.close();
+
1759
+
1760 env(pay(depositor, owner, XRP(incReserve)));
+
1761 env.close();
+
1762
+
1763 // Withdraw can now create asset MPToken, tx will succeed
+
1764 env(tx);
+
1765 env.close();
+
1766 }
+
1767 },
+
1768 {.requireAuth = false, .initialXRP = acctReserve + incReserve * 4 + 1});
+
1769
+
1770 testCase([this](
+
1771 Env& env,
+
1772 Account const& issuer,
+
1773 Account const& owner,
+
1774 Account const& depositor,
+
1775 PrettyAsset const& asset,
+
1776 Vault& vault,
+
1777 MPTTester& mptt) {
+
1778 testcase("MPT issuance deleted");
+
1779
+
1780 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1781 env(tx);
+
1782 env.close();
+
1783
+
1784 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
+
1785 env(tx);
+
1786 env.close();
+
1787
+
1788 {
+
1789 auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
+
1790 env(tx);
+
1791 }
+
1792
+
1793 mptt.destroy({.issuer = issuer, .id = mptt.issuanceID()});
+
1794 env.close();
+
1795
+
1796 {
+
1797 auto [tx, keylet] = vault.create({.owner = depositor, .asset = asset});
+
1798 env(tx, ter{tecOBJECT_NOT_FOUND});
+
1799 }
+
1800
+
1801 {
+
1802 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(10)});
+
1803 env(tx, ter{tecOBJECT_NOT_FOUND});
+
1804 }
+
1805
+
1806 {
+
1807 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)});
+
1808 env(tx, ter{tecOBJECT_NOT_FOUND});
+
1809 }
+
1810
+
1811 {
+
1812 auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
+
1813 env(tx, ter{tecOBJECT_NOT_FOUND});
+
1814 }
+
1815
+
1816 env(vault.del({.owner = owner, .id = keylet.key}));
+
1817 });
+
1818
+
1819 testCase([this](
+
1820 Env& env,
+
1821 Account const& issuer,
+
1822 Account const& owner,
+
1823 Account const& depositor,
+
1824 PrettyAsset const& asset,
+
1825 Vault& vault,
+
1826 MPTTester& mptt) {
+
1827 testcase("MPT vault owner can receive shares unless unauthorized");
+
1828
+
1829 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1830 env(tx);
+
1831 env.close();
+
1832
+
1833 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
+
1834 env(tx);
+
1835 env.close();
+
1836
+
1837 auto const issuanceId = [&env](xrpl::Keylet keylet) -> MPTID {
+
1838 auto const vault = env.le(keylet);
+
1839 return vault->at(sfShareMPTID);
+
1840 }(keylet);
+
1841 PrettyAsset shares = MPTIssue(issuanceId);
+
1842
+
1843 {
+
1844 // owner has MPToken for shares they did not explicitly create
+
1845 env(pay(depositor, owner, shares(1)));
+
1846 env.close();
+
1847
+
1848 tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = shares(1)});
+
1849 env(tx);
+
1850 env.close();
+
1851
+
1852 // owner's MPToken for vault shares not destroyed by withdraw
+
1853 env(pay(depositor, owner, shares(1)));
+
1854 env.close();
+
1855
+
1856 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)});
+
1857 env(tx);
+
1858 env.close();
+
1859
+
1860 // owner's MPToken for vault shares not destroyed by clawback
+
1861 env(pay(depositor, owner, shares(1)));
+
1862 env.close();
+
1863
+
1864 // pay back, so we can destroy owner's MPToken now
+
1865 env(pay(owner, depositor, shares(1)));
+
1866 env.close();
+
1867
+
1868 {
+
1869 // explicitly destroy vault owners MPToken with zero balance
+
1870 Json::Value jv;
+
1871 jv[sfAccount] = owner.human();
+
1872 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
+
1873 jv[sfFlags] = tfMPTUnauthorize;
+
1874 jv[sfTransactionType] = jss::MPTokenAuthorize;
+
1875 env(jv);
+
1876 env.close();
+
1877 }
+
1878
+
1879 // owner no longer has MPToken for vault shares
+
1880 tx = pay(depositor, owner, shares(1));
+
1881 env(tx, ter{tecNO_AUTH});
+
1882 env.close();
+
1883
+
1884 // destroy all remaining shares, so we can delete vault
+
1885 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
+
1886 env(tx);
+
1887 env.close();
+
1888
+
1889 // will soft fail destroying MPToken for vault owner
+
1890 env(vault.del({.owner = owner, .id = keylet.key}));
+
1891 env.close();
+
1892 }
+
1893 });
+
1894
+
1895 testCase(
+
1896 [this](
+
1897 Env& env,
+
1898 Account const& issuer,
+
1899 Account const& owner,
+
1900 Account const& depositor,
+
1901 PrettyAsset const& asset,
+
1902 Vault& vault,
+
1903 MPTTester& mptt) {
+
1904 testcase("MPT clawback disabled");
+
1905
+
1906 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1907 env(tx);
+
1908 env.close();
+
1909
+
1910 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
+
1911 env(tx);
+
1912 env.close();
+
1913
+
1914 {
+
1915 auto tx =
+
1916 vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
+
1917 env(tx, ter{tecNO_PERMISSION});
+
1918 }
+
1919 },
+
1920 {.enableClawback = false});
+
1921
+
1922 testCase([this](
+
1923 Env& env,
+
1924 Account const& issuer,
+
1925 Account const& owner,
+
1926 Account const& depositor,
+
1927 Asset const& asset,
+
1928 Vault& vault,
+
1929 MPTTester& mptt) {
+
1930 testcase("MPT un-authorization");
+
1931 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1932 env(tx);
+
1933 env.close();
+
1934 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1000)});
+
1935 env(tx);
+
1936 env.close();
+
1937
+
1938 mptt.authorize({.account = issuer, .holder = depositor, .flags = tfMPTUnauthorize});
+
1939 env.close();
+
1940
+
1941 {
+
1942 auto tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1943 env(tx, ter(tecNO_AUTH));
+
1944
+
1945 // Withdrawal to other (authorized) accounts works
+
1946 tx[sfDestination] = issuer.human();
+
1947 env(tx);
+
1948 env.close();
+
1949
+
1950 tx[sfDestination] = owner.human();
+
1951 env(tx);
+
1952 env.close();
+
1953 }
+
1954
+
1955 {
+
1956 // Cannot deposit some more
+
1957 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1958 env(tx, ter(tecNO_AUTH));
+
1959 }
+
1960
+
1961 {
+
1962 // Cannot clawback if issuer is the holder
+
1963 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = issuer, .amount = asset(800)});
+
1964 env(tx, ter(tecNO_PERMISSION));
+
1965 }
+
1966 // Clawback works
+
1967 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(800)});
+
1968 env(tx);
+
1969 env.close();
+
1970
+
1971 env(vault.del({.owner = owner, .id = keylet.key}));
+
1972 });
+
1973
+
1974 testCase([this](
+
1975 Env& env,
+
1976 Account const& issuer,
+
1977 Account const& owner,
+
1978 Account const& depositor,
+
1979 Asset const& asset,
+
1980 Vault& vault,
+
1981 MPTTester& mptt) {
+
1982 testcase("MPT lock of vault pseudo-account");
+
1983 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
1984 env(tx);
+
1985 env.close();
+
1986
+
1987 auto const vaultAccount = [&env, keylet = keylet, this]() -> AccountID {
+
1988 auto const vault = env.le(keylet);
+
1989 BEAST_EXPECT(vault != nullptr);
+
1990 return vault->at(sfAccount);
+
1991 }();
+
1992
+
1993 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
1994 env(tx);
+
1995 env.close();
+
1996
+
1997 tx = [&]() {
+
1998 Json::Value jv;
+
1999 jv[jss::Account] = issuer.human();
+
2000 jv[sfMPTokenIssuanceID] = to_string(asset.get<MPTIssue>().getMptID());
+
2001 jv[jss::Holder] = toBase58(vaultAccount);
+
2002 jv[jss::TransactionType] = jss::MPTokenIssuanceSet;
+
2003 jv[jss::Flags] = tfMPTLock;
+
2004 return jv;
+
2005 }();
+
2006 env(tx);
+
2007 env.close();
+
2008
+
2009 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
2010 env(tx, ter(tecLOCKED));
+
2011
+
2012 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
2013 env(tx, ter(tecLOCKED));
+
2014
+
2015 // Clawback works, even when locked
+
2016 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(100)});
+
2017 env(tx);
+
2018
+
2019 // Can delete an empty vault even when asset is locked.
+
2020 tx = vault.del({.owner = owner, .id = keylet.key});
+
2021 env(tx);
+
2022 });
+
2023
+
2024 {
+
2025 testcase("MPT shares to a vault");
+
2026
+
2027 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
2028 Account owner{"owner"};
+
2029 Account issuer{"issuer"};
+
2030 env.fund(XRP(1000000), owner, issuer);
+
2031 env.close();
+
2032 Vault vault{env};
+
2033
+
2034 MPTTester mptt{env, issuer, mptInitNoFund};
+ +
2036 mptt.authorize({.account = owner});
+
2037 mptt.authorize({.account = issuer, .holder = owner});
+
2038 PrettyAsset asset = mptt.issuanceID();
+
2039 env(pay(issuer, owner, asset(100)));
+
2040 auto [tx1, k1] = vault.create({.owner = owner, .asset = asset});
+
2041 env(tx1);
+
2042 env.close();
+
2043
+
2044 auto const shares = [&env, keylet = k1, this]() -> Asset {
+
2045 auto const vault = env.le(keylet);
+
2046 BEAST_EXPECT(vault != nullptr);
+
2047 return MPTIssue(vault->at(sfShareMPTID));
+
2048 }();
+
2049
+
2050 auto [tx2, k2] = vault.create({.owner = owner, .asset = shares});
+
2051 env(tx2, ter{tecWRONG_ASSET});
+
2052 env.close();
+
2053 }
+
2054
+
2055 testCase([this](
+
2056 Env& env,
+
2057 Account const&,
+
2058 Account const& owner,
+
2059 Account const& depositor,
+
2060 PrettyAsset const& asset,
+
2061 Vault& vault,
+
2062 MPTTester& mptt) {
+
2063 testcase("MPT non-transferable");
+
2064
+
2065 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2066 env(tx);
+
2067 env.close();
+
2068
+
2069 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
2070 env(tx);
+
2071 env.close();
+
2072
+
2073 // Remove CanTransfer
+
2074 mptt.set({.mutableFlags = tmfMPTClearCanTransfer});
+
2075 env.close();
+
2076
+
2077 env(tx, ter{tecNO_AUTH});
+
2078 env.close();
2079
-
2080 env(tx, ter{tecNO_AUTH});
-
2081 env.close();
-
2082
-
2083 // Restore CanTransfer
-
2084 mptt.set({.mutableFlags = tmfMPTSetCanTransfer});
-
2085 env.close();
-
2086
-
2087 env(tx);
-
2088 env.close();
-
2089
-
2090 // Delete vault with zero balance
-
2091 env(vault.del({.owner = owner, .id = keylet.key}));
-
2092 });
-
2093 }
+
2080 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
+
2081
+
2082 env(tx, ter{tecNO_AUTH});
+
2083 env.close();
+
2084
+
2085 // Restore CanTransfer
+
2086 mptt.set({.mutableFlags = tmfMPTSetCanTransfer});
+
2087 env.close();
+
2088
+
2089 env(tx);
+
2090 env.close();
+
2091
+
2092 // Delete vault with zero balance
+
2093 env(vault.del({.owner = owner, .id = keylet.key}));
+
2094 });
+
2095 }
-
2094
-
2095 void
-
- -
2097 {
-
2098 using namespace test::jtx;
-
2099
-
2100 struct CaseArgs
-
2101 {
-
2102 int initialXRP = 1000;
-
2103 Number initialIOU = 200;
-
2104 double transferRate = 1.0;
-
2105 bool charlieRipple = true;
-
2106 };
-
2107
-
2108 auto testCase = [&, this](
-
2109 std::function<void(
-
2110 Env & env,
-
2111 Account const& owner,
-
2112 Account const& issuer,
-
2113 Account const& charlie,
-
2114 std::function<Account(xrpl::Keylet)> vaultAccount,
-
2115 Vault& vault,
-
2116 PrettyAsset const& asset,
-
2117 std::function<MPTID(xrpl::Keylet)> issuanceId)> test,
-
2118 CaseArgs args = {}) {
-
2119 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
2120 Account const owner{"owner"};
-
2121 Account const issuer{"issuer"};
-
2122 Account const charlie{"charlie"};
-
2123 Vault vault{env};
-
2124 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
-
2125 env(fset(issuer, asfAllowTrustLineClawback));
-
2126 env.close();
-
2127
-
2128 PrettyAsset const asset = issuer["IOU"];
-
2129 env.trust(asset(1000), owner);
-
2130 env(pay(issuer, owner, asset(args.initialIOU)));
-
2131 env.close();
-
2132 if (!args.charlieRipple)
-
2133 {
-
2134 env(fset(issuer, 0, asfDefaultRipple));
-
2135 env.close();
-
2136 env.trust(asset(1000), charlie);
+
2096
+
2097 void
+
+ +
2099 {
+
2100 using namespace test::jtx;
+
2101
+
2102 struct CaseArgs
+
2103 {
+
2104 int initialXRP = 1000;
+
2105 Number initialIOU = 200;
+
2106 double transferRate = 1.0;
+
2107 bool charlieRipple = true;
+
2108 };
+
2109
+
2110 auto testCase = [&, this](
+
2111 std::function<void(
+
2112 Env & env,
+
2113 Account const& owner,
+
2114 Account const& issuer,
+
2115 Account const& charlie,
+
2116 std::function<Account(xrpl::Keylet)> vaultAccount,
+
2117 Vault& vault,
+
2118 PrettyAsset const& asset,
+
2119 std::function<MPTID(xrpl::Keylet)> issuanceId)> test,
+
2120 CaseArgs args = {}) {
+
2121 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
2122 Account const owner{"owner"};
+
2123 Account const issuer{"issuer"};
+
2124 Account const charlie{"charlie"};
+
2125 Vault vault{env};
+
2126 env.fund(XRP(args.initialXRP), issuer, owner, charlie);
+
2127 env(fset(issuer, asfAllowTrustLineClawback));
+
2128 env.close();
+
2129
+
2130 PrettyAsset const asset = issuer["IOU"];
+
2131 env.trust(asset(1000), owner);
+
2132 env(pay(issuer, owner, asset(args.initialIOU)));
+
2133 env.close();
+
2134 if (!args.charlieRipple)
+
2135 {
+
2136 env(fset(issuer, 0, asfDefaultRipple));
2137 env.close();
-
2138 env(pay(issuer, charlie, asset(args.initialIOU)));
+
2138 env.trust(asset(1000), charlie);
2139 env.close();
-
2140 env(fset(issuer, asfDefaultRipple));
-
2141 }
-
2142 else
-
2143 env.trust(asset(1000), charlie);
-
2144 env.close();
-
2145 env(rate(issuer, args.transferRate));
+
2140 env(pay(issuer, charlie, asset(args.initialIOU)));
+
2141 env.close();
+
2142 env(fset(issuer, asfDefaultRipple));
+
2143 }
+
2144 else
+
2145 env.trust(asset(1000), charlie);
2146 env.close();
-
2147
-
2148 auto const vaultAccount = [&env](xrpl::Keylet keylet) -> Account {
-
2149 return Account("vault", env.le(keylet)->at(sfAccount));
-
2150 };
-
2151 auto const issuanceId = [&env](xrpl::Keylet keylet) -> MPTID { return env.le(keylet)->at(sfShareMPTID); };
-
2152
-
2153 test(env, owner, issuer, charlie, vaultAccount, vault, asset, issuanceId);
-
2154 };
-
2155
-
2156 testCase([&, this](
-
2157 Env& env,
-
2158 Account const& owner,
-
2159 Account const& issuer,
-
2160 Account const&,
-
2161 auto vaultAccount,
-
2162 Vault& vault,
-
2163 PrettyAsset const& asset,
-
2164 auto&&...) {
-
2165 testcase("IOU cannot use different asset");
-
2166 PrettyAsset const foo = issuer["FOO"];
-
2167
-
2168 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2169 env(tx);
-
2170 env.close();
-
2171
-
2172 {
-
2173 // Cannot create new trustline to a vault
-
2174 auto tx = [&, account = vaultAccount(keylet)]() {
-
2175 Json::Value jv;
-
2176 jv[jss::Account] = issuer.human();
-
2177 {
-
2178 auto& ja = jv[jss::LimitAmount] = foo(0).value().getJson(JsonOptions::none);
-
2179 ja[jss::issuer] = toBase58(account);
-
2180 }
-
2181 jv[jss::TransactionType] = jss::TrustSet;
-
2182 jv[jss::Flags] = tfSetFreeze;
-
2183 return jv;
-
2184 }();
-
2185 env(tx, ter{tecNO_PERMISSION});
-
2186 env.close();
-
2187 }
-
2188
-
2189 {
-
2190 auto tx = vault.deposit({.depositor = issuer, .id = keylet.key, .amount = foo(20)});
-
2191 env(tx, ter{tecWRONG_ASSET});
-
2192 env.close();
-
2193 }
-
2194
-
2195 {
-
2196 auto tx = vault.withdraw({.depositor = issuer, .id = keylet.key, .amount = foo(20)});
-
2197 env(tx, ter{tecWRONG_ASSET});
-
2198 env.close();
-
2199 }
-
2200
-
2201 env(vault.del({.owner = owner, .id = keylet.key}));
-
2202 env.close();
-
2203 });
-
2204
-
2205 testCase([&, this](
-
2206 Env& env,
-
2207 Account const& owner,
-
2208 Account const& issuer,
-
2209 Account const& charlie,
-
2210 auto vaultAccount,
-
2211 Vault& vault,
-
2212 PrettyAsset const& asset,
-
2213 auto issuanceId) {
-
2214 testcase("IOU frozen trust line to vault account");
-
2215
-
2216 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2217 env(tx);
-
2218 env.close();
-
2219
-
2220 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2221 env.close();
-
2222
-
2223 Asset const share = Asset(issuanceId(keylet));
+
2147 env(rate(issuer, args.transferRate));
+
2148 env.close();
+
2149
+
2150 auto const vaultAccount = [&env](xrpl::Keylet keylet) -> Account {
+
2151 return Account("vault", env.le(keylet)->at(sfAccount));
+
2152 };
+
2153 auto const issuanceId = [&env](xrpl::Keylet keylet) -> MPTID { return env.le(keylet)->at(sfShareMPTID); };
+
2154
+
2155 test(env, owner, issuer, charlie, vaultAccount, vault, asset, issuanceId);
+
2156 };
+
2157
+
2158 testCase([&, this](
+
2159 Env& env,
+
2160 Account const& owner,
+
2161 Account const& issuer,
+
2162 Account const&,
+
2163 auto vaultAccount,
+
2164 Vault& vault,
+
2165 PrettyAsset const& asset,
+
2166 auto&&...) {
+
2167 testcase("IOU cannot use different asset");
+
2168 PrettyAsset const foo = issuer["FOO"];
+
2169
+
2170 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2171 env(tx);
+
2172 env.close();
+
2173
+
2174 {
+
2175 // Cannot create new trustline to a vault
+
2176 auto tx = [&, account = vaultAccount(keylet)]() {
+
2177 Json::Value jv;
+
2178 jv[jss::Account] = issuer.human();
+
2179 {
+
2180 auto& ja = jv[jss::LimitAmount] = foo(0).value().getJson(JsonOptions::none);
+
2181 ja[jss::issuer] = toBase58(account);
+
2182 }
+
2183 jv[jss::TransactionType] = jss::TrustSet;
+
2184 jv[jss::Flags] = tfSetFreeze;
+
2185 return jv;
+
2186 }();
+
2187 env(tx, ter{tecNO_PERMISSION});
+
2188 env.close();
+
2189 }
+
2190
+
2191 {
+
2192 auto tx = vault.deposit({.depositor = issuer, .id = keylet.key, .amount = foo(20)});
+
2193 env(tx, ter{tecWRONG_ASSET});
+
2194 env.close();
+
2195 }
+
2196
+
2197 {
+
2198 auto tx = vault.withdraw({.depositor = issuer, .id = keylet.key, .amount = foo(20)});
+
2199 env(tx, ter{tecWRONG_ASSET});
+
2200 env.close();
+
2201 }
+
2202
+
2203 env(vault.del({.owner = owner, .id = keylet.key}));
+
2204 env.close();
+
2205 });
+
2206
+
2207 testCase([&, this](
+
2208 Env& env,
+
2209 Account const& owner,
+
2210 Account const& issuer,
+
2211 Account const& charlie,
+
2212 auto vaultAccount,
+
2213 Vault& vault,
+
2214 PrettyAsset const& asset,
+
2215 auto issuanceId) {
+
2216 testcase("IOU frozen trust line to vault account");
+
2217
+
2218 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2219 env(tx);
+
2220 env.close();
+
2221
+
2222 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2223 env.close();
2224
-
2225 // Freeze the trustline to the vault
-
2226 auto trustSet = [&, account = vaultAccount(keylet)]() {
-
2227 Json::Value jv;
-
2228 jv[jss::Account] = issuer.human();
-
2229 {
-
2230 auto& ja = jv[jss::LimitAmount] = asset(0).value().getJson(JsonOptions::none);
-
2231 ja[jss::issuer] = toBase58(account);
-
2232 }
-
2233 jv[jss::TransactionType] = jss::TrustSet;
-
2234 jv[jss::Flags] = tfSetFreeze;
-
2235 return jv;
-
2236 }();
-
2237 env(trustSet);
-
2238 env.close();
-
2239
-
2240 {
-
2241 // Note, the "frozen" state of the trust line to vault account
-
2242 // is reported as "locked" state of the vault shares, because
-
2243 // this state is attached to shares by means of the transitive
-
2244 // isFrozen.
-
2245 auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(80)});
-
2246 env(tx, ter{tecLOCKED});
-
2247 }
-
2248
-
2249 {
-
2250 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)});
-
2251 env(tx, ter{tecLOCKED});
-
2252
-
2253 // also when trying to withdraw to a 3rd party
-
2254 tx[sfDestination] = charlie.human();
-
2255 env(tx, ter{tecLOCKED});
-
2256 env.close();
-
2257 }
-
2258
-
2259 {
-
2260 // Clawback works, even when locked
-
2261 auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)});
-
2262 env(tx);
-
2263 env.close();
-
2264 }
-
2265
-
2266 // Clear the frozen state
-
2267 trustSet[jss::Flags] = tfClearFreeze;
-
2268 env(trustSet);
-
2269 env.close();
-
2270
-
2271 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(50'000'000)}));
+
2225 Asset const share = Asset(issuanceId(keylet));
+
2226
+
2227 // Freeze the trustline to the vault
+
2228 auto trustSet = [&, account = vaultAccount(keylet)]() {
+
2229 Json::Value jv;
+
2230 jv[jss::Account] = issuer.human();
+
2231 {
+
2232 auto& ja = jv[jss::LimitAmount] = asset(0).value().getJson(JsonOptions::none);
+
2233 ja[jss::issuer] = toBase58(account);
+
2234 }
+
2235 jv[jss::TransactionType] = jss::TrustSet;
+
2236 jv[jss::Flags] = tfSetFreeze;
+
2237 return jv;
+
2238 }();
+
2239 env(trustSet);
+
2240 env.close();
+
2241
+
2242 {
+
2243 // Note, the "frozen" state of the trust line to vault account
+
2244 // is reported as "locked" state of the vault shares, because
+
2245 // this state is attached to shares by means of the transitive
+
2246 // isFrozen.
+
2247 auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(80)});
+
2248 env(tx, ter{tecLOCKED});
+
2249 }
+
2250
+
2251 {
+
2252 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)});
+
2253 env(tx, ter{tecLOCKED});
+
2254
+
2255 // also when trying to withdraw to a 3rd party
+
2256 tx[sfDestination] = charlie.human();
+
2257 env(tx, ter{tecLOCKED});
+
2258 env.close();
+
2259 }
+
2260
+
2261 {
+
2262 // Clawback works, even when locked
+
2263 auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)});
+
2264 env(tx);
+
2265 env.close();
+
2266 }
+
2267
+
2268 // Clear the frozen state
+
2269 trustSet[jss::Flags] = tfClearFreeze;
+
2270 env(trustSet);
+
2271 env.close();
2272
-
2273 env(vault.del({.owner = owner, .id = keylet.key}));
-
2274 env.close();
-
2275 });
-
2276
-
2277 testCase(
-
2278 [&, this](
-
2279 Env& env,
-
2280 Account const& owner,
-
2281 Account const& issuer,
-
2282 Account const& charlie,
-
2283 auto vaultAccount,
-
2284 Vault& vault,
-
2285 PrettyAsset const& asset,
-
2286 auto issuanceId) {
-
2287 testcase("IOU transfer fees not applied");
-
2288
-
2289 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2290 env(tx);
-
2291 env.close();
-
2292
-
2293 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2294 env.close();
-
2295
-
2296 auto const issue = asset.raw().get<Issue>();
-
2297 Asset const share = Asset(issuanceId(keylet));
-
2298
-
2299 // transfer fees ignored on deposit
-
2300 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
-
2301 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(100));
-
2302
-
2303 {
-
2304 auto tx =
-
2305 vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)});
-
2306 env(tx);
-
2307 env.close();
-
2308 }
-
2309
-
2310 // transfer fees ignored on clawback
-
2311 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
-
2312 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50));
-
2313
-
2314 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(20'000'000)}));
+
2273 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(50'000'000)}));
+
2274
+
2275 env(vault.del({.owner = owner, .id = keylet.key}));
+
2276 env.close();
+
2277 });
+
2278
+
2279 testCase(
+
2280 [&, this](
+
2281 Env& env,
+
2282 Account const& owner,
+
2283 Account const& issuer,
+
2284 Account const& charlie,
+
2285 auto vaultAccount,
+
2286 Vault& vault,
+
2287 PrettyAsset const& asset,
+
2288 auto issuanceId) {
+
2289 testcase("IOU transfer fees not applied");
+
2290
+
2291 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2292 env(tx);
+
2293 env.close();
+
2294
+
2295 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2296 env.close();
+
2297
+
2298 auto const issue = asset.raw().get<Issue>();
+
2299 Asset const share = Asset(issuanceId(keylet));
+
2300
+
2301 // transfer fees ignored on deposit
+
2302 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
+
2303 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(100));
+
2304
+
2305 {
+
2306 auto tx =
+
2307 vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(50)});
+
2308 env(tx);
+
2309 env.close();
+
2310 }
+
2311
+
2312 // transfer fees ignored on clawback
+
2313 BEAST_EXPECT(env.balance(owner, issue) == asset(100));
+
2314 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50));
2315
-
2316 // transfer fees ignored on withdraw
-
2317 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
-
2318 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30));
-
2319
-
2320 {
-
2321 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(30'000'000)});
-
2322 tx[sfDestination] = charlie.human();
-
2323 env(tx);
-
2324 }
-
2325
-
2326 // transfer fees ignored on withdraw to 3rd party
-
2327 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
-
2328 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
-
2329 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(0));
-
2330
-
2331 env(vault.del({.owner = owner, .id = keylet.key}));
-
2332 env.close();
-
2333 },
-
2334 CaseArgs{.transferRate = 1.25});
-
2335
-
2336 testCase([&, this](
-
2337 Env& env,
-
2338 Account const& owner,
-
2339 Account const& issuer,
-
2340 Account const& charlie,
-
2341 auto,
-
2342 Vault& vault,
-
2343 PrettyAsset const& asset,
-
2344 auto&&...) {
-
2345 testcase("IOU frozen trust line to depositor");
-
2346
-
2347 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2348 env(tx);
-
2349 env.close();
-
2350
-
2351 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2352 env.close();
-
2353
-
2354 // Withdraw to 3rd party works
-
2355 auto const withdrawToCharlie = [&](xrpl::Keylet keylet) {
-
2356 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2357 tx[sfDestination] = charlie.human();
-
2358 return tx;
-
2359 }(keylet);
-
2360 env(withdrawToCharlie);
-
2361
-
2362 // Freeze the owner
-
2363 env(trust(issuer, asset(0), owner, tfSetFreeze));
-
2364 env.close();
-
2365
-
2366 // Cannot withdraw
-
2367 auto const withdraw = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2368 env(withdraw, ter{tecFROZEN});
-
2369
-
2370 // Cannot withdraw to 3rd party
-
2371 env(withdrawToCharlie, ter{tecLOCKED});
-
2372 env.close();
-
2373
-
2374 {
-
2375 // Cannot deposit some more
-
2376 auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2377 env(tx, ter{tecFROZEN});
-
2378 }
-
2379
-
2380 {
-
2381 // Clawback still works
-
2382 auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)});
-
2383 env(tx);
-
2384 env.close();
-
2385 }
-
2386
-
2387 env(vault.del({.owner = owner, .id = keylet.key}));
-
2388 env.close();
-
2389 });
-
2390
-
2391 testCase([&, this](
-
2392 Env& env,
-
2393 Account const& owner,
-
2394 Account const& issuer,
-
2395 Account const& charlie,
-
2396 auto,
-
2397 Vault& vault,
-
2398 PrettyAsset const& asset,
-
2399 auto&&...) {
-
2400 testcase("IOU no trust line to 3rd party");
-
2401
-
2402 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2403 env(tx);
-
2404 env.close();
-
2405
-
2406 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2407 env.close();
-
2408
-
2409 Account const erin{"erin"};
-
2410 env.fund(XRP(1000), erin);
-
2411 env.close();
-
2412
-
2413 // Withdraw to 3rd party without trust line
-
2414 auto const tx1 = [&](xrpl::Keylet keylet) {
-
2415 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2416 tx[sfDestination] = erin.human();
-
2417 return tx;
-
2418 }(keylet);
-
2419 env(tx1, ter{tecNO_LINE});
-
2420 });
-
2421
-
2422 testCase([&, this](
-
2423 Env& env,
-
2424 Account const& owner,
-
2425 Account const& issuer,
-
2426 Account const& charlie,
-
2427 auto,
-
2428 Vault& vault,
-
2429 PrettyAsset const& asset,
-
2430 auto&&...) {
-
2431 testcase("IOU no trust line to depositor");
-
2432
-
2433 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2434 env(tx);
-
2435 env.close();
-
2436
-
2437 // reset limit, so deposit of all funds will delete the trust line
-
2438 env.trust(asset(0), owner);
-
2439 env.close();
-
2440
-
2441 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
-
2442 env.close();
-
2443
-
2444 auto trustline = env.le(keylet::line(owner, asset.raw().get<Issue>()));
-
2445 BEAST_EXPECT(trustline == nullptr);
-
2446
-
2447 // Withdraw without trust line, will succeed
-
2448 auto const tx1 = [&](xrpl::Keylet keylet) {
-
2449 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2450 return tx;
-
2451 }(keylet);
-
2452 env(tx1);
-
2453 });
-
2454
-
2455 testCase(
-
2456 [&, this](
-
2457 Env& env,
-
2458 Account const& owner,
-
2459 Account const& issuer,
-
2460 Account const& charlie,
-
2461 auto vaultAccount,
-
2462 Vault& vault,
-
2463 PrettyAsset const& asset,
-
2464 std::function<MPTID(xrpl::Keylet)> issuanceId) {
-
2465 testcase("IOU non-transferable");
-
2466
-
2467 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2468 tx[sfScale] = 0;
-
2469 env(tx);
-
2470 env.close();
-
2471
-
2472 // Turn on noripple on the pseudo account's trust line.
-
2473 // Charlie's is already set.
-
2474 env(trust(issuer, vaultAccount(keylet)["IOU"], tfSetNoRipple), THISLINE);
-
2475
-
2476 {
-
2477 // Charlie cannot deposit
-
2478 auto tx = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(100)});
-
2479 env(tx, ter{terNO_RIPPLE}, THISLINE);
-
2480 env.close();
-
2481 }
-
2482
-
2483 {
-
2484 PrettyAsset shares = issuanceId(keylet);
-
2485 auto tx1 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)});
-
2486 env(tx1, THISLINE);
-
2487 env.close();
-
2488
-
2489 // Charlie cannot receive funds
-
2490 auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = shares(100)});
-
2491 tx2[sfDestination] = charlie.human();
-
2492 env(tx2, ter{terNO_RIPPLE}, THISLINE);
-
2493 env.close();
-
2494
-
2495 {
-
2496 // Create MPToken for shares held by Charlie
- -
2498 tx[sfAccount] = charlie.human();
-
2499 tx[sfMPTokenIssuanceID] = to_string(shares.raw().get<MPTIssue>().getMptID());
-
2500 tx[sfTransactionType] = jss::MPTokenAuthorize;
-
2501 env(tx);
-
2502 env.close();
-
2503 }
-
2504 env(pay(owner, charlie, shares(100)), THISLINE);
-
2505 env.close();
-
2506
-
2507 // Charlie cannot withdraw
-
2508 auto tx3 = vault.withdraw({.depositor = charlie, .id = keylet.key, .amount = shares(100)});
-
2509 env(tx3, ter{terNO_RIPPLE});
-
2510 env.close();
-
2511
-
2512 env(pay(charlie, owner, shares(100)), THISLINE);
-
2513 env.close();
-
2514 }
-
2515
-
2516 tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)});
-
2517 env(tx, THISLINE);
-
2518 env.close();
-
2519
-
2520 // Delete vault with zero balance
-
2521 env(vault.del({.owner = owner, .id = keylet.key}), THISLINE);
-
2522 },
-
2523 {.charlieRipple = false});
-
2524
-
2525 testCase(
-
2526 [&, this](
-
2527 Env& env,
-
2528 Account const& owner,
-
2529 Account const& issuer,
-
2530 Account const& charlie,
-
2531 auto const& vaultAccount,
-
2532 Vault& vault,
-
2533 PrettyAsset const& asset,
-
2534 auto&&...) {
-
2535 testcase("IOU calculation rounding");
-
2536
-
2537 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2538 tx[sfScale] = 1;
-
2539 env(tx);
-
2540 env.close();
-
2541
-
2542 auto const startingOwnerBalance = env.balance(owner, asset);
-
2543 BEAST_EXPECT((startingOwnerBalance.value() == STAmount{asset, 11875, -2}));
-
2544
-
2545 // This operation (first deposit 100, then 3.75 x 5) is known to
-
2546 // have triggered calculation rounding errors in Number
-
2547 // (addition and division), causing the last deposit to be
-
2548 // blocked by Vault invariants.
-
2549 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2550
-
2551 auto const tx1 =
-
2552 vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(Number(375, -2))});
-
2553 for (auto i = 0; i < 5; ++i)
-
2554 {
-
2555 env(tx1);
-
2556 }
-
2557 env.close();
-
2558
-
2559 {
-
2560 STAmount const xfer{asset, 1185, -1};
-
2561 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value() - xfer);
-
2562 BEAST_EXPECT(env.balance(vaultAccount(keylet), asset) == xfer);
-
2563
-
2564 auto const vault = env.le(keylet);
-
2565 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
-
2566 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
-
2567 }
-
2568
-
2569 // Total vault balance should be 118.5 IOU. Withdraw and delete
-
2570 // the vault to verify this exact amount was deposited and the
-
2571 // owner has matching shares
-
2572 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(Number(1000 + 37 * 5, -1))}));
-
2573
-
2574 {
-
2575 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value());
-
2576 BEAST_EXPECT(env.balance(vaultAccount(keylet), asset) == beast::zero);
-
2577 auto const vault = env.le(keylet);
-
2578 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::zero);
-
2579 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::zero);
-
2580 }
-
2581
-
2582 env(vault.del({.owner = owner, .id = keylet.key}));
-
2583 env.close();
-
2584 },
-
2585 {.initialIOU = Number(11875, -2)});
-
2586
-
2587 auto const [acctReserve, incReserve] = [this]() -> std::pair<int, int> {
-
2588 Env env{*this, testable_amendments()};
-
2589 return {
-
2590 env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(),
-
2591 env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()};
-
2592 }();
-
2593
-
2594 testCase(
-
2595 [&, this](
-
2596 Env& env,
-
2597 Account const& owner,
-
2598 Account const& issuer,
-
2599 Account const& charlie,
-
2600 auto,
-
2601 Vault& vault,
-
2602 PrettyAsset const& asset,
-
2603 auto&&...) {
-
2604 testcase("IOU no trust line to depositor no reserve");
-
2605 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2606 env(tx);
-
2607 env.close();
-
2608
-
2609 // reset limit, so deposit of all funds will delete the trust
-
2610 // line
-
2611 env.trust(asset(0), owner);
-
2612 env.close();
-
2613
-
2614 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
-
2615 env.close();
-
2616
-
2617 auto trustline = env.le(keylet::line(owner, asset.raw().get<Issue>()));
-
2618 BEAST_EXPECT(trustline == nullptr);
-
2619
-
2620 env(ticket::create(owner, 1));
-
2621 env.close();
-
2622
-
2623 // Fail because not enough reserve to create trust line
-
2624 tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2625 env(tx, ter{tecNO_LINE_INSUF_RESERVE});
-
2626 env.close();
-
2627
-
2628 env(pay(charlie, owner, XRP(incReserve)));
-
2629 env.close();
-
2630
-
2631 // Withdraw can now create trust line, will succeed
-
2632 env(tx);
-
2633 env.close();
-
2634 },
-
2635 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
-
2636
-
2637 testCase(
-
2638 [&, this](
-
2639 Env& env,
-
2640 Account const& owner,
-
2641 Account const& issuer,
-
2642 Account const& charlie,
-
2643 auto,
-
2644 Vault& vault,
-
2645 PrettyAsset const& asset,
-
2646 auto&&...) {
-
2647 testcase("IOU no reserve for share MPToken");
-
2648 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2649 env(tx);
-
2650 env.close();
-
2651
-
2652 env(pay(owner, charlie, asset(100)));
-
2653 env.close();
-
2654
-
2655 env(ticket::create(charlie, 3));
-
2656 env.close();
-
2657
-
2658 // Fail because not enough reserve to create MPToken for shares
-
2659 tx = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(100)});
-
2660 env(tx, ter{tecINSUFFICIENT_RESERVE});
-
2661 env.close();
-
2662
-
2663 env(pay(issuer, charlie, XRP(incReserve)));
-
2664 env.close();
-
2665
-
2666 // Deposit can now create MPToken, will succeed
-
2667 env(tx);
-
2668 env.close();
-
2669 },
-
2670 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
-
2671
-
2672 testCase([&, this](
-
2673 Env& env,
-
2674 Account const& owner,
-
2675 Account const& issuer,
-
2676 Account const& charlie,
-
2677 auto,
-
2678 Vault& vault,
-
2679 PrettyAsset const& asset,
-
2680 auto&&...) {
-
2681 testcase("IOU frozen trust line to 3rd party");
-
2682
-
2683 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2684 env(tx);
-
2685 env.close();
-
2686
-
2687 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2688 env.close();
-
2689
-
2690 // Withdraw to 3rd party works
-
2691 auto const withdrawToCharlie = [&](xrpl::Keylet keylet) {
-
2692 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2693 tx[sfDestination] = charlie.human();
-
2694 return tx;
-
2695 }(keylet);
-
2696 env(withdrawToCharlie);
-
2697
-
2698 // Freeze the 3rd party
-
2699 env(trust(issuer, asset(0), charlie, tfSetFreeze));
-
2700 env.close();
-
2701
-
2702 // Can withdraw
-
2703 auto const withdraw = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2704 env(withdraw);
-
2705 env.close();
-
2706
-
2707 // Cannot withdraw to 3rd party
-
2708 env(withdrawToCharlie, ter{tecFROZEN});
-
2709 env.close();
-
2710
-
2711 env(vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}));
-
2712 env.close();
-
2713
-
2714 env(vault.del({.owner = owner, .id = keylet.key}));
-
2715 env.close();
-
2716 });
-
2717
-
2718 testCase([&, this](
-
2719 Env& env,
-
2720 Account const& owner,
-
2721 Account const& issuer,
-
2722 Account const& charlie,
-
2723 auto,
-
2724 Vault& vault,
-
2725 PrettyAsset const& asset,
-
2726 auto&&...) {
-
2727 testcase("IOU global freeze");
-
2728
-
2729 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
2730 env(tx);
-
2731 env.close();
-
2732
-
2733 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
-
2734 env.close();
-
2735
-
2736 env(fset(issuer, asfGlobalFreeze));
-
2737 env.close();
-
2738
-
2739 {
-
2740 // Cannot withdraw
-
2741 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2742 env(tx, ter{tecFROZEN});
-
2743
-
2744 // Cannot withdraw to 3rd party
-
2745 tx[sfDestination] = charlie.human();
-
2746 env(tx, ter{tecFROZEN});
-
2747 env.close();
-
2748
-
2749 // Cannot deposit some more
-
2750 tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)});
-
2751
-
2752 env(tx, ter{tecFROZEN});
-
2753 }
-
2754
-
2755 // Clawback is permitted
-
2756 env(vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}));
-
2757 env.close();
-
2758
-
2759 env(vault.del({.owner = owner, .id = keylet.key}));
-
2760 env.close();
-
2761 });
-
2762 }
+
2316 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(20'000'000)}));
+
2317
+
2318 // transfer fees ignored on withdraw
+
2319 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
+
2320 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30));
+
2321
+
2322 {
+
2323 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = share(30'000'000)});
+
2324 tx[sfDestination] = charlie.human();
+
2325 env(tx);
+
2326 }
+
2327
+
2328 // transfer fees ignored on withdraw to 3rd party
+
2329 BEAST_EXPECT(env.balance(owner, issue) == asset(120));
+
2330 BEAST_EXPECT(env.balance(charlie, issue) == asset(30));
+
2331 BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(0));
+
2332
+
2333 env(vault.del({.owner = owner, .id = keylet.key}));
+
2334 env.close();
+
2335 },
+
2336 CaseArgs{.transferRate = 1.25});
+
2337
+
2338 testCase([&, this](
+
2339 Env& env,
+
2340 Account const& owner,
+
2341 Account const& issuer,
+
2342 Account const& charlie,
+
2343 auto,
+
2344 Vault& vault,
+
2345 PrettyAsset const& asset,
+
2346 auto&&...) {
+
2347 testcase("IOU frozen trust line to depositor");
+
2348
+
2349 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2350 env(tx);
+
2351 env.close();
+
2352
+
2353 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2354 env.close();
+
2355
+
2356 // Withdraw to 3rd party works
+
2357 auto const withdrawToCharlie = [&](xrpl::Keylet keylet) {
+
2358 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2359 tx[sfDestination] = charlie.human();
+
2360 return tx;
+
2361 }(keylet);
+
2362 env(withdrawToCharlie);
+
2363
+
2364 // Freeze the owner
+
2365 env(trust(issuer, asset(0), owner, tfSetFreeze));
+
2366 env.close();
+
2367
+
2368 // Cannot withdraw
+
2369 auto const withdraw = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2370 env(withdraw, ter{tecFROZEN});
+
2371
+
2372 // Cannot withdraw to 3rd party
+
2373 env(withdrawToCharlie, ter{tecLOCKED});
+
2374 env.close();
+
2375
+
2376 {
+
2377 // Cannot deposit some more
+
2378 auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2379 env(tx, ter{tecFROZEN});
+
2380 }
+
2381
+
2382 {
+
2383 // Clawback still works
+
2384 auto tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)});
+
2385 env(tx);
+
2386 env.close();
+
2387 }
+
2388
+
2389 env(vault.del({.owner = owner, .id = keylet.key}));
+
2390 env.close();
+
2391 });
+
2392
+
2393 testCase([&, this](
+
2394 Env& env,
+
2395 Account const& owner,
+
2396 Account const& issuer,
+
2397 Account const& charlie,
+
2398 auto,
+
2399 Vault& vault,
+
2400 PrettyAsset const& asset,
+
2401 auto&&...) {
+
2402 testcase("IOU no trust line to 3rd party");
+
2403
+
2404 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2405 env(tx);
+
2406 env.close();
+
2407
+
2408 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2409 env.close();
+
2410
+
2411 Account const erin{"erin"};
+
2412 env.fund(XRP(1000), erin);
+
2413 env.close();
+
2414
+
2415 // Withdraw to 3rd party without trust line
+
2416 auto const tx1 = [&](xrpl::Keylet keylet) {
+
2417 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2418 tx[sfDestination] = erin.human();
+
2419 return tx;
+
2420 }(keylet);
+
2421 env(tx1, ter{tecNO_LINE});
+
2422 });
+
2423
+
2424 testCase([&, this](
+
2425 Env& env,
+
2426 Account const& owner,
+
2427 Account const& issuer,
+
2428 Account const& charlie,
+
2429 auto,
+
2430 Vault& vault,
+
2431 PrettyAsset const& asset,
+
2432 auto&&...) {
+
2433 testcase("IOU no trust line to depositor");
+
2434
+
2435 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2436 env(tx);
+
2437 env.close();
+
2438
+
2439 // reset limit, so deposit of all funds will delete the trust line
+
2440 env.trust(asset(0), owner);
+
2441 env.close();
+
2442
+
2443 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
+
2444 env.close();
+
2445
+
2446 auto trustline = env.le(keylet::line(owner, asset.raw().get<Issue>()));
+
2447 BEAST_EXPECT(trustline == nullptr);
+
2448
+
2449 // Withdraw without trust line, will succeed
+
2450 auto const tx1 = [&](xrpl::Keylet keylet) {
+
2451 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2452 return tx;
+
2453 }(keylet);
+
2454 env(tx1);
+
2455 });
+
2456
+
2457 testCase(
+
2458 [&, this](
+
2459 Env& env,
+
2460 Account const& owner,
+
2461 Account const& issuer,
+
2462 Account const& charlie,
+
2463 auto vaultAccount,
+
2464 Vault& vault,
+
2465 PrettyAsset const& asset,
+
2466 std::function<MPTID(xrpl::Keylet)> issuanceId) {
+
2467 testcase("IOU non-transferable");
+
2468
+
2469 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2470 tx[sfScale] = 0;
+
2471 env(tx);
+
2472 env.close();
+
2473
+
2474 // Turn on noripple on the pseudo account's trust line.
+
2475 // Charlie's is already set.
+
2476 env(trust(issuer, vaultAccount(keylet)["IOU"], tfSetNoRipple), THISLINE);
+
2477
+
2478 {
+
2479 // Charlie cannot deposit
+
2480 auto tx = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(100)});
+
2481 env(tx, ter{terNO_RIPPLE}, THISLINE);
+
2482 env.close();
+
2483 }
+
2484
+
2485 {
+
2486 PrettyAsset shares = issuanceId(keylet);
+
2487 auto tx1 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)});
+
2488 env(tx1, THISLINE);
+
2489 env.close();
+
2490
+
2491 // Charlie cannot receive funds
+
2492 auto tx2 = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = shares(100)});
+
2493 tx2[sfDestination] = charlie.human();
+
2494 env(tx2, ter{terNO_RIPPLE}, THISLINE);
+
2495 env.close();
+
2496
+
2497 {
+
2498 // Create MPToken for shares held by Charlie
+ +
2500 tx[sfAccount] = charlie.human();
+
2501 tx[sfMPTokenIssuanceID] = to_string(shares.raw().get<MPTIssue>().getMptID());
+
2502 tx[sfTransactionType] = jss::MPTokenAuthorize;
+
2503 env(tx);
+
2504 env.close();
+
2505 }
+
2506 env(pay(owner, charlie, shares(100)), THISLINE);
+
2507 env.close();
+
2508
+
2509 // Charlie cannot withdraw
+
2510 auto tx3 = vault.withdraw({.depositor = charlie, .id = keylet.key, .amount = shares(100)});
+
2511 env(tx3, ter{terNO_RIPPLE});
+
2512 env.close();
+
2513
+
2514 env(pay(charlie, owner, shares(100)), THISLINE);
+
2515 env.close();
+
2516 }
+
2517
+
2518 tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(100)});
+
2519 env(tx, THISLINE);
+
2520 env.close();
+
2521
+
2522 // Delete vault with zero balance
+
2523 env(vault.del({.owner = owner, .id = keylet.key}), THISLINE);
+
2524 },
+
2525 {.charlieRipple = false});
+
2526
+
2527 testCase(
+
2528 [&, this](
+
2529 Env& env,
+
2530 Account const& owner,
+
2531 Account const& issuer,
+
2532 Account const& charlie,
+
2533 auto const& vaultAccount,
+
2534 Vault& vault,
+
2535 PrettyAsset const& asset,
+
2536 auto&&...) {
+
2537 testcase("IOU calculation rounding");
+
2538
+
2539 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2540 tx[sfScale] = 1;
+
2541 env(tx);
+
2542 env.close();
+
2543
+
2544 auto const startingOwnerBalance = env.balance(owner, asset);
+
2545 BEAST_EXPECT((startingOwnerBalance.value() == STAmount{asset, 11875, -2}));
+
2546
+
2547 // This operation (first deposit 100, then 3.75 x 5) is known to
+
2548 // have triggered calculation rounding errors in Number
+
2549 // (addition and division), causing the last deposit to be
+
2550 // blocked by Vault invariants.
+
2551 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2552
+
2553 auto const tx1 =
+
2554 vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(Number(375, -2))});
+
2555 for (auto i = 0; i < 5; ++i)
+
2556 {
+
2557 env(tx1);
+
2558 }
+
2559 env.close();
+
2560
+
2561 {
+
2562 STAmount const xfer{asset, 1185, -1};
+
2563 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value() - xfer);
+
2564 BEAST_EXPECT(env.balance(vaultAccount(keylet), asset) == xfer);
+
2565
+
2566 auto const vault = env.le(keylet);
+
2567 BEAST_EXPECT(vault->at(sfAssetsAvailable) == xfer);
+
2568 BEAST_EXPECT(vault->at(sfAssetsTotal) == xfer);
+
2569 }
+
2570
+
2571 // Total vault balance should be 118.5 IOU. Withdraw and delete
+
2572 // the vault to verify this exact amount was deposited and the
+
2573 // owner has matching shares
+
2574 env(vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(Number(1000 + 37 * 5, -1))}));
+
2575
+
2576 {
+
2577 BEAST_EXPECT(env.balance(owner, asset) == startingOwnerBalance.value());
+
2578 BEAST_EXPECT(env.balance(vaultAccount(keylet), asset) == beast::zero);
+
2579 auto const vault = env.le(keylet);
+
2580 BEAST_EXPECT(vault->at(sfAssetsAvailable) == beast::zero);
+
2581 BEAST_EXPECT(vault->at(sfAssetsTotal) == beast::zero);
+
2582 }
+
2583
+
2584 env(vault.del({.owner = owner, .id = keylet.key}));
+
2585 env.close();
+
2586 },
+
2587 {.initialIOU = Number(11875, -2)});
+
2588
+
2589 auto const [acctReserve, incReserve] = [this]() -> std::pair<int, int> {
+
2590 Env env{*this, testable_amendments()};
+
2591 return {
+
2592 env.current()->fees().accountReserve(0).drops() / DROPS_PER_XRP.drops(),
+
2593 env.current()->fees().increment.drops() / DROPS_PER_XRP.drops()};
+
2594 }();
+
2595
+
2596 testCase(
+
2597 [&, this](
+
2598 Env& env,
+
2599 Account const& owner,
+
2600 Account const& issuer,
+
2601 Account const& charlie,
+
2602 auto,
+
2603 Vault& vault,
+
2604 PrettyAsset const& asset,
+
2605 auto&&...) {
+
2606 testcase("IOU no trust line to depositor no reserve");
+
2607 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2608 env(tx);
+
2609 env.close();
+
2610
+
2611 // reset limit, so deposit of all funds will delete the trust
+
2612 // line
+
2613 env.trust(asset(0), owner);
+
2614 env.close();
+
2615
+
2616 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(200)}));
+
2617 env.close();
+
2618
+
2619 auto trustline = env.le(keylet::line(owner, asset.raw().get<Issue>()));
+
2620 BEAST_EXPECT(trustline == nullptr);
+
2621
+
2622 env(ticket::create(owner, 1));
+
2623 env.close();
+
2624
+
2625 // Fail because not enough reserve to create trust line
+
2626 tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2627 env(tx, ter{tecNO_LINE_INSUF_RESERVE});
+
2628 env.close();
+
2629
+
2630 env(pay(charlie, owner, XRP(incReserve)));
+
2631 env.close();
+
2632
+
2633 // Withdraw can now create trust line, will succeed
+
2634 env(tx);
+
2635 env.close();
+
2636 },
+
2637 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
+
2638
+
2639 testCase(
+
2640 [&, this](
+
2641 Env& env,
+
2642 Account const& owner,
+
2643 Account const& issuer,
+
2644 Account const& charlie,
+
2645 auto,
+
2646 Vault& vault,
+
2647 PrettyAsset const& asset,
+
2648 auto&&...) {
+
2649 testcase("IOU no reserve for share MPToken");
+
2650 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2651 env(tx);
+
2652 env.close();
+
2653
+
2654 env(pay(owner, charlie, asset(100)));
+
2655 env.close();
+
2656
+
2657 env(ticket::create(charlie, 3));
+
2658 env.close();
+
2659
+
2660 // Fail because not enough reserve to create MPToken for shares
+
2661 tx = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(100)});
+
2662 env(tx, ter{tecINSUFFICIENT_RESERVE});
+
2663 env.close();
+
2664
+
2665 env(pay(issuer, charlie, XRP(incReserve)));
+
2666 env.close();
+
2667
+
2668 // Deposit can now create MPToken, will succeed
+
2669 env(tx);
+
2670 env.close();
+
2671 },
+
2672 CaseArgs{.initialXRP = acctReserve + incReserve * 4 + 1});
+
2673
+
2674 testCase([&, this](
+
2675 Env& env,
+
2676 Account const& owner,
+
2677 Account const& issuer,
+
2678 Account const& charlie,
+
2679 auto,
+
2680 Vault& vault,
+
2681 PrettyAsset const& asset,
+
2682 auto&&...) {
+
2683 testcase("IOU frozen trust line to 3rd party");
+
2684
+
2685 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2686 env(tx);
+
2687 env.close();
+
2688
+
2689 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2690 env.close();
+
2691
+
2692 // Withdraw to 3rd party works
+
2693 auto const withdrawToCharlie = [&](xrpl::Keylet keylet) {
+
2694 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2695 tx[sfDestination] = charlie.human();
+
2696 return tx;
+
2697 }(keylet);
+
2698 env(withdrawToCharlie);
+
2699
+
2700 // Freeze the 3rd party
+
2701 env(trust(issuer, asset(0), charlie, tfSetFreeze));
+
2702 env.close();
+
2703
+
2704 // Can withdraw
+
2705 auto const withdraw = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2706 env(withdraw);
+
2707 env.close();
+
2708
+
2709 // Cannot withdraw to 3rd party
+
2710 env(withdrawToCharlie, ter{tecFROZEN});
+
2711 env.close();
+
2712
+
2713 env(vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}));
+
2714 env.close();
+
2715
+
2716 env(vault.del({.owner = owner, .id = keylet.key}));
+
2717 env.close();
+
2718 });
+
2719
+
2720 testCase([&, this](
+
2721 Env& env,
+
2722 Account const& owner,
+
2723 Account const& issuer,
+
2724 Account const& charlie,
+
2725 auto,
+
2726 Vault& vault,
+
2727 PrettyAsset const& asset,
+
2728 auto&&...) {
+
2729 testcase("IOU global freeze");
+
2730
+
2731 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
2732 env(tx);
+
2733 env.close();
+
2734
+
2735 env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(100)}));
+
2736 env.close();
+
2737
+
2738 env(fset(issuer, asfGlobalFreeze));
+
2739 env.close();
+
2740
+
2741 {
+
2742 // Cannot withdraw
+
2743 auto tx = vault.withdraw({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2744 env(tx, ter{tecFROZEN});
+
2745
+
2746 // Cannot withdraw to 3rd party
+
2747 tx[sfDestination] = charlie.human();
+
2748 env(tx, ter{tecFROZEN});
+
2749 env.close();
+
2750
+
2751 // Cannot deposit some more
+
2752 tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(10)});
+
2753
+
2754 env(tx, ter{tecFROZEN});
+
2755 }
+
2756
+
2757 // Clawback is permitted
+
2758 env(vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)}));
+
2759 env.close();
+
2760
+
2761 env(vault.del({.owner = owner, .id = keylet.key}));
+
2762 env.close();
+
2763 });
+
2764 }
-
2763
-
2764 void
-
- -
2766 {
-
2767 using namespace test::jtx;
-
2768
-
2769 testcase("private vault");
+
2765
+
2766 void
+
+ +
2768 {
+
2769 using namespace test::jtx;
2770
-
2771 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
2772 Account issuer{"issuer"};
-
2773 Account owner{"owner"};
-
2774 Account depositor{"depositor"};
-
2775 Account charlie{"charlie"};
-
2776 Account pdOwner{"pdOwner"};
-
2777 Account credIssuer1{"credIssuer1"};
-
2778 Account credIssuer2{"credIssuer2"};
-
2779 std::string const credType = "credential";
-
2780 Vault vault{env};
-
2781 env.fund(XRP(1000), issuer, owner, depositor, charlie, pdOwner, credIssuer1, credIssuer2);
-
2782 env.close();
-
2783 env(fset(issuer, asfAllowTrustLineClawback));
+
2771 testcase("private vault");
+
2772
+
2773 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
2774 Account issuer{"issuer"};
+
2775 Account owner{"owner"};
+
2776 Account depositor{"depositor"};
+
2777 Account charlie{"charlie"};
+
2778 Account pdOwner{"pdOwner"};
+
2779 Account credIssuer1{"credIssuer1"};
+
2780 Account credIssuer2{"credIssuer2"};
+
2781 std::string const credType = "credential";
+
2782 Vault vault{env};
+
2783 env.fund(XRP(1000), issuer, owner, depositor, charlie, pdOwner, credIssuer1, credIssuer2);
2784 env.close();
-
2785 env.require(flags(issuer, asfAllowTrustLineClawback));
-
2786
-
2787 PrettyAsset asset = issuer["IOU"];
-
2788 env.trust(asset(1000), owner);
-
2789 env(pay(issuer, owner, asset(500)));
-
2790 env.trust(asset(1000), depositor);
-
2791 env(pay(issuer, depositor, asset(500)));
-
2792 env.trust(asset(1000), charlie);
-
2793 env(pay(issuer, charlie, asset(5)));
-
2794 env.close();
-
2795
-
2796 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
-
2797 env(tx);
-
2798 env.close();
-
2799 BEAST_EXPECT(env.le(keylet));
-
2800
-
2801 {
-
2802 testcase("private vault owner can deposit");
-
2803 auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(50)});
-
2804 env(tx);
-
2805 }
-
2806
-
2807 {
-
2808 testcase("private vault depositor not authorized yet");
-
2809 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
-
2810 env(tx, ter{tecNO_AUTH});
-
2811 }
-
2812
-
2813 {
-
2814 testcase("private vault cannot set non-existing domain");
-
2815 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
2816 tx[sfDomainID] = to_string(base_uint<256>(42ul));
-
2817 env(tx, ter{tecOBJECT_NOT_FOUND});
-
2818 }
-
2819
-
2820 {
-
2821 testcase("private vault set domainId");
-
2822
-
2823 {
-
2824 pdomain::Credentials const credentials1{{.issuer = credIssuer1, .credType = credType}};
-
2825
-
2826 env(pdomain::setTx(pdOwner, credentials1));
-
2827 auto const domainId1 = [&]() {
-
2828 auto tx = env.tx()->getJson(JsonOptions::none);
-
2829 return pdomain::getNewDomain(env.meta());
-
2830 }();
-
2831
-
2832 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
2833 tx[sfDomainID] = to_string(domainId1);
-
2834 env(tx);
-
2835 env.close();
-
2836
-
2837 // Update domain second time, should be harmless
-
2838 env(tx);
-
2839 env.close();
-
2840 }
-
2841
-
2842 {
-
2843 pdomain::Credentials const credentials{
-
2844 {.issuer = credIssuer1, .credType = credType}, {.issuer = credIssuer2, .credType = credType}};
-
2845
-
2846 env(pdomain::setTx(pdOwner, credentials));
-
2847 auto const domainId = [&]() {
-
2848 auto tx = env.tx()->getJson(JsonOptions::none);
-
2849 return pdomain::getNewDomain(env.meta());
-
2850 }();
-
2851
-
2852 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
2853 tx[sfDomainID] = to_string(domainId);
-
2854 env(tx);
-
2855 env.close();
-
2856
-
2857 // Should be idempotent
-
2858 tx = vault.set({.owner = owner, .id = keylet.key});
-
2859 tx[sfDomainID] = to_string(domainId);
-
2860 env(tx);
-
2861 env.close();
-
2862 }
-
2863 }
-
2864
-
2865 {
-
2866 testcase("private vault depositor still not authorized");
-
2867 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
-
2868 env(tx, ter{tecNO_AUTH});
-
2869 env.close();
-
2870 }
-
2871
-
2872 auto const credKeylet = credentials::keylet(depositor, credIssuer1, credType);
-
2873 {
-
2874 testcase("private vault depositor now authorized");
-
2875 env(credentials::create(depositor, credIssuer1, credType));
-
2876 env(credentials::accept(depositor, credIssuer1, credType));
-
2877 env(credentials::create(charlie, credIssuer1, credType));
-
2878 // charlie's credential not accepted
-
2879 env.close();
-
2880 auto credSle = env.le(credKeylet);
-
2881 BEAST_EXPECT(credSle != nullptr);
-
2882
-
2883 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
-
2884 env(tx);
-
2885 env.close();
-
2886
-
2887 tx = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(50)});
-
2888 env(tx, ter{tecNO_AUTH});
-
2889 env.close();
-
2890 }
-
2891
-
2892 {
-
2893 testcase("private vault depositor lost authorization");
-
2894 env(credentials::deleteCred(credIssuer1, depositor, credIssuer1, credType));
-
2895 env(credentials::deleteCred(credIssuer1, charlie, credIssuer1, credType));
-
2896 env.close();
-
2897 auto credSle = env.le(credKeylet);
-
2898 BEAST_EXPECT(credSle == nullptr);
-
2899
-
2900 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
-
2901 env(tx, ter{tecNO_AUTH});
-
2902 env.close();
-
2903 }
-
2904
-
2905 auto const shares = [&env, keylet = keylet, this]() -> Asset {
-
2906 auto const vault = env.le(keylet);
-
2907 BEAST_EXPECT(vault != nullptr);
-
2908 return MPTIssue(vault->at(sfShareMPTID));
-
2909 }();
-
2910
-
2911 {
-
2912 testcase("private vault expired authorization");
-
2913 uint32_t const closeTime = env.current()->header().parentCloseTime.time_since_epoch().count();
-
2914 {
-
2915 auto tx0 = credentials::create(depositor, credIssuer2, credType);
-
2916 tx0[sfExpiration] = closeTime + 20;
-
2917 env(tx0);
-
2918 tx0 = credentials::create(charlie, credIssuer2, credType);
-
2919 tx0[sfExpiration] = closeTime + 20;
-
2920 env(tx0);
-
2921 env.close();
-
2922
-
2923 env(credentials::accept(depositor, credIssuer2, credType));
-
2924 env(credentials::accept(charlie, credIssuer2, credType));
-
2925 env.close();
-
2926 }
-
2927
-
2928 {
-
2929 auto tx1 = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
-
2930 env(tx1);
-
2931 env.close();
-
2932
-
2933 auto const tokenKeylet = keylet::mptoken(shares.get<MPTIssue>().getMptID(), depositor.id());
-
2934 BEAST_EXPECT(env.le(tokenKeylet) != nullptr);
-
2935 }
-
2936
-
2937 {
-
2938 // time advance
-
2939 env.close();
-
2940 env.close();
+
2785 env(fset(issuer, asfAllowTrustLineClawback));
+
2786 env.close();
+
2787 env.require(flags(issuer, asfAllowTrustLineClawback));
+
2788
+
2789 PrettyAsset asset = issuer["IOU"];
+
2790 env.trust(asset(1000), owner);
+
2791 env(pay(issuer, owner, asset(500)));
+
2792 env.trust(asset(1000), depositor);
+
2793 env(pay(issuer, depositor, asset(500)));
+
2794 env.trust(asset(1000), charlie);
+
2795 env(pay(issuer, charlie, asset(5)));
+
2796 env.close();
+
2797
+
2798 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
+
2799 env(tx);
+
2800 env.close();
+
2801 BEAST_EXPECT(env.le(keylet));
+
2802
+
2803 {
+
2804 testcase("private vault owner can deposit");
+
2805 auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(50)});
+
2806 env(tx);
+
2807 }
+
2808
+
2809 {
+
2810 testcase("private vault depositor not authorized yet");
+
2811 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
+
2812 env(tx, ter{tecNO_AUTH});
+
2813 }
+
2814
+
2815 {
+
2816 testcase("private vault cannot set non-existing domain");
+
2817 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
2818 tx[sfDomainID] = to_string(base_uint<256>(42ul));
+
2819 env(tx, ter{tecOBJECT_NOT_FOUND});
+
2820 }
+
2821
+
2822 {
+
2823 testcase("private vault set domainId");
+
2824
+
2825 {
+
2826 pdomain::Credentials const credentials1{{.issuer = credIssuer1, .credType = credType}};
+
2827
+
2828 env(pdomain::setTx(pdOwner, credentials1));
+
2829 auto const domainId1 = [&]() {
+
2830 auto tx = env.tx()->getJson(JsonOptions::none);
+
2831 return pdomain::getNewDomain(env.meta());
+
2832 }();
+
2833
+
2834 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
2835 tx[sfDomainID] = to_string(domainId1);
+
2836 env(tx);
+
2837 env.close();
+
2838
+
2839 // Update domain second time, should be harmless
+
2840 env(tx);
+
2841 env.close();
+
2842 }
+
2843
+
2844 {
+
2845 pdomain::Credentials const credentials{
+
2846 {.issuer = credIssuer1, .credType = credType}, {.issuer = credIssuer2, .credType = credType}};
+
2847
+
2848 env(pdomain::setTx(pdOwner, credentials));
+
2849 auto const domainId = [&]() {
+
2850 auto tx = env.tx()->getJson(JsonOptions::none);
+
2851 return pdomain::getNewDomain(env.meta());
+
2852 }();
+
2853
+
2854 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
2855 tx[sfDomainID] = to_string(domainId);
+
2856 env(tx);
+
2857 env.close();
+
2858
+
2859 // Should be idempotent
+
2860 tx = vault.set({.owner = owner, .id = keylet.key});
+
2861 tx[sfDomainID] = to_string(domainId);
+
2862 env(tx);
+
2863 env.close();
+
2864 }
+
2865 }
+
2866
+
2867 {
+
2868 testcase("private vault depositor still not authorized");
+
2869 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
+
2870 env(tx, ter{tecNO_AUTH});
+
2871 env.close();
+
2872 }
+
2873
+
2874 auto const credKeylet = credentials::keylet(depositor, credIssuer1, credType);
+
2875 {
+
2876 testcase("private vault depositor now authorized");
+
2877 env(credentials::create(depositor, credIssuer1, credType));
+
2878 env(credentials::accept(depositor, credIssuer1, credType));
+
2879 env(credentials::create(charlie, credIssuer1, credType));
+
2880 // charlie's credential not accepted
+
2881 env.close();
+
2882 auto credSle = env.le(credKeylet);
+
2883 BEAST_EXPECT(credSle != nullptr);
+
2884
+
2885 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
+
2886 env(tx);
+
2887 env.close();
+
2888
+
2889 tx = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(50)});
+
2890 env(tx, ter{tecNO_AUTH});
+
2891 env.close();
+
2892 }
+
2893
+
2894 {
+
2895 testcase("private vault depositor lost authorization");
+
2896 env(credentials::deleteCred(credIssuer1, depositor, credIssuer1, credType));
+
2897 env(credentials::deleteCred(credIssuer1, charlie, credIssuer1, credType));
+
2898 env.close();
+
2899 auto credSle = env.le(credKeylet);
+
2900 BEAST_EXPECT(credSle == nullptr);
+
2901
+
2902 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
+
2903 env(tx, ter{tecNO_AUTH});
+
2904 env.close();
+
2905 }
+
2906
+
2907 auto const shares = [&env, keylet = keylet, this]() -> Asset {
+
2908 auto const vault = env.le(keylet);
+
2909 BEAST_EXPECT(vault != nullptr);
+
2910 return MPTIssue(vault->at(sfShareMPTID));
+
2911 }();
+
2912
+
2913 {
+
2914 testcase("private vault expired authorization");
+
2915 uint32_t const closeTime = env.current()->header().parentCloseTime.time_since_epoch().count();
+
2916 {
+
2917 auto tx0 = credentials::create(depositor, credIssuer2, credType);
+
2918 tx0[sfExpiration] = closeTime + 20;
+
2919 env(tx0);
+
2920 tx0 = credentials::create(charlie, credIssuer2, credType);
+
2921 tx0[sfExpiration] = closeTime + 20;
+
2922 env(tx0);
+
2923 env.close();
+
2924
+
2925 env(credentials::accept(depositor, credIssuer2, credType));
+
2926 env(credentials::accept(charlie, credIssuer2, credType));
+
2927 env.close();
+
2928 }
+
2929
+
2930 {
+
2931 auto tx1 = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
+
2932 env(tx1);
+
2933 env.close();
+
2934
+
2935 auto const tokenKeylet = keylet::mptoken(shares.get<MPTIssue>().getMptID(), depositor.id());
+
2936 BEAST_EXPECT(env.le(tokenKeylet) != nullptr);
+
2937 }
+
2938
+
2939 {
+
2940 // time advance
2941 env.close();
-
2942
-
2943 auto const credsKeylet = credentials::keylet(depositor, credIssuer2, credType);
-
2944 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
-
2945
-
2946 auto tx2 = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1)});
-
2947 env(tx2, ter{tecEXPIRED});
-
2948 env.close();
-
2949
-
2950 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
-
2951 }
-
2952
-
2953 {
-
2954 auto const credsKeylet = credentials::keylet(charlie, credIssuer2, credType);
-
2955 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
-
2956 auto const tokenKeylet = keylet::mptoken(shares.get<MPTIssue>().getMptID(), charlie.id());
-
2957 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
-
2958
-
2959 auto tx3 = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(2)});
-
2960 env(tx3, ter{tecEXPIRED});
-
2961
-
2962 env.close();
-
2963 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
-
2964 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
-
2965 }
-
2966 }
-
2967
-
2968 {
-
2969 testcase("private vault reset domainId");
-
2970 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
2971 tx[sfDomainID] = "0";
-
2972 env(tx);
-
2973 env.close();
-
2974
-
2975 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
-
2976 env(tx, ter{tecNO_AUTH});
-
2977 env.close();
-
2978
-
2979 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
-
2980 env(tx);
-
2981 env.close();
-
2982
-
2983 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
-
2984 env(tx);
-
2985
-
2986 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)});
-
2987 env(tx);
-
2988 env.close();
-
2989
-
2990 tx = vault.del({
-
2991 .owner = owner,
-
2992 .id = keylet.key,
-
2993 });
-
2994 env(tx);
-
2995 }
-
2996 }
+
2942 env.close();
+
2943 env.close();
+
2944
+
2945 auto const credsKeylet = credentials::keylet(depositor, credIssuer2, credType);
+
2946 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
+
2947
+
2948 auto tx2 = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(1)});
+
2949 env(tx2, ter{tecEXPIRED});
+
2950 env.close();
+
2951
+
2952 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
+
2953 }
+
2954
+
2955 {
+
2956 auto const credsKeylet = credentials::keylet(charlie, credIssuer2, credType);
+
2957 BEAST_EXPECT(env.le(credsKeylet) != nullptr);
+
2958 auto const tokenKeylet = keylet::mptoken(shares.get<MPTIssue>().getMptID(), charlie.id());
+
2959 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
+
2960
+
2961 auto tx3 = vault.deposit({.depositor = charlie, .id = keylet.key, .amount = asset(2)});
+
2962 env(tx3, ter{tecEXPIRED});
+
2963
+
2964 env.close();
+
2965 BEAST_EXPECT(env.le(credsKeylet) == nullptr);
+
2966 BEAST_EXPECT(env.le(tokenKeylet) == nullptr);
+
2967 }
+
2968 }
+
2969
+
2970 {
+
2971 testcase("private vault reset domainId");
+
2972 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
2973 tx[sfDomainID] = "0";
+
2974 env(tx);
+
2975 env.close();
+
2976
+
2977 tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
+
2978 env(tx, ter{tecNO_AUTH});
+
2979 env.close();
+
2980
+
2981 tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
+
2982 env(tx);
+
2983 env.close();
+
2984
+
2985 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = depositor, .amount = asset(0)});
+
2986 env(tx);
+
2987
+
2988 tx = vault.clawback({.issuer = issuer, .id = keylet.key, .holder = owner, .amount = asset(0)});
+
2989 env(tx);
+
2990 env.close();
+
2991
+
2992 tx = vault.del({
+
2993 .owner = owner,
+
2994 .id = keylet.key,
+
2995 });
+
2996 env(tx);
+
2997 }
+
2998 }
-
2997
-
2998 void
-
- -
3000 {
-
3001 using namespace test::jtx;
-
3002
-
3003 testcase("private XRP vault");
+
2999
+
3000 void
+
+ +
3002 {
+
3003 using namespace test::jtx;
3004
-
3005 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3006 Account owner{"owner"};
-
3007 Account depositor{"depositor"};
-
3008 Account alice{"charlie"};
-
3009 std::string const credType = "credential";
-
3010 Vault vault{env};
-
3011 env.fund(XRP(100000), owner, depositor, alice);
-
3012 env.close();
-
3013
-
3014 PrettyAsset asset = xrpIssue();
-
3015 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
-
3016 env(tx);
-
3017 env.close();
-
3018
-
3019 auto const [vaultAccount, issuanceId] = [&env, keylet = keylet, this]() -> std::tuple<AccountID, uint192> {
-
3020 auto const vault = env.le(keylet);
-
3021 BEAST_EXPECT(vault != nullptr);
-
3022 return {vault->at(sfAccount), vault->at(sfShareMPTID)};
-
3023 }();
-
3024 BEAST_EXPECT(env.le(keylet::account(vaultAccount)));
-
3025 BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId)));
-
3026 PrettyAsset shares{issuanceId};
-
3027
-
3028 {
-
3029 testcase("private XRP vault owner can deposit");
-
3030 auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(50)});
-
3031 env(tx);
-
3032 env.close();
-
3033 }
-
3034
-
3035 {
-
3036 testcase("private XRP vault cannot pay shares to depositor yet");
-
3037 env(pay(owner, depositor, shares(1)), ter{tecNO_AUTH});
-
3038 }
-
3039
-
3040 {
-
3041 testcase("private XRP vault depositor not authorized yet");
-
3042 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
-
3043 env(tx, ter{tecNO_AUTH});
-
3044 }
-
3045
-
3046 {
-
3047 testcase("private XRP vault set DomainID");
-
3048 pdomain::Credentials const credentials{{.issuer = owner, .credType = credType}};
-
3049
-
3050 env(pdomain::setTx(owner, credentials));
-
3051 auto const domainId = [&]() {
-
3052 auto tx = env.tx()->getJson(JsonOptions::none);
-
3053 return pdomain::getNewDomain(env.meta());
-
3054 }();
-
3055
-
3056 auto tx = vault.set({.owner = owner, .id = keylet.key});
-
3057 tx[sfDomainID] = to_string(domainId);
-
3058 env(tx);
-
3059 env.close();
-
3060 }
-
3061
-
3062 auto const credKeylet = credentials::keylet(depositor, owner, credType);
-
3063 {
-
3064 testcase("private XRP vault depositor now authorized");
-
3065 env(credentials::create(depositor, owner, credType));
-
3066 env(credentials::accept(depositor, owner, credType));
-
3067 env.close();
-
3068
-
3069 BEAST_EXPECT(env.le(credKeylet));
-
3070 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
-
3071 env(tx);
-
3072 env.close();
-
3073 }
-
3074
-
3075 {
-
3076 testcase("private XRP vault can pay shares to depositor");
-
3077 env(pay(owner, depositor, shares(1)));
-
3078 }
-
3079
-
3080 {
-
3081 testcase("private XRP vault cannot pay shares to 3rd party");
-
3082 Json::Value jv;
-
3083 jv[sfAccount] = alice.human();
-
3084 jv[sfTransactionType] = jss::MPTokenAuthorize;
-
3085 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
-
3086 env(jv);
-
3087 env.close();
-
3088
-
3089 env(pay(owner, alice, shares(1)), ter{tecNO_AUTH});
-
3090 }
-
3091 }
+
3005 testcase("private XRP vault");
+
3006
+
3007 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3008 Account owner{"owner"};
+
3009 Account depositor{"depositor"};
+
3010 Account alice{"charlie"};
+
3011 std::string const credType = "credential";
+
3012 Vault vault{env};
+
3013 env.fund(XRP(100000), owner, depositor, alice);
+
3014 env.close();
+
3015
+
3016 PrettyAsset asset = xrpIssue();
+
3017 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset, .flags = tfVaultPrivate});
+
3018 env(tx);
+
3019 env.close();
+
3020
+
3021 auto const [vaultAccount, issuanceId] = [&env, keylet = keylet, this]() -> std::tuple<AccountID, uint192> {
+
3022 auto const vault = env.le(keylet);
+
3023 BEAST_EXPECT(vault != nullptr);
+
3024 return {vault->at(sfAccount), vault->at(sfShareMPTID)};
+
3025 }();
+
3026 BEAST_EXPECT(env.le(keylet::account(vaultAccount)));
+
3027 BEAST_EXPECT(env.le(keylet::mptIssuance(issuanceId)));
+
3028 PrettyAsset shares{issuanceId};
+
3029
+
3030 {
+
3031 testcase("private XRP vault owner can deposit");
+
3032 auto tx = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(50)});
+
3033 env(tx);
+
3034 env.close();
+
3035 }
+
3036
+
3037 {
+
3038 testcase("private XRP vault cannot pay shares to depositor yet");
+
3039 env(pay(owner, depositor, shares(1)), ter{tecNO_AUTH});
+
3040 }
+
3041
+
3042 {
+
3043 testcase("private XRP vault depositor not authorized yet");
+
3044 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
+
3045 env(tx, ter{tecNO_AUTH});
+
3046 }
+
3047
+
3048 {
+
3049 testcase("private XRP vault set DomainID");
+
3050 pdomain::Credentials const credentials{{.issuer = owner, .credType = credType}};
+
3051
+
3052 env(pdomain::setTx(owner, credentials));
+
3053 auto const domainId = [&]() {
+
3054 auto tx = env.tx()->getJson(JsonOptions::none);
+
3055 return pdomain::getNewDomain(env.meta());
+
3056 }();
+
3057
+
3058 auto tx = vault.set({.owner = owner, .id = keylet.key});
+
3059 tx[sfDomainID] = to_string(domainId);
+
3060 env(tx);
+
3061 env.close();
+
3062 }
+
3063
+
3064 auto const credKeylet = credentials::keylet(depositor, owner, credType);
+
3065 {
+
3066 testcase("private XRP vault depositor now authorized");
+
3067 env(credentials::create(depositor, owner, credType));
+
3068 env(credentials::accept(depositor, owner, credType));
+
3069 env.close();
+
3070
+
3071 BEAST_EXPECT(env.le(credKeylet));
+
3072 auto tx = vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(50)});
+
3073 env(tx);
+
3074 env.close();
+
3075 }
+
3076
+
3077 {
+
3078 testcase("private XRP vault can pay shares to depositor");
+
3079 env(pay(owner, depositor, shares(1)));
+
3080 }
+
3081
+
3082 {
+
3083 testcase("private XRP vault cannot pay shares to 3rd party");
+
3084 Json::Value jv;
+
3085 jv[sfAccount] = alice.human();
+
3086 jv[sfTransactionType] = jss::MPTokenAuthorize;
+
3087 jv[sfMPTokenIssuanceID] = to_string(issuanceId);
+
3088 env(jv);
+
3089 env.close();
+
3090
+
3091 env(pay(owner, alice, shares(1)), ter{tecNO_AUTH});
+
3092 }
+
3093 }
-
3092
-
3093 void
-
- -
3095 {
-
3096 using namespace test::jtx;
-
3097
-
3098 testcase("fail pseudo-account allocation");
-
3099 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3100 Account const owner{"owner"};
-
3101 Vault vault{env};
-
3102 env.fund(XRP(1000), owner);
-
3103
-
3104 auto const keylet = keylet::vault(owner.id(), env.seq(owner));
-
3105 for (int i = 0; i < 256; ++i)
-
3106 {
-
3107 AccountID const accountId = xrpl::pseudoAccountAddress(*env.current(), keylet.key);
-
3108
-
3109 env(pay(env.master.id(), accountId, XRP(1000)), seq(autofill), fee(autofill), sig(autofill));
-
3110 }
-
3111
-
3112 auto [tx, keylet1] = vault.create({.owner = owner, .asset = xrpIssue()});
-
3113 BEAST_EXPECT(keylet.key == keylet1.key);
-
3114 env(tx, ter{terADDRESS_COLLISION});
-
3115 }
+
3094
+
3095 void
+
+ +
3097 {
+
3098 using namespace test::jtx;
+
3099
+
3100 testcase("fail pseudo-account allocation");
+
3101 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3102 Account const owner{"owner"};
+
3103 Vault vault{env};
+
3104 env.fund(XRP(1000), owner);
+
3105
+
3106 auto const keylet = keylet::vault(owner.id(), env.seq(owner));
+
3107 for (int i = 0; i < 256; ++i)
+
3108 {
+
3109 AccountID const accountId = xrpl::pseudoAccountAddress(*env.current(), keylet.key);
+
3110
+
3111 env(pay(env.master.id(), accountId, XRP(1000)), seq(autofill), fee(autofill), sig(autofill));
+
3112 }
+
3113
+
3114 auto [tx, keylet1] = vault.create({.owner = owner, .asset = xrpIssue()});
+
3115 BEAST_EXPECT(keylet.key == keylet1.key);
+
3116 env(tx, ter{terADDRESS_COLLISION});
+
3117 }
-
3116
-
3117 void
-
- -
3119 {
-
3120 using namespace test::jtx;
-
3121
-
3122 struct Data
-
3123 {
-
3124 Account const& owner;
-
3125 Account const& issuer;
-
3126 Account const& depositor;
-
3127 Account const& vaultAccount;
-
3128 MPTIssue shares;
-
3129 PrettyAsset const& share;
-
3130 Vault& vault;
-
3131 xrpl::Keylet keylet;
-
3132 Issue assets;
-
3133 PrettyAsset const& asset;
-
3134 std::function<bool(std::function<bool(SLE&, SLE&)>)> peek;
-
3135 };
-
3136
-
3137 auto testCase = [&, this](std::uint8_t scale, std::function<void(Env & env, Data data)> test) {
-
3138 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3139 Account const owner{"owner"};
-
3140 Account const issuer{"issuer"};
-
3141 Account const depositor{"depositor"};
-
3142 Vault vault{env};
-
3143 env.fund(XRP(1000), issuer, owner, depositor);
-
3144 env(fset(issuer, asfAllowTrustLineClawback));
-
3145 env.close();
-
3146
-
3147 PrettyAsset const asset = issuer["IOU"];
-
3148 env.trust(asset(1000), owner);
-
3149 env.trust(asset(1000), depositor);
-
3150 env(pay(issuer, owner, asset(200)));
-
3151 env(pay(issuer, depositor, asset(200)));
-
3152 env.close();
-
3153
-
3154 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
3155 tx[sfScale] = scale;
-
3156 env(tx);
-
3157
-
3158 auto const [vaultAccount, issuanceId] = [&env](xrpl::Keylet keylet) -> std::tuple<Account, MPTID> {
-
3159 auto const vault = env.le(keylet);
-
3160 return {Account("vault", vault->at(sfAccount)), vault->at(sfShareMPTID)};
-
3161 }(keylet);
-
3162 MPTIssue shares(issuanceId);
-
3163 env.memoize(vaultAccount);
-
3164
-
3165 auto const peek = [=, &env, this](std::function<bool(SLE&, SLE&)> fn) -> bool {
-
3166 return env.app().openLedger().modify([&](OpenView& view, beast::Journal j) -> bool {
-
3167 Sandbox sb(&view, tapNONE);
-
3168 auto vault = sb.peek(keylet::vault(keylet.key));
-
3169 if (!BEAST_EXPECT(vault != nullptr))
-
3170 return false;
-
3171 auto shares = sb.peek(keylet::mptIssuance(vault->at(sfShareMPTID)));
-
3172 if (!BEAST_EXPECT(shares != nullptr))
-
3173 return false;
-
3174 if (fn(*vault, *shares))
-
3175 {
-
3176 sb.update(vault);
-
3177 sb.update(shares);
-
3178 sb.apply(view);
-
3179 return true;
-
3180 }
-
3181 return false;
-
3182 });
-
3183 };
-
3184
-
3185 test(
-
3186 env,
-
3187 {.owner = owner,
-
3188 .issuer = issuer,
-
3189 .depositor = depositor,
-
3190 .vaultAccount = vaultAccount,
-
3191 .shares = shares,
-
3192 .share = PrettyAsset(shares),
-
3193 .vault = vault,
-
3194 .keylet = keylet,
-
3195 .assets = asset.raw().get<Issue>(),
-
3196 .asset = asset,
-
3197 .peek = peek});
-
3198 };
-
3199
-
3200 testCase(18, [&, this](Env& env, Data d) {
-
3201 testcase("Scale deposit overflow on first deposit");
-
3202 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
-
3203 env(tx, ter{tecPATH_DRY});
-
3204 env.close();
-
3205 });
-
3206
-
3207 testCase(18, [&, this](Env& env, Data d) {
-
3208 testcase("Scale deposit overflow on second deposit");
-
3209
-
3210 {
-
3211 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
-
3212 env(tx);
-
3213 env.close();
-
3214 }
-
3215
-
3216 {
-
3217 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
-
3218 env(tx, ter{tecPATH_DRY});
-
3219 env.close();
-
3220 }
-
3221 });
-
3222
-
3223 testCase(18, [&, this](Env& env, Data d) {
-
3224 testcase("Scale deposit overflow on total shares");
-
3225
-
3226 {
-
3227 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
-
3228 env(tx);
-
3229 env.close();
-
3230 }
-
3231
-
3232 {
-
3233 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
-
3234 env(tx, ter{tecPATH_DRY});
-
3235 env.close();
-
3236 }
-
3237 });
-
3238
-
3239 testCase(1, [&, this](Env& env, Data d) {
-
3240 testcase("Scale deposit exact");
-
3241
-
3242 auto const start = env.balance(d.depositor, d.assets).number();
-
3243 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(1)});
-
3244 env(tx);
-
3245 env.close();
-
3246 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
-
3247 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - 1));
-
3248 });
-
3249
-
3250 testCase(1, [&, this](Env& env, Data d) {
-
3251 testcase("Scale deposit insignificant amount");
-
3252
-
3253 auto tx = d.vault.deposit(
-
3254 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(9, -2))});
-
3255 env(tx, ter{tecPRECISION_LOSS});
-
3256 });
-
3257
-
3258 testCase(1, [&, this](Env& env, Data d) {
-
3259 testcase("Scale deposit exact, using full precision");
-
3260
-
3261 auto const start = env.balance(d.depositor, d.assets).number();
-
3262 auto tx = d.vault.deposit(
-
3263 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(15, -1))});
-
3264 env(tx);
-
3265 env.close();
-
3266 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
-
3267 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(15, -1)));
-
3268 });
-
3269
-
3270 testCase(1, [&, this](Env& env, Data d) {
-
3271 testcase("Scale deposit exact, truncating from .5");
-
3272
-
3273 auto const start = env.balance(d.depositor, d.assets).number();
-
3274 // Each of the cases below will transfer exactly 1.2 IOU to the
-
3275 // vault and receive 12 shares in exchange
-
3276 {
-
3277 auto tx = d.vault.deposit(
-
3278 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(125, -2))});
-
3279 env(tx);
-
3280 env.close();
-
3281 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
-
3282 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1)));
-
3283 }
-
3284
-
3285 {
-
3286 auto tx = d.vault.deposit(
-
3287 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1201, -3))});
-
3288 env(tx);
-
3289 env.close();
-
3290 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
-
3291 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(24, -1)));
-
3292 }
-
3293
-
3294 {
-
3295 auto tx = d.vault.deposit(
-
3296 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1299, -3))});
-
3297 env(tx);
-
3298 env.close();
-
3299 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
-
3300 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(36, -1)));
-
3301 }
-
3302 });
-
3303
-
3304 testCase(1, [&, this](Env& env, Data d) {
-
3305 testcase("Scale deposit exact, truncating from .01");
-
3306
-
3307 auto const start = env.balance(d.depositor, d.assets).number();
-
3308 // round to 12
-
3309 auto tx = d.vault.deposit(
-
3310 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1201, -3))});
-
3311 env(tx);
-
3312 env.close();
-
3313 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
-
3314 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1)));
-
3315
-
3316 {
-
3317 // round to 6
-
3318 auto tx = d.vault.deposit(
-
3319 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(69, -2))});
-
3320 env(tx);
-
3321 env.close();
-
3322 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
-
3323 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(18, -1)));
-
3324 }
-
3325 });
-
3326
-
3327 testCase(1, [&, this](Env& env, Data d) {
-
3328 testcase("Scale deposit exact, truncating from .99");
-
3329
-
3330 auto const start = env.balance(d.depositor, d.assets).number();
-
3331 // round to 12
-
3332 auto tx = d.vault.deposit(
-
3333 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1299, -3))});
-
3334 env(tx);
-
3335 env.close();
-
3336 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
-
3337 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1)));
-
3338
-
3339 {
-
3340 // round to 6
-
3341 auto tx = d.vault.deposit(
-
3342 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(62, -2))});
-
3343 env(tx);
-
3344 env.close();
-
3345 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
-
3346 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(18, -1)));
-
3347 }
-
3348 });
-
3349
-
3350 testCase(1, [&, this](Env& env, Data d) {
-
3351 // initial setup: deposit 100 IOU, receive 1000 shares
-
3352 auto const start = env.balance(d.depositor, d.assets).number();
-
3353 auto tx = d.vault.deposit(
-
3354 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(100, 0))});
-
3355 env(tx);
-
3356 env.close();
-
3357 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
-
3358 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0)));
-
3359 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0)));
-
3360 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-1000, 0)));
-
3361
-
3362 {
-
3363 testcase("Scale redeem exact");
-
3364 // sharesToAssetsWithdraw:
-
3365 // assets = assetsTotal * (shares / sharesTotal)
-
3366 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
-
3367
-
3368 auto const start = env.balance(d.depositor, d.assets).number();
-
3369 auto tx = d.vault.withdraw(
-
3370 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, Number(100, 0))});
-
3371 env(tx);
-
3372 env.close();
-
3373 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
-
3374 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(10, 0)));
-
3375 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0)));
-
3376 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-900, 0)));
-
3377 }
-
3378
-
3379 {
-
3380 testcase("Scale redeem with rounding");
-
3381 // sharesToAssetsWithdraw:
-
3382 // assets = assetsTotal * (shares / sharesTotal)
-
3383 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
-
3384
-
3385 auto const start = env.balance(d.depositor, d.assets).number();
-
3386 d.peek([](SLE& vault, auto&) -> bool {
-
3387 vault[sfAssetsAvailable] = Number(1);
-
3388 return true;
-
3389 });
-
3390
-
3391 // Note, this transaction fails first (because of above change
-
3392 // in the open ledger) but then succeeds when the ledger is
-
3393 // closed (because a modification like above is not persistent),
-
3394 // which is why the checks below are expected to pass.
-
3395 auto tx = d.vault.withdraw(
-
3396 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, Number(25, 0))});
-
3397 env(tx, ter{tecINSUFFICIENT_FUNDS});
-
3398 env.close();
-
3399 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
-
3400 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(25, -1)));
-
3401 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(900 - 25, -1)));
-
3402 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900 - 25, 0)));
-
3403 }
-
3404
-
3405 {
-
3406 testcase("Scale redeem exact");
-
3407 // sharesToAssetsWithdraw:
-
3408 // assets = assetsTotal * (shares / sharesTotal)
-
3409 // assets = 87.5 * 21 / 875 = 87.5 * 0.024 = 2.1
-
3410
-
3411 auto const start = env.balance(d.depositor, d.assets).number();
+
3118
+
3119 void
+
+ +
3121 {
+
3122 using namespace test::jtx;
+
3123
+
3124 struct Data
+
3125 {
+
3126 Account const& owner;
+
3127 Account const& issuer;
+
3128 Account const& depositor;
+
3129 Account const& vaultAccount;
+
3130 MPTIssue shares;
+
3131 PrettyAsset const& share;
+
3132 Vault& vault;
+
3133 xrpl::Keylet keylet;
+
3134 Issue assets;
+
3135 PrettyAsset const& asset;
+
3136 std::function<bool(std::function<bool(SLE&, SLE&)>)> peek;
+
3137 };
+
3138
+
3139 auto testCase = [&, this](std::uint8_t scale, std::function<void(Env & env, Data data)> test) {
+
3140 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3141 Account const owner{"owner"};
+
3142 Account const issuer{"issuer"};
+
3143 Account const depositor{"depositor"};
+
3144 Vault vault{env};
+
3145 env.fund(XRP(1000), issuer, owner, depositor);
+
3146 env(fset(issuer, asfAllowTrustLineClawback));
+
3147 env.close();
+
3148
+
3149 PrettyAsset const asset = issuer["IOU"];
+
3150 env.trust(asset(1000), owner);
+
3151 env.trust(asset(1000), depositor);
+
3152 env(pay(issuer, owner, asset(200)));
+
3153 env(pay(issuer, depositor, asset(200)));
+
3154 env.close();
+
3155
+
3156 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
3157 tx[sfScale] = scale;
+
3158 env(tx);
+
3159
+
3160 auto const [vaultAccount, issuanceId] = [&env](xrpl::Keylet keylet) -> std::tuple<Account, MPTID> {
+
3161 auto const vault = env.le(keylet);
+
3162 return {Account("vault", vault->at(sfAccount)), vault->at(sfShareMPTID)};
+
3163 }(keylet);
+
3164 MPTIssue shares(issuanceId);
+
3165 env.memoize(vaultAccount);
+
3166
+
3167 auto const peek = [=, &env, this](std::function<bool(SLE&, SLE&)> fn) -> bool {
+
3168 return env.app().openLedger().modify([&](OpenView& view, beast::Journal j) -> bool {
+
3169 Sandbox sb(&view, tapNONE);
+
3170 auto vault = sb.peek(keylet::vault(keylet.key));
+
3171 if (!BEAST_EXPECT(vault != nullptr))
+
3172 return false;
+
3173 auto shares = sb.peek(keylet::mptIssuance(vault->at(sfShareMPTID)));
+
3174 if (!BEAST_EXPECT(shares != nullptr))
+
3175 return false;
+
3176 if (fn(*vault, *shares))
+
3177 {
+
3178 sb.update(vault);
+
3179 sb.update(shares);
+
3180 sb.apply(view);
+
3181 return true;
+
3182 }
+
3183 return false;
+
3184 });
+
3185 };
+
3186
+
3187 test(
+
3188 env,
+
3189 {.owner = owner,
+
3190 .issuer = issuer,
+
3191 .depositor = depositor,
+
3192 .vaultAccount = vaultAccount,
+
3193 .shares = shares,
+
3194 .share = PrettyAsset(shares),
+
3195 .vault = vault,
+
3196 .keylet = keylet,
+
3197 .assets = asset.raw().get<Issue>(),
+
3198 .asset = asset,
+
3199 .peek = peek});
+
3200 };
+
3201
+
3202 testCase(18, [&, this](Env& env, Data d) {
+
3203 testcase("Scale deposit overflow on first deposit");
+
3204 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
+
3205 env(tx, ter{tecPATH_DRY});
+
3206 env.close();
+
3207 });
+
3208
+
3209 testCase(18, [&, this](Env& env, Data d) {
+
3210 testcase("Scale deposit overflow on second deposit");
+
3211
+
3212 {
+
3213 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
+
3214 env(tx);
+
3215 env.close();
+
3216 }
+
3217
+
3218 {
+
3219 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(10)});
+
3220 env(tx, ter{tecPATH_DRY});
+
3221 env.close();
+
3222 }
+
3223 });
+
3224
+
3225 testCase(18, [&, this](Env& env, Data d) {
+
3226 testcase("Scale deposit overflow on total shares");
+
3227
+
3228 {
+
3229 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
+
3230 env(tx);
+
3231 env.close();
+
3232 }
+
3233
+
3234 {
+
3235 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
+
3236 env(tx, ter{tecPATH_DRY});
+
3237 env.close();
+
3238 }
+
3239 });
+
3240
+
3241 testCase(1, [&, this](Env& env, Data d) {
+
3242 testcase("Scale deposit exact");
+
3243
+
3244 auto const start = env.balance(d.depositor, d.assets).number();
+
3245 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(1)});
+
3246 env(tx);
+
3247 env.close();
+
3248 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10));
+
3249 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - 1));
+
3250 });
+
3251
+
3252 testCase(1, [&, this](Env& env, Data d) {
+
3253 testcase("Scale deposit insignificant amount");
+
3254
+
3255 auto tx = d.vault.deposit(
+
3256 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(9, -2))});
+
3257 env(tx, ter{tecPRECISION_LOSS});
+
3258 });
+
3259
+
3260 testCase(1, [&, this](Env& env, Data d) {
+
3261 testcase("Scale deposit exact, using full precision");
+
3262
+
3263 auto const start = env.balance(d.depositor, d.assets).number();
+
3264 auto tx = d.vault.deposit(
+
3265 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(15, -1))});
+
3266 env(tx);
+
3267 env.close();
+
3268 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15));
+
3269 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(15, -1)));
+
3270 });
+
3271
+
3272 testCase(1, [&, this](Env& env, Data d) {
+
3273 testcase("Scale deposit exact, truncating from .5");
+
3274
+
3275 auto const start = env.balance(d.depositor, d.assets).number();
+
3276 // Each of the cases below will transfer exactly 1.2 IOU to the
+
3277 // vault and receive 12 shares in exchange
+
3278 {
+
3279 auto tx = d.vault.deposit(
+
3280 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(125, -2))});
+
3281 env(tx);
+
3282 env.close();
+
3283 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
+
3284 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1)));
+
3285 }
+
3286
+
3287 {
+
3288 auto tx = d.vault.deposit(
+
3289 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1201, -3))});
+
3290 env(tx);
+
3291 env.close();
+
3292 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24));
+
3293 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(24, -1)));
+
3294 }
+
3295
+
3296 {
+
3297 auto tx = d.vault.deposit(
+
3298 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1299, -3))});
+
3299 env(tx);
+
3300 env.close();
+
3301 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36));
+
3302 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(36, -1)));
+
3303 }
+
3304 });
+
3305
+
3306 testCase(1, [&, this](Env& env, Data d) {
+
3307 testcase("Scale deposit exact, truncating from .01");
+
3308
+
3309 auto const start = env.balance(d.depositor, d.assets).number();
+
3310 // round to 12
+
3311 auto tx = d.vault.deposit(
+
3312 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1201, -3))});
+
3313 env(tx);
+
3314 env.close();
+
3315 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
+
3316 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1)));
+
3317
+
3318 {
+
3319 // round to 6
+
3320 auto tx = d.vault.deposit(
+
3321 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(69, -2))});
+
3322 env(tx);
+
3323 env.close();
+
3324 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
+
3325 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(18, -1)));
+
3326 }
+
3327 });
+
3328
+
3329 testCase(1, [&, this](Env& env, Data d) {
+
3330 testcase("Scale deposit exact, truncating from .99");
+
3331
+
3332 auto const start = env.balance(d.depositor, d.assets).number();
+
3333 // round to 12
+
3334 auto tx = d.vault.deposit(
+
3335 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(1299, -3))});
+
3336 env(tx);
+
3337 env.close();
+
3338 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12));
+
3339 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(12, -1)));
+
3340
+
3341 {
+
3342 // round to 6
+
3343 auto tx = d.vault.deposit(
+
3344 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(62, -2))});
+
3345 env(tx);
+
3346 env.close();
+
3347 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18));
+
3348 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(18, -1)));
+
3349 }
+
3350 });
+
3351
+
3352 testCase(1, [&, this](Env& env, Data d) {
+
3353 // initial setup: deposit 100 IOU, receive 1000 shares
+
3354 auto const start = env.balance(d.depositor, d.assets).number();
+
3355 auto tx = d.vault.deposit(
+
3356 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(100, 0))});
+
3357 env(tx);
+
3358 env.close();
+
3359 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
+
3360 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0)));
+
3361 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0)));
+
3362 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-1000, 0)));
+
3363
+
3364 {
+
3365 testcase("Scale redeem exact");
+
3366 // sharesToAssetsWithdraw:
+
3367 // assets = assetsTotal * (shares / sharesTotal)
+
3368 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
+
3369
+
3370 auto const start = env.balance(d.depositor, d.assets).number();
+
3371 auto tx = d.vault.withdraw(
+
3372 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, Number(100, 0))});
+
3373 env(tx);
+
3374 env.close();
+
3375 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
+
3376 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(10, 0)));
+
3377 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0)));
+
3378 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-900, 0)));
+
3379 }
+
3380
+
3381 {
+
3382 testcase("Scale redeem with rounding");
+
3383 // sharesToAssetsWithdraw:
+
3384 // assets = assetsTotal * (shares / sharesTotal)
+
3385 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
+
3386
+
3387 auto const start = env.balance(d.depositor, d.assets).number();
+
3388 d.peek([](SLE& vault, auto&) -> bool {
+
3389 vault[sfAssetsAvailable] = Number(1);
+
3390 return true;
+
3391 });
+
3392
+
3393 // Note, this transaction fails first (because of above change
+
3394 // in the open ledger) but then succeeds when the ledger is
+
3395 // closed (because a modification like above is not persistent),
+
3396 // which is why the checks below are expected to pass.
+
3397 auto tx = d.vault.withdraw(
+
3398 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, Number(25, 0))});
+
3399 env(tx, ter{tecINSUFFICIENT_FUNDS});
+
3400 env.close();
+
3401 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
+
3402 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(25, -1)));
+
3403 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(900 - 25, -1)));
+
3404 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900 - 25, 0)));
+
3405 }
+
3406
+
3407 {
+
3408 testcase("Scale redeem exact");
+
3409 // sharesToAssetsWithdraw:
+
3410 // assets = assetsTotal * (shares / sharesTotal)
+
3411 // assets = 87.5 * 21 / 875 = 87.5 * 0.024 = 2.1
3412
-
3413 tx = d.vault.withdraw(
-
3414 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, Number(21, 0))});
-
3415 env(tx);
-
3416 env.close();
-
3417 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 21));
-
3418 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(21, -1)));
-
3419 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(875 - 21, -1)));
-
3420 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(875 - 21, 0)));
-
3421 }
-
3422
-
3423 {
-
3424 testcase("Scale redeem rest");
-
3425 auto const rest = env.balance(d.depositor, d.shares).number();
-
3426
-
3427 tx =
-
3428 d.vault.withdraw({.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, rest)});
-
3429 env(tx);
-
3430 env.close();
-
3431 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
-
3432 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
-
3433 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
-
3434 }
-
3435 });
-
3436
-
3437 testCase(18, [&, this](Env& env, Data d) {
-
3438 testcase("Scale withdraw overflow");
-
3439
-
3440 {
-
3441 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
-
3442 env(tx);
-
3443 env.close();
-
3444 }
-
3445
-
3446 {
-
3447 auto tx = d.vault.withdraw(
-
3448 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(10, 0))});
-
3449 env(tx, ter{tecPATH_DRY});
-
3450 env.close();
-
3451 }
-
3452 });
-
3453
-
3454 testCase(1, [&, this](Env& env, Data d) {
-
3455 // initial setup: deposit 100 IOU, receive 1000 shares
-
3456 auto const start = env.balance(d.depositor, d.assets).number();
-
3457 auto tx = d.vault.deposit(
-
3458 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(100, 0))});
-
3459 env(tx);
-
3460 env.close();
-
3461 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
-
3462 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0)));
-
3463 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0)));
-
3464 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-1000, 0)));
-
3465
-
3466 {
-
3467 testcase("Scale withdraw exact");
-
3468 // assetsToSharesWithdraw:
-
3469 // shares = sharesTotal * (assets / assetsTotal)
-
3470 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
-
3471 // sharesToAssetsWithdraw:
-
3472 // assets = assetsTotal * (shares / sharesTotal)
-
3473 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
-
3474
-
3475 auto const start = env.balance(d.depositor, d.assets).number();
-
3476 auto tx = d.vault.withdraw(
-
3477 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(10, 0))});
-
3478 env(tx);
-
3479 env.close();
-
3480 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
-
3481 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(10, 0)));
-
3482 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0)));
-
3483 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-900, 0)));
-
3484 }
-
3485
-
3486 {
-
3487 testcase("Scale withdraw insignificant amount");
-
3488 auto tx = d.vault.withdraw(
-
3489 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(4, -2))});
-
3490 env(tx, ter{tecPRECISION_LOSS});
-
3491 }
-
3492
-
3493 {
-
3494 testcase("Scale withdraw with rounding assets");
-
3495 // assetsToSharesWithdraw:
-
3496 // shares = sharesTotal * (assets / assetsTotal)
-
3497 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
-
3498 // sharesToAssetsWithdraw:
-
3499 // assets = assetsTotal * (shares / sharesTotal)
-
3500 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
-
3501
-
3502 auto const start = env.balance(d.depositor, d.assets).number();
-
3503 d.peek([](SLE& vault, auto&) -> bool {
-
3504 vault[sfAssetsAvailable] = Number(1);
-
3505 return true;
-
3506 });
-
3507
-
3508 // Note, this transaction fails first (because of above change
-
3509 // in the open ledger) but then succeeds when the ledger is
-
3510 // closed (because a modification like above is not persistent),
-
3511 // which is why the checks below are expected to pass.
-
3512 auto tx = d.vault.withdraw(
-
3513 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(25, -1))});
-
3514 env(tx, ter{tecINSUFFICIENT_FUNDS});
-
3515 env.close();
-
3516 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
-
3517 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(25, -1)));
-
3518 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(900 - 25, -1)));
-
3519 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900 - 25, 0)));
-
3520 }
-
3521
-
3522 {
-
3523 testcase("Scale withdraw with rounding shares up");
-
3524 // assetsToSharesWithdraw:
-
3525 // shares = sharesTotal * (assets / assetsTotal)
-
3526 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
-
3527 // sharesToAssetsWithdraw:
-
3528 // assets = assetsTotal * (shares / sharesTotal)
-
3529 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
-
3530
-
3531 auto const start = env.balance(d.depositor, d.assets).number();
-
3532 auto tx = d.vault.withdraw(
-
3533 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(375, -2))});
-
3534 env(tx);
-
3535 env.close();
-
3536 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
-
3537 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(38, -1)));
-
3538 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(875 - 38, -1)));
-
3539 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(875 - 38, 0)));
-
3540 }
-
3541
-
3542 {
-
3543 testcase("Scale withdraw with rounding shares down");
-
3544 // assetsToSharesWithdraw:
-
3545 // shares = sharesTotal * (assets / assetsTotal)
-
3546 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
-
3547 // sharesToAssetsWithdraw:
-
3548 // assets = assetsTotal * (shares / sharesTotal)
-
3549 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
-
3550
-
3551 auto const start = env.balance(d.depositor, d.assets).number();
-
3552 auto tx = d.vault.withdraw(
-
3553 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(372, -2))});
-
3554 env(tx);
-
3555 env.close();
-
3556 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
-
3557 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(37, -1)));
-
3558 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(837 - 37, -1)));
-
3559 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(837 - 37, 0)));
-
3560 }
-
3561
-
3562 {
-
3563 testcase("Scale withdraw tiny amount");
-
3564
-
3565 auto const start = env.balance(d.depositor, d.assets).number();
-
3566 auto tx = d.vault.withdraw(
-
3567 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(9, -2))});
-
3568 env(tx);
-
3569 env.close();
-
3570 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
-
3571 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(1, -1)));
-
3572 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(800 - 1, -1)));
-
3573 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(800 - 1, 0)));
-
3574 }
-
3575
-
3576 {
-
3577 testcase("Scale withdraw rest");
-
3578 auto const rest = env.balance(d.vaultAccount, d.assets).number();
-
3579
-
3580 tx =
-
3581 d.vault.withdraw({.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, rest)});
-
3582 env(tx);
-
3583 env.close();
-
3584 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
-
3585 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
-
3586 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
-
3587 }
-
3588 });
-
3589
-
3590 testCase(18, [&, this](Env& env, Data d) {
-
3591 testcase("Scale clawback overflow");
-
3592
-
3593 {
-
3594 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
-
3595 env(tx);
-
3596 env.close();
-
3597 }
-
3598
-
3599 {
-
3600 auto tx = d.vault.clawback(
-
3601 {.issuer = d.issuer,
-
3602 .id = d.keylet.key,
-
3603 .holder = d.depositor,
-
3604 .amount = STAmount(d.asset, Number(10, 0))});
-
3605 env(tx, ter{tecPATH_DRY});
-
3606 env.close();
-
3607 }
-
3608 });
-
3609
-
3610 testCase(1, [&, this](Env& env, Data d) {
-
3611 // initial setup: deposit 100 IOU, receive 1000 shares
-
3612 auto const start = env.balance(d.depositor, d.assets).number();
-
3613 auto tx = d.vault.deposit(
-
3614 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(100, 0))});
-
3615 env(tx);
-
3616 env.close();
-
3617 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
-
3618 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0)));
-
3619 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0)));
-
3620 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(1000, 0)));
-
3621 {
-
3622 testcase("Scale clawback exact");
-
3623 // assetsToSharesWithdraw:
-
3624 // shares = sharesTotal * (assets / assetsTotal)
-
3625 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
-
3626 // sharesToAssetsWithdraw:
-
3627 // assets = assetsTotal * (shares / sharesTotal)
-
3628 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
-
3629
-
3630 auto const start = env.balance(d.depositor, d.assets).number();
-
3631 auto tx = d.vault.clawback(
-
3632 {.issuer = d.issuer,
-
3633 .id = d.keylet.key,
-
3634 .holder = d.depositor,
-
3635 .amount = STAmount(d.asset, Number(10, 0))});
-
3636 env(tx);
-
3637 env.close();
-
3638 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
-
3639 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
-
3640 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0)));
-
3641 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900, 0)));
-
3642 }
-
3643
-
3644 {
-
3645 testcase("Scale clawback insignificant amount");
-
3646 auto tx = d.vault.clawback(
-
3647 {.issuer = d.issuer,
-
3648 .id = d.keylet.key,
-
3649 .holder = d.depositor,
-
3650 .amount = STAmount(d.asset, Number(4, -2))});
-
3651 env(tx, ter{tecPRECISION_LOSS});
-
3652 }
-
3653
-
3654 {
-
3655 testcase("Scale clawback with rounding assets");
-
3656 // assetsToSharesWithdraw:
-
3657 // shares = sharesTotal * (assets / assetsTotal)
-
3658 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
-
3659 // sharesToAssetsWithdraw:
-
3660 // assets = assetsTotal * (shares / sharesTotal)
-
3661 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
-
3662
-
3663 auto const start = env.balance(d.depositor, d.assets).number();
-
3664 auto tx = d.vault.clawback(
-
3665 {.issuer = d.issuer,
-
3666 .id = d.keylet.key,
-
3667 .holder = d.depositor,
-
3668 .amount = STAmount(d.asset, Number(25, -1))});
-
3669 env(tx);
-
3670 env.close();
-
3671 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
-
3672 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
-
3673 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(900 - 25, -1)));
-
3674 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900 - 25, 0)));
-
3675 }
-
3676
-
3677 {
-
3678 testcase("Scale clawback with rounding shares up");
-
3679 // assetsToSharesWithdraw:
-
3680 // shares = sharesTotal * (assets / assetsTotal)
-
3681 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
-
3682 // sharesToAssetsWithdraw:
-
3683 // assets = assetsTotal * (shares / sharesTotal)
-
3684 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
-
3685
-
3686 auto const start = env.balance(d.depositor, d.assets).number();
-
3687 auto tx = d.vault.clawback(
-
3688 {.issuer = d.issuer,
-
3689 .id = d.keylet.key,
-
3690 .holder = d.depositor,
-
3691 .amount = STAmount(d.asset, Number(375, -2))});
-
3692 env(tx);
-
3693 env.close();
-
3694 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
-
3695 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
-
3696 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(875 - 38, -1)));
-
3697 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(875 - 38, 0)));
-
3698 }
-
3699
-
3700 {
-
3701 testcase("Scale clawback with rounding shares down");
-
3702 // assetsToSharesWithdraw:
-
3703 // shares = sharesTotal * (assets / assetsTotal)
-
3704 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
-
3705 // sharesToAssetsWithdraw:
-
3706 // assets = assetsTotal * (shares / sharesTotal)
-
3707 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
-
3708
-
3709 auto const start = env.balance(d.depositor, d.assets).number();
-
3710 auto tx = d.vault.clawback(
-
3711 {.issuer = d.issuer,
-
3712 .id = d.keylet.key,
-
3713 .holder = d.depositor,
-
3714 .amount = STAmount(d.asset, Number(372, -2))});
-
3715 env(tx);
-
3716 env.close();
-
3717 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
-
3718 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
-
3719 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(837 - 37, -1)));
-
3720 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(837 - 37, 0)));
-
3721 }
-
3722
-
3723 {
-
3724 testcase("Scale clawback tiny amount");
-
3725
-
3726 auto const start = env.balance(d.depositor, d.assets).number();
-
3727 auto tx = d.vault.clawback(
-
3728 {.issuer = d.issuer,
-
3729 .id = d.keylet.key,
-
3730 .holder = d.depositor,
-
3731 .amount = STAmount(d.asset, Number(9, -2))});
-
3732 env(tx);
-
3733 env.close();
-
3734 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
-
3735 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
-
3736 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(800 - 1, -1)));
-
3737 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(800 - 1, 0)));
-
3738 }
-
3739
-
3740 {
-
3741 testcase("Scale clawback rest");
-
3742 auto const rest = env.balance(d.vaultAccount, d.assets).number();
-
3743 d.peek([](SLE& vault, auto&) -> bool {
-
3744 vault[sfAssetsAvailable] = Number(5);
-
3745 return true;
-
3746 });
-
3747
-
3748 // Note, this transaction yields two different results:
-
3749 // * in the open ledger, with AssetsAvailable = 5
-
3750 // * when the ledger is closed with unmodified AssetsAvailable
-
3751 // because a modification like above is not persistent.
-
3752 tx = d.vault.clawback(
-
3753 {.issuer = d.issuer, .id = d.keylet.key, .holder = d.depositor, .amount = STAmount(d.asset, rest)});
-
3754 env(tx);
-
3755 env.close();
-
3756 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
-
3757 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
-
3758 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
-
3759 }
-
3760 });
-
3761 }
+
3413 auto const start = env.balance(d.depositor, d.assets).number();
+
3414
+
3415 tx = d.vault.withdraw(
+
3416 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, Number(21, 0))});
+
3417 env(tx);
+
3418 env.close();
+
3419 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 21));
+
3420 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(21, -1)));
+
3421 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(875 - 21, -1)));
+
3422 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(875 - 21, 0)));
+
3423 }
+
3424
+
3425 {
+
3426 testcase("Scale redeem rest");
+
3427 auto const rest = env.balance(d.depositor, d.shares).number();
+
3428
+
3429 tx =
+
3430 d.vault.withdraw({.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.share, rest)});
+
3431 env(tx);
+
3432 env.close();
+
3433 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
3434 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
+
3435 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
+
3436 }
+
3437 });
+
3438
+
3439 testCase(18, [&, this](Env& env, Data d) {
+
3440 testcase("Scale withdraw overflow");
+
3441
+
3442 {
+
3443 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
+
3444 env(tx);
+
3445 env.close();
+
3446 }
+
3447
+
3448 {
+
3449 auto tx = d.vault.withdraw(
+
3450 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(10, 0))});
+
3451 env(tx, ter{tecPATH_DRY});
+
3452 env.close();
+
3453 }
+
3454 });
+
3455
+
3456 testCase(1, [&, this](Env& env, Data d) {
+
3457 // initial setup: deposit 100 IOU, receive 1000 shares
+
3458 auto const start = env.balance(d.depositor, d.assets).number();
+
3459 auto tx = d.vault.deposit(
+
3460 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(100, 0))});
+
3461 env(tx);
+
3462 env.close();
+
3463 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
+
3464 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0)));
+
3465 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0)));
+
3466 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-1000, 0)));
+
3467
+
3468 {
+
3469 testcase("Scale withdraw exact");
+
3470 // assetsToSharesWithdraw:
+
3471 // shares = sharesTotal * (assets / assetsTotal)
+
3472 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
+
3473 // sharesToAssetsWithdraw:
+
3474 // assets = assetsTotal * (shares / sharesTotal)
+
3475 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
+
3476
+
3477 auto const start = env.balance(d.depositor, d.assets).number();
+
3478 auto tx = d.vault.withdraw(
+
3479 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(10, 0))});
+
3480 env(tx);
+
3481 env.close();
+
3482 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
+
3483 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(10, 0)));
+
3484 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0)));
+
3485 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, Number(-900, 0)));
+
3486 }
+
3487
+
3488 {
+
3489 testcase("Scale withdraw insignificant amount");
+
3490 auto tx = d.vault.withdraw(
+
3491 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(4, -2))});
+
3492 env(tx, ter{tecPRECISION_LOSS});
+
3493 }
+
3494
+
3495 {
+
3496 testcase("Scale withdraw with rounding assets");
+
3497 // assetsToSharesWithdraw:
+
3498 // shares = sharesTotal * (assets / assetsTotal)
+
3499 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
+
3500 // sharesToAssetsWithdraw:
+
3501 // assets = assetsTotal * (shares / sharesTotal)
+
3502 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
+
3503
+
3504 auto const start = env.balance(d.depositor, d.assets).number();
+
3505 d.peek([](SLE& vault, auto&) -> bool {
+
3506 vault[sfAssetsAvailable] = Number(1);
+
3507 return true;
+
3508 });
+
3509
+
3510 // Note, this transaction fails first (because of above change
+
3511 // in the open ledger) but then succeeds when the ledger is
+
3512 // closed (because a modification like above is not persistent),
+
3513 // which is why the checks below are expected to pass.
+
3514 auto tx = d.vault.withdraw(
+
3515 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(25, -1))});
+
3516 env(tx, ter{tecINSUFFICIENT_FUNDS});
+
3517 env.close();
+
3518 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
+
3519 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(25, -1)));
+
3520 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(900 - 25, -1)));
+
3521 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900 - 25, 0)));
+
3522 }
+
3523
+
3524 {
+
3525 testcase("Scale withdraw with rounding shares up");
+
3526 // assetsToSharesWithdraw:
+
3527 // shares = sharesTotal * (assets / assetsTotal)
+
3528 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
+
3529 // sharesToAssetsWithdraw:
+
3530 // assets = assetsTotal * (shares / sharesTotal)
+
3531 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
+
3532
+
3533 auto const start = env.balance(d.depositor, d.assets).number();
+
3534 auto tx = d.vault.withdraw(
+
3535 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(375, -2))});
+
3536 env(tx);
+
3537 env.close();
+
3538 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
+
3539 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(38, -1)));
+
3540 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(875 - 38, -1)));
+
3541 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(875 - 38, 0)));
+
3542 }
+
3543
+
3544 {
+
3545 testcase("Scale withdraw with rounding shares down");
+
3546 // assetsToSharesWithdraw:
+
3547 // shares = sharesTotal * (assets / assetsTotal)
+
3548 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
+
3549 // sharesToAssetsWithdraw:
+
3550 // assets = assetsTotal * (shares / sharesTotal)
+
3551 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
+
3552
+
3553 auto const start = env.balance(d.depositor, d.assets).number();
+
3554 auto tx = d.vault.withdraw(
+
3555 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(372, -2))});
+
3556 env(tx);
+
3557 env.close();
+
3558 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
+
3559 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(37, -1)));
+
3560 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(837 - 37, -1)));
+
3561 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(837 - 37, 0)));
+
3562 }
+
3563
+
3564 {
+
3565 testcase("Scale withdraw tiny amount");
+
3566
+
3567 auto const start = env.balance(d.depositor, d.assets).number();
+
3568 auto tx = d.vault.withdraw(
+
3569 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(9, -2))});
+
3570 env(tx);
+
3571 env.close();
+
3572 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
+
3573 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start + Number(1, -1)));
+
3574 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(800 - 1, -1)));
+
3575 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(800 - 1, 0)));
+
3576 }
+
3577
+
3578 {
+
3579 testcase("Scale withdraw rest");
+
3580 auto const rest = env.balance(d.vaultAccount, d.assets).number();
+
3581
+
3582 tx =
+
3583 d.vault.withdraw({.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, rest)});
+
3584 env(tx);
+
3585 env.close();
+
3586 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
3587 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
+
3588 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
+
3589 }
+
3590 });
+
3591
+
3592 testCase(18, [&, this](Env& env, Data d) {
+
3593 testcase("Scale clawback overflow");
+
3594
+
3595 {
+
3596 auto tx = d.vault.deposit({.depositor = d.depositor, .id = d.keylet.key, .amount = d.asset(5)});
+
3597 env(tx);
+
3598 env.close();
+
3599 }
+
3600
+
3601 {
+
3602 auto tx = d.vault.clawback(
+
3603 {.issuer = d.issuer,
+
3604 .id = d.keylet.key,
+
3605 .holder = d.depositor,
+
3606 .amount = STAmount(d.asset, Number(10, 0))});
+
3607 env(tx, ter{tecPATH_DRY});
+
3608 env.close();
+
3609 }
+
3610 });
+
3611
+
3612 testCase(1, [&, this](Env& env, Data d) {
+
3613 // initial setup: deposit 100 IOU, receive 1000 shares
+
3614 auto const start = env.balance(d.depositor, d.assets).number();
+
3615 auto tx = d.vault.deposit(
+
3616 {.depositor = d.depositor, .id = d.keylet.key, .amount = STAmount(d.asset, Number(100, 0))});
+
3617 env(tx);
+
3618 env.close();
+
3619 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000));
+
3620 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start - Number(100, 0)));
+
3621 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(100, 0)));
+
3622 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(1000, 0)));
+
3623 {
+
3624 testcase("Scale clawback exact");
+
3625 // assetsToSharesWithdraw:
+
3626 // shares = sharesTotal * (assets / assetsTotal)
+
3627 // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100
+
3628 // sharesToAssetsWithdraw:
+
3629 // assets = assetsTotal * (shares / sharesTotal)
+
3630 // assets = 100 * 100 / 1000 = 100 * 0.1 = 10
+
3631
+
3632 auto const start = env.balance(d.depositor, d.assets).number();
+
3633 auto tx = d.vault.clawback(
+
3634 {.issuer = d.issuer,
+
3635 .id = d.keylet.key,
+
3636 .holder = d.depositor,
+
3637 .amount = STAmount(d.asset, Number(10, 0))});
+
3638 env(tx);
+
3639 env.close();
+
3640 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900));
+
3641 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
+
3642 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(90, 0)));
+
3643 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900, 0)));
+
3644 }
+
3645
+
3646 {
+
3647 testcase("Scale clawback insignificant amount");
+
3648 auto tx = d.vault.clawback(
+
3649 {.issuer = d.issuer,
+
3650 .id = d.keylet.key,
+
3651 .holder = d.depositor,
+
3652 .amount = STAmount(d.asset, Number(4, -2))});
+
3653 env(tx, ter{tecPRECISION_LOSS});
+
3654 }
+
3655
+
3656 {
+
3657 testcase("Scale clawback with rounding assets");
+
3658 // assetsToSharesWithdraw:
+
3659 // shares = sharesTotal * (assets / assetsTotal)
+
3660 // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25
+
3661 // sharesToAssetsWithdraw:
+
3662 // assets = assetsTotal * (shares / sharesTotal)
+
3663 // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5
+
3664
+
3665 auto const start = env.balance(d.depositor, d.assets).number();
+
3666 auto tx = d.vault.clawback(
+
3667 {.issuer = d.issuer,
+
3668 .id = d.keylet.key,
+
3669 .holder = d.depositor,
+
3670 .amount = STAmount(d.asset, Number(25, -1))});
+
3671 env(tx);
+
3672 env.close();
+
3673 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(900 - 25));
+
3674 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
+
3675 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(900 - 25, -1)));
+
3676 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(900 - 25, 0)));
+
3677 }
+
3678
+
3679 {
+
3680 testcase("Scale clawback with rounding shares up");
+
3681 // assetsToSharesWithdraw:
+
3682 // shares = sharesTotal * (assets / assetsTotal)
+
3683 // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5
+
3684 // sharesToAssetsWithdraw:
+
3685 // assets = assetsTotal * (shares / sharesTotal)
+
3686 // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8
+
3687
+
3688 auto const start = env.balance(d.depositor, d.assets).number();
+
3689 auto tx = d.vault.clawback(
+
3690 {.issuer = d.issuer,
+
3691 .id = d.keylet.key,
+
3692 .holder = d.depositor,
+
3693 .amount = STAmount(d.asset, Number(375, -2))});
+
3694 env(tx);
+
3695 env.close();
+
3696 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(875 - 38));
+
3697 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
+
3698 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(875 - 38, -1)));
+
3699 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(875 - 38, 0)));
+
3700 }
+
3701
+
3702 {
+
3703 testcase("Scale clawback with rounding shares down");
+
3704 // assetsToSharesWithdraw:
+
3705 // shares = sharesTotal * (assets / assetsTotal)
+
3706 // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2
+
3707 // sharesToAssetsWithdraw:
+
3708 // assets = assetsTotal * (shares / sharesTotal)
+
3709 // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7
+
3710
+
3711 auto const start = env.balance(d.depositor, d.assets).number();
+
3712 auto tx = d.vault.clawback(
+
3713 {.issuer = d.issuer,
+
3714 .id = d.keylet.key,
+
3715 .holder = d.depositor,
+
3716 .amount = STAmount(d.asset, Number(372, -2))});
+
3717 env(tx);
+
3718 env.close();
+
3719 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(837 - 37));
+
3720 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
+
3721 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(837 - 37, -1)));
+
3722 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(837 - 37, 0)));
+
3723 }
+
3724
+
3725 {
+
3726 testcase("Scale clawback tiny amount");
+
3727
+
3728 auto const start = env.balance(d.depositor, d.assets).number();
+
3729 auto tx = d.vault.clawback(
+
3730 {.issuer = d.issuer,
+
3731 .id = d.keylet.key,
+
3732 .holder = d.depositor,
+
3733 .amount = STAmount(d.asset, Number(9, -2))});
+
3734 env(tx);
+
3735 env.close();
+
3736 BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(800 - 1));
+
3737 BEAST_EXPECT(env.balance(d.depositor, d.assets) == STAmount(d.asset, start));
+
3738 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets) == STAmount(d.asset, Number(800 - 1, -1)));
+
3739 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares) == STAmount(d.share, -Number(800 - 1, 0)));
+
3740 }
+
3741
+
3742 {
+
3743 testcase("Scale clawback rest");
+
3744 auto const rest = env.balance(d.vaultAccount, d.assets).number();
+
3745 d.peek([](SLE& vault, auto&) -> bool {
+
3746 vault[sfAssetsAvailable] = Number(5);
+
3747 return true;
+
3748 });
+
3749
+
3750 // Note, this transaction yields two different results:
+
3751 // * in the open ledger, with AssetsAvailable = 5
+
3752 // * when the ledger is closed with unmodified AssetsAvailable
+
3753 // because a modification like above is not persistent.
+
3754 tx = d.vault.clawback(
+
3755 {.issuer = d.issuer, .id = d.keylet.key, .holder = d.depositor, .amount = STAmount(d.asset, rest)});
+
3756 env(tx);
+
3757 env.close();
+
3758 BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0);
+
3759 BEAST_EXPECT(env.balance(d.vaultAccount, d.assets).number() == 0);
+
3760 BEAST_EXPECT(env.balance(d.vaultAccount, d.shares).number() == 0);
+
3761 }
+
3762 });
+
3763 }
-
3762
-
3763 void
-
- -
3765 {
-
3766 using namespace test::jtx;
-
3767
-
3768 testcase("RPC");
-
3769 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
3770 Account const owner{"owner"};
-
3771 Account const issuer{"issuer"};
-
3772 Vault vault{env};
-
3773 env.fund(XRP(1000), issuer, owner);
-
3774 env.close();
-
3775
-
3776 PrettyAsset asset = issuer["IOU"];
-
3777 env.trust(asset(1000), owner);
-
3778 env(pay(issuer, owner, asset(200)));
-
3779 env.close();
-
3780
-
3781 auto const sequence = env.seq(owner);
-
3782 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
-
3783 env(tx);
-
3784 env.close();
-
3785
-
3786 // Set some fields
-
3787 {
-
3788 auto tx1 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(50)});
-
3789 env(tx1);
-
3790
-
3791 auto tx2 = vault.set({.owner = owner, .id = keylet.key});
-
3792 tx2[sfAssetsMaximum] = asset(1000).number();
-
3793 env(tx2);
-
3794 env.close();
-
3795 }
-
3796
-
3797 auto const sleVault = [&env, keylet = keylet, this]() {
-
3798 auto const vault = env.le(keylet);
-
3799 BEAST_EXPECT(vault != nullptr);
-
3800 return vault;
-
3801 }();
-
3802
-
3803 auto const check = [&, keylet = keylet, sle = sleVault, this](
-
3804 Json::Value const& vault, Json::Value const& issuance = Json::nullValue) {
-
3805 BEAST_EXPECT(vault.isObject());
-
3806
-
3807 constexpr auto checkString = [](auto& node, SField const& field, std::string v) -> bool {
-
3808 return node.isMember(field.fieldName) && node[field.fieldName].isString() && node[field.fieldName] == v;
-
3809 };
-
3810 constexpr auto checkObject = [](auto& node, SField const& field, Json::Value v) -> bool {
-
3811 return node.isMember(field.fieldName) && node[field.fieldName].isObject() && node[field.fieldName] == v;
-
3812 };
-
3813 constexpr auto checkInt = [](auto& node, SField const& field, int v) -> bool {
-
3814 return node.isMember(field.fieldName) &&
-
3815 ((node[field.fieldName].isInt() && node[field.fieldName] == Json::Int(v)) ||
-
3816 (node[field.fieldName].isUInt() && node[field.fieldName] == Json::UInt(v)));
-
3817 };
-
3818
-
3819 BEAST_EXPECT(vault["LedgerEntryType"].asString() == "Vault");
-
3820 BEAST_EXPECT(vault[jss::index].asString() == strHex(keylet.key));
-
3821 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
-
3822 // Ignore all other standard fields, this test doesn't care
-
3823
-
3824 BEAST_EXPECT(checkString(vault, sfAccount, toBase58(sle->at(sfAccount))));
-
3825 BEAST_EXPECT(checkObject(vault, sfAsset, to_json(sle->at(sfAsset))));
-
3826 BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
-
3827 BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
-
3828 BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
-
3829 BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
-
3830
-
3831 auto const strShareID = strHex(sle->at(sfShareMPTID));
-
3832 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
-
3833 BEAST_EXPECT(checkString(vault, sfOwner, toBase58(owner.id())));
-
3834 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
-
3835 BEAST_EXPECT(checkInt(vault, sfWithdrawalPolicy, vaultStrategyFirstComeFirstServe));
-
3836
-
3837 if (issuance.isObject())
-
3838 {
-
3839 BEAST_EXPECT(issuance["LedgerEntryType"].asString() == "MPTokenIssuance");
-
3840 BEAST_EXPECT(issuance[jss::mpt_issuance_id].asString() == strShareID);
-
3841 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
-
3842 BEAST_EXPECT(checkInt(issuance, sfFlags, int(lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer)));
-
3843 BEAST_EXPECT(checkString(issuance, sfOutstandingAmount, "50000000"));
-
3844 }
-
3845 };
-
3846
-
3847 {
-
3848 testcase("RPC ledger_entry selected by key");
-
3849 Json::Value jvParams;
-
3850 jvParams[jss::ledger_index] = jss::validated;
-
3851 jvParams[jss::vault] = strHex(keylet.key);
-
3852 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3853
-
3854 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
-
3855 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
-
3856 check(jvVault[jss::result][jss::node]);
-
3857 }
-
3858
-
3859 {
-
3860 testcase("RPC ledger_entry selected by owner and seq");
-
3861 Json::Value jvParams;
-
3862 jvParams[jss::ledger_index] = jss::validated;
-
3863 jvParams[jss::vault][jss::owner] = owner.human();
-
3864 jvParams[jss::vault][jss::seq] = sequence;
-
3865 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3866
-
3867 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
-
3868 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
-
3869 check(jvVault[jss::result][jss::node]);
-
3870 }
-
3871
-
3872 {
-
3873 testcase("RPC ledger_entry cannot find vault by key");
-
3874 Json::Value jvParams;
-
3875 jvParams[jss::ledger_index] = jss::validated;
-
3876 jvParams[jss::vault] = to_string(uint256(42));
-
3877 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3878 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "entryNotFound");
-
3879 }
-
3880
-
3881 {
-
3882 testcase("RPC ledger_entry cannot find vault by owner and seq");
-
3883 Json::Value jvParams;
-
3884 jvParams[jss::ledger_index] = jss::validated;
-
3885 jvParams[jss::vault][jss::owner] = issuer.human();
-
3886 jvParams[jss::vault][jss::seq] = 1'000'000;
-
3887 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3888 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "entryNotFound");
-
3889 }
-
3890
-
3891 {
-
3892 testcase("RPC ledger_entry malformed key");
-
3893 Json::Value jvParams;
-
3894 jvParams[jss::ledger_index] = jss::validated;
-
3895 jvParams[jss::vault] = 42;
-
3896 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3897 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
-
3898 }
-
3899
-
3900 {
-
3901 testcase("RPC ledger_entry malformed owner");
-
3902 Json::Value jvParams;
-
3903 jvParams[jss::ledger_index] = jss::validated;
-
3904 jvParams[jss::vault][jss::owner] = 42;
-
3905 jvParams[jss::vault][jss::seq] = sequence;
-
3906 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3907 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedOwner");
-
3908 }
-
3909
-
3910 {
-
3911 testcase("RPC ledger_entry malformed seq");
-
3912 Json::Value jvParams;
-
3913 jvParams[jss::ledger_index] = jss::validated;
-
3914 jvParams[jss::vault][jss::owner] = issuer.human();
-
3915 jvParams[jss::vault][jss::seq] = "foo";
-
3916 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3917 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
-
3918 }
-
3919
-
3920 {
-
3921 testcase("RPC ledger_entry negative seq");
-
3922 Json::Value jvParams;
-
3923 jvParams[jss::ledger_index] = jss::validated;
-
3924 jvParams[jss::vault][jss::owner] = issuer.human();
-
3925 jvParams[jss::vault][jss::seq] = -1;
-
3926 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3927 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
-
3928 }
-
3929
-
3930 {
-
3931 testcase("RPC ledger_entry oversized seq");
-
3932 Json::Value jvParams;
-
3933 jvParams[jss::ledger_index] = jss::validated;
-
3934 jvParams[jss::vault][jss::owner] = issuer.human();
-
3935 jvParams[jss::vault][jss::seq] = 1e20;
-
3936 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3937 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
-
3938 }
-
3939
-
3940 {
-
3941 testcase("RPC ledger_entry bool seq");
-
3942 Json::Value jvParams;
-
3943 jvParams[jss::ledger_index] = jss::validated;
-
3944 jvParams[jss::vault][jss::owner] = issuer.human();
-
3945 jvParams[jss::vault][jss::seq] = true;
-
3946 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
-
3947 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
-
3948 }
-
3949
-
3950 {
-
3951 testcase("RPC account_objects");
-
3952
-
3953 Json::Value jvParams;
-
3954 jvParams[jss::account] = owner.human();
-
3955 jvParams[jss::type] = jss::vault;
-
3956 auto jv = env.rpc("json", "account_objects", to_string(jvParams))[jss::result];
-
3957
-
3958 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
-
3959 check(jv[jss::account_objects][0u]);
-
3960 }
-
3961
-
3962 {
-
3963 testcase("RPC ledger_data");
-
3964
-
3965 Json::Value jvParams;
-
3966 jvParams[jss::ledger_index] = jss::validated;
-
3967 jvParams[jss::binary] = false;
-
3968 jvParams[jss::type] = jss::vault;
-
3969 Json::Value jv = env.rpc("json", "ledger_data", to_string(jvParams));
-
3970 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
-
3971 check(jv[jss::result][jss::state][0u]);
-
3972 }
-
3973
-
3974 {
-
3975 testcase("RPC vault_info command line");
-
3976 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "validated");
-
3977
-
3978 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
-
3979 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
-
3980 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
-
3981 }
-
3982
-
3983 {
-
3984 testcase("RPC vault_info json");
-
3985 Json::Value jvParams;
-
3986 jvParams[jss::ledger_index] = jss::validated;
-
3987 jvParams[jss::vault_id] = strHex(keylet.key);
-
3988 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
3989
-
3990 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
-
3991 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
-
3992 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
-
3993 }
-
3994
-
3995 {
-
3996 testcase("RPC vault_info invalid vault_id");
-
3997 Json::Value jvParams;
-
3998 jvParams[jss::ledger_index] = jss::validated;
-
3999 jvParams[jss::vault_id] = "foobar";
-
4000 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4001 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4002 }
-
4003
-
4004 {
-
4005 testcase("RPC vault_info json invalid index");
-
4006 Json::Value jvParams;
-
4007 jvParams[jss::ledger_index] = jss::validated;
-
4008 jvParams[jss::vault_id] = 0;
-
4009 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4010 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4011 }
-
4012
-
4013 {
-
4014 testcase("RPC vault_info json by owner and sequence");
-
4015 Json::Value jvParams;
-
4016 jvParams[jss::ledger_index] = jss::validated;
-
4017 jvParams[jss::owner] = owner.human();
-
4018 jvParams[jss::seq] = sequence;
-
4019 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4020
-
4021 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
-
4022 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
-
4023 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
-
4024 }
-
4025
-
4026 {
-
4027 testcase("RPC vault_info json malformed sequence");
-
4028 Json::Value jvParams;
-
4029 jvParams[jss::ledger_index] = jss::validated;
-
4030 jvParams[jss::owner] = owner.human();
-
4031 jvParams[jss::seq] = "foobar";
-
4032 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4033 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4034 }
-
4035
-
4036 {
-
4037 testcase("RPC vault_info json invalid sequence");
-
4038 Json::Value jvParams;
-
4039 jvParams[jss::ledger_index] = jss::validated;
-
4040 jvParams[jss::owner] = owner.human();
-
4041 jvParams[jss::seq] = 0;
-
4042 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4043 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4044 }
-
4045
-
4046 {
-
4047 testcase("RPC vault_info json negative sequence");
-
4048 Json::Value jvParams;
-
4049 jvParams[jss::ledger_index] = jss::validated;
-
4050 jvParams[jss::owner] = owner.human();
-
4051 jvParams[jss::seq] = -1;
-
4052 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4053 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4054 }
-
4055
-
4056 {
-
4057 testcase("RPC vault_info json oversized sequence");
-
4058 Json::Value jvParams;
-
4059 jvParams[jss::ledger_index] = jss::validated;
-
4060 jvParams[jss::owner] = owner.human();
-
4061 jvParams[jss::seq] = 1e20;
-
4062 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4063 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4064 }
-
4065
-
4066 {
-
4067 testcase("RPC vault_info json bool sequence");
-
4068 Json::Value jvParams;
-
4069 jvParams[jss::ledger_index] = jss::validated;
-
4070 jvParams[jss::owner] = owner.human();
-
4071 jvParams[jss::seq] = true;
-
4072 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4073 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4074 }
-
4075
-
4076 {
-
4077 testcase("RPC vault_info json malformed owner");
-
4078 Json::Value jvParams;
-
4079 jvParams[jss::ledger_index] = jss::validated;
-
4080 jvParams[jss::owner] = "foobar";
-
4081 jvParams[jss::seq] = sequence;
-
4082 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4083 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4084 }
-
4085
-
4086 {
-
4087 testcase("RPC vault_info json invalid combination only owner");
-
4088 Json::Value jvParams;
-
4089 jvParams[jss::ledger_index] = jss::validated;
-
4090 jvParams[jss::owner] = owner.human();
-
4091 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4092 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4093 }
-
4094
-
4095 {
-
4096 testcase("RPC vault_info json invalid combination only seq");
-
4097 Json::Value jvParams;
-
4098 jvParams[jss::ledger_index] = jss::validated;
-
4099 jvParams[jss::seq] = sequence;
-
4100 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4101 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4102 }
-
4103
-
4104 {
-
4105 testcase("RPC vault_info json invalid combination seq vault_id");
-
4106 Json::Value jvParams;
-
4107 jvParams[jss::ledger_index] = jss::validated;
-
4108 jvParams[jss::vault_id] = strHex(keylet.key);
-
4109 jvParams[jss::seq] = sequence;
-
4110 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4111 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4112 }
-
4113
-
4114 {
-
4115 testcase("RPC vault_info json invalid combination owner vault_id");
-
4116 Json::Value jvParams;
-
4117 jvParams[jss::ledger_index] = jss::validated;
-
4118 jvParams[jss::vault_id] = strHex(keylet.key);
-
4119 jvParams[jss::owner] = owner.human();
-
4120 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4121 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4122 }
-
4123
-
4124 {
-
4125 testcase(
-
4126 "RPC vault_info json invalid combination owner seq "
-
4127 "vault_id");
-
4128 Json::Value jvParams;
-
4129 jvParams[jss::ledger_index] = jss::validated;
-
4130 jvParams[jss::vault_id] = strHex(keylet.key);
-
4131 jvParams[jss::seq] = sequence;
-
4132 jvParams[jss::owner] = owner.human();
-
4133 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4134 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4135 }
-
4136
-
4137 {
-
4138 testcase("RPC vault_info json no input");
-
4139 Json::Value jvParams;
-
4140 jvParams[jss::ledger_index] = jss::validated;
-
4141 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
-
4142 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4143 }
-
4144
-
4145 {
-
4146 testcase("RPC vault_info command line invalid index");
-
4147 Json::Value jv = env.rpc("vault_info", "foobar", "validated");
-
4148 BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
-
4149 }
-
4150
-
4151 {
-
4152 testcase("RPC vault_info command line invalid index");
-
4153 Json::Value jv = env.rpc("vault_info", "0", "validated");
-
4154 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
-
4155 }
-
4156
-
4157 {
-
4158 testcase("RPC vault_info command line invalid index");
-
4159 Json::Value jv = env.rpc("vault_info", strHex(uint256(42)), "validated");
-
4160 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "entryNotFound");
-
4161 }
-
4162
-
4163 {
-
4164 testcase("RPC vault_info command line invalid ledger");
-
4165 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
-
4166 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "lgrNotFound");
-
4167 }
-
4168 }
+
3764
+
3765 void
+
+ +
3767 {
+
3768 using namespace test::jtx;
+
3769
+
3770 testcase("RPC");
+
3771 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
3772 Account const owner{"owner"};
+
3773 Account const issuer{"issuer"};
+
3774 Vault vault{env};
+
3775 env.fund(XRP(1000), issuer, owner);
+
3776 env.close();
+
3777
+
3778 PrettyAsset asset = issuer["IOU"];
+
3779 env.trust(asset(1000), owner);
+
3780 env(pay(issuer, owner, asset(200)));
+
3781 env.close();
+
3782
+
3783 auto const sequence = env.seq(owner);
+
3784 auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
+
3785 env(tx);
+
3786 env.close();
+
3787
+
3788 // Set some fields
+
3789 {
+
3790 auto tx1 = vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(50)});
+
3791 env(tx1);
+
3792
+
3793 auto tx2 = vault.set({.owner = owner, .id = keylet.key});
+
3794 tx2[sfAssetsMaximum] = asset(1000).number();
+
3795 env(tx2);
+
3796 env.close();
+
3797 }
+
3798
+
3799 auto const sleVault = [&env, keylet = keylet, this]() {
+
3800 auto const vault = env.le(keylet);
+
3801 BEAST_EXPECT(vault != nullptr);
+
3802 return vault;
+
3803 }();
+
3804
+
3805 auto const check = [&, keylet = keylet, sle = sleVault, this](
+
3806 Json::Value const& vault, Json::Value const& issuance = Json::nullValue) {
+
3807 BEAST_EXPECT(vault.isObject());
+
3808
+
3809 constexpr auto checkString = [](auto& node, SField const& field, std::string v) -> bool {
+
3810 return node.isMember(field.fieldName) && node[field.fieldName].isString() && node[field.fieldName] == v;
+
3811 };
+
3812 constexpr auto checkObject = [](auto& node, SField const& field, Json::Value v) -> bool {
+
3813 return node.isMember(field.fieldName) && node[field.fieldName].isObject() && node[field.fieldName] == v;
+
3814 };
+
3815 constexpr auto checkInt = [](auto& node, SField const& field, int v) -> bool {
+
3816 return node.isMember(field.fieldName) &&
+
3817 ((node[field.fieldName].isInt() && node[field.fieldName] == Json::Int(v)) ||
+
3818 (node[field.fieldName].isUInt() && node[field.fieldName] == Json::UInt(v)));
+
3819 };
+
3820
+
3821 BEAST_EXPECT(vault["LedgerEntryType"].asString() == "Vault");
+
3822 BEAST_EXPECT(vault[jss::index].asString() == strHex(keylet.key));
+
3823 BEAST_EXPECT(checkInt(vault, sfFlags, 0));
+
3824 // Ignore all other standard fields, this test doesn't care
+
3825
+
3826 BEAST_EXPECT(checkString(vault, sfAccount, toBase58(sle->at(sfAccount))));
+
3827 BEAST_EXPECT(checkObject(vault, sfAsset, to_json(sle->at(sfAsset))));
+
3828 BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
+
3829 BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
+
3830 BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
+
3831 BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
+
3832
+
3833 auto const strShareID = strHex(sle->at(sfShareMPTID));
+
3834 BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
+
3835 BEAST_EXPECT(checkString(vault, sfOwner, toBase58(owner.id())));
+
3836 BEAST_EXPECT(checkInt(vault, sfSequence, sequence));
+
3837 BEAST_EXPECT(checkInt(vault, sfWithdrawalPolicy, vaultStrategyFirstComeFirstServe));
+
3838
+
3839 if (issuance.isObject())
+
3840 {
+
3841 BEAST_EXPECT(issuance["LedgerEntryType"].asString() == "MPTokenIssuance");
+
3842 BEAST_EXPECT(issuance[jss::mpt_issuance_id].asString() == strShareID);
+
3843 BEAST_EXPECT(checkInt(issuance, sfSequence, 1));
+
3844 BEAST_EXPECT(checkInt(issuance, sfFlags, int(lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer)));
+
3845 BEAST_EXPECT(checkString(issuance, sfOutstandingAmount, "50000000"));
+
3846 }
+
3847 };
+
3848
+
3849 {
+
3850 testcase("RPC ledger_entry selected by key");
+
3851 Json::Value jvParams;
+
3852 jvParams[jss::ledger_index] = jss::validated;
+
3853 jvParams[jss::vault] = strHex(keylet.key);
+
3854 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3855
+
3856 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
+
3857 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
+
3858 check(jvVault[jss::result][jss::node]);
+
3859 }
+
3860
+
3861 {
+
3862 testcase("RPC ledger_entry selected by owner and seq");
+
3863 Json::Value jvParams;
+
3864 jvParams[jss::ledger_index] = jss::validated;
+
3865 jvParams[jss::vault][jss::owner] = owner.human();
+
3866 jvParams[jss::vault][jss::seq] = sequence;
+
3867 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3868
+
3869 BEAST_EXPECT(!jvVault[jss::result].isMember(jss::error));
+
3870 BEAST_EXPECT(jvVault[jss::result].isMember(jss::node));
+
3871 check(jvVault[jss::result][jss::node]);
+
3872 }
+
3873
+
3874 {
+
3875 testcase("RPC ledger_entry cannot find vault by key");
+
3876 Json::Value jvParams;
+
3877 jvParams[jss::ledger_index] = jss::validated;
+
3878 jvParams[jss::vault] = to_string(uint256(42));
+
3879 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3880 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "entryNotFound");
+
3881 }
+
3882
+
3883 {
+
3884 testcase("RPC ledger_entry cannot find vault by owner and seq");
+
3885 Json::Value jvParams;
+
3886 jvParams[jss::ledger_index] = jss::validated;
+
3887 jvParams[jss::vault][jss::owner] = issuer.human();
+
3888 jvParams[jss::vault][jss::seq] = 1'000'000;
+
3889 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3890 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "entryNotFound");
+
3891 }
+
3892
+
3893 {
+
3894 testcase("RPC ledger_entry malformed key");
+
3895 Json::Value jvParams;
+
3896 jvParams[jss::ledger_index] = jss::validated;
+
3897 jvParams[jss::vault] = 42;
+
3898 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3899 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
+
3900 }
+
3901
+
3902 {
+
3903 testcase("RPC ledger_entry malformed owner");
+
3904 Json::Value jvParams;
+
3905 jvParams[jss::ledger_index] = jss::validated;
+
3906 jvParams[jss::vault][jss::owner] = 42;
+
3907 jvParams[jss::vault][jss::seq] = sequence;
+
3908 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3909 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedOwner");
+
3910 }
+
3911
+
3912 {
+
3913 testcase("RPC ledger_entry malformed seq");
+
3914 Json::Value jvParams;
+
3915 jvParams[jss::ledger_index] = jss::validated;
+
3916 jvParams[jss::vault][jss::owner] = issuer.human();
+
3917 jvParams[jss::vault][jss::seq] = "foo";
+
3918 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3919 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
+
3920 }
+
3921
+
3922 {
+
3923 testcase("RPC ledger_entry negative seq");
+
3924 Json::Value jvParams;
+
3925 jvParams[jss::ledger_index] = jss::validated;
+
3926 jvParams[jss::vault][jss::owner] = issuer.human();
+
3927 jvParams[jss::vault][jss::seq] = -1;
+
3928 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3929 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
+
3930 }
+
3931
+
3932 {
+
3933 testcase("RPC ledger_entry oversized seq");
+
3934 Json::Value jvParams;
+
3935 jvParams[jss::ledger_index] = jss::validated;
+
3936 jvParams[jss::vault][jss::owner] = issuer.human();
+
3937 jvParams[jss::vault][jss::seq] = 1e20;
+
3938 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3939 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
+
3940 }
+
3941
+
3942 {
+
3943 testcase("RPC ledger_entry bool seq");
+
3944 Json::Value jvParams;
+
3945 jvParams[jss::ledger_index] = jss::validated;
+
3946 jvParams[jss::vault][jss::owner] = issuer.human();
+
3947 jvParams[jss::vault][jss::seq] = true;
+
3948 auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
+
3949 BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
+
3950 }
+
3951
+
3952 {
+
3953 testcase("RPC account_objects");
+
3954
+
3955 Json::Value jvParams;
+
3956 jvParams[jss::account] = owner.human();
+
3957 jvParams[jss::type] = jss::vault;
+
3958 auto jv = env.rpc("json", "account_objects", to_string(jvParams))[jss::result];
+
3959
+
3960 BEAST_EXPECT(jv[jss::account_objects].size() == 1);
+
3961 check(jv[jss::account_objects][0u]);
+
3962 }
+
3963
+
3964 {
+
3965 testcase("RPC ledger_data");
+
3966
+
3967 Json::Value jvParams;
+
3968 jvParams[jss::ledger_index] = jss::validated;
+
3969 jvParams[jss::binary] = false;
+
3970 jvParams[jss::type] = jss::vault;
+
3971 Json::Value jv = env.rpc("json", "ledger_data", to_string(jvParams));
+
3972 BEAST_EXPECT(jv[jss::result][jss::state].size() == 1);
+
3973 check(jv[jss::result][jss::state][0u]);
+
3974 }
+
3975
+
3976 {
+
3977 testcase("RPC vault_info command line");
+
3978 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "validated");
+
3979
+
3980 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
+
3981 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
+
3982 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
+
3983 }
+
3984
+
3985 {
+
3986 testcase("RPC vault_info json");
+
3987 Json::Value jvParams;
+
3988 jvParams[jss::ledger_index] = jss::validated;
+
3989 jvParams[jss::vault_id] = strHex(keylet.key);
+
3990 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
3991
+
3992 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
+
3993 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
+
3994 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
+
3995 }
+
3996
+
3997 {
+
3998 testcase("RPC vault_info invalid vault_id");
+
3999 Json::Value jvParams;
+
4000 jvParams[jss::ledger_index] = jss::validated;
+
4001 jvParams[jss::vault_id] = "foobar";
+
4002 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4003 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4004 }
+
4005
+
4006 {
+
4007 testcase("RPC vault_info json invalid index");
+
4008 Json::Value jvParams;
+
4009 jvParams[jss::ledger_index] = jss::validated;
+
4010 jvParams[jss::vault_id] = 0;
+
4011 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4012 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4013 }
+
4014
+
4015 {
+
4016 testcase("RPC vault_info json by owner and sequence");
+
4017 Json::Value jvParams;
+
4018 jvParams[jss::ledger_index] = jss::validated;
+
4019 jvParams[jss::owner] = owner.human();
+
4020 jvParams[jss::seq] = sequence;
+
4021 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4022
+
4023 BEAST_EXPECT(!jv[jss::result].isMember(jss::error));
+
4024 BEAST_EXPECT(jv[jss::result].isMember(jss::vault));
+
4025 check(jv[jss::result][jss::vault], jv[jss::result][jss::vault][jss::shares]);
+
4026 }
+
4027
+
4028 {
+
4029 testcase("RPC vault_info json malformed sequence");
+
4030 Json::Value jvParams;
+
4031 jvParams[jss::ledger_index] = jss::validated;
+
4032 jvParams[jss::owner] = owner.human();
+
4033 jvParams[jss::seq] = "foobar";
+
4034 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4035 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4036 }
+
4037
+
4038 {
+
4039 testcase("RPC vault_info json invalid sequence");
+
4040 Json::Value jvParams;
+
4041 jvParams[jss::ledger_index] = jss::validated;
+
4042 jvParams[jss::owner] = owner.human();
+
4043 jvParams[jss::seq] = 0;
+
4044 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4045 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4046 }
+
4047
+
4048 {
+
4049 testcase("RPC vault_info json negative sequence");
+
4050 Json::Value jvParams;
+
4051 jvParams[jss::ledger_index] = jss::validated;
+
4052 jvParams[jss::owner] = owner.human();
+
4053 jvParams[jss::seq] = -1;
+
4054 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4055 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4056 }
+
4057
+
4058 {
+
4059 testcase("RPC vault_info json oversized sequence");
+
4060 Json::Value jvParams;
+
4061 jvParams[jss::ledger_index] = jss::validated;
+
4062 jvParams[jss::owner] = owner.human();
+
4063 jvParams[jss::seq] = 1e20;
+
4064 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4065 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4066 }
+
4067
+
4068 {
+
4069 testcase("RPC vault_info json bool sequence");
+
4070 Json::Value jvParams;
+
4071 jvParams[jss::ledger_index] = jss::validated;
+
4072 jvParams[jss::owner] = owner.human();
+
4073 jvParams[jss::seq] = true;
+
4074 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4075 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4076 }
+
4077
+
4078 {
+
4079 testcase("RPC vault_info json malformed owner");
+
4080 Json::Value jvParams;
+
4081 jvParams[jss::ledger_index] = jss::validated;
+
4082 jvParams[jss::owner] = "foobar";
+
4083 jvParams[jss::seq] = sequence;
+
4084 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4085 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4086 }
+
4087
+
4088 {
+
4089 testcase("RPC vault_info json invalid combination only owner");
+
4090 Json::Value jvParams;
+
4091 jvParams[jss::ledger_index] = jss::validated;
+
4092 jvParams[jss::owner] = owner.human();
+
4093 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4094 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4095 }
+
4096
+
4097 {
+
4098 testcase("RPC vault_info json invalid combination only seq");
+
4099 Json::Value jvParams;
+
4100 jvParams[jss::ledger_index] = jss::validated;
+
4101 jvParams[jss::seq] = sequence;
+
4102 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4103 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4104 }
+
4105
+
4106 {
+
4107 testcase("RPC vault_info json invalid combination seq vault_id");
+
4108 Json::Value jvParams;
+
4109 jvParams[jss::ledger_index] = jss::validated;
+
4110 jvParams[jss::vault_id] = strHex(keylet.key);
+
4111 jvParams[jss::seq] = sequence;
+
4112 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4113 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4114 }
+
4115
+
4116 {
+
4117 testcase("RPC vault_info json invalid combination owner vault_id");
+
4118 Json::Value jvParams;
+
4119 jvParams[jss::ledger_index] = jss::validated;
+
4120 jvParams[jss::vault_id] = strHex(keylet.key);
+
4121 jvParams[jss::owner] = owner.human();
+
4122 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4123 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4124 }
+
4125
+
4126 {
+
4127 testcase(
+
4128 "RPC vault_info json invalid combination owner seq "
+
4129 "vault_id");
+
4130 Json::Value jvParams;
+
4131 jvParams[jss::ledger_index] = jss::validated;
+
4132 jvParams[jss::vault_id] = strHex(keylet.key);
+
4133 jvParams[jss::seq] = sequence;
+
4134 jvParams[jss::owner] = owner.human();
+
4135 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4136 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4137 }
+
4138
+
4139 {
+
4140 testcase("RPC vault_info json no input");
+
4141 Json::Value jvParams;
+
4142 jvParams[jss::ledger_index] = jss::validated;
+
4143 auto jv = env.rpc("json", "vault_info", to_string(jvParams));
+
4144 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4145 }
+
4146
+
4147 {
+
4148 testcase("RPC vault_info command line invalid index");
+
4149 Json::Value jv = env.rpc("vault_info", "foobar", "validated");
+
4150 BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
+
4151 }
+
4152
+
4153 {
+
4154 testcase("RPC vault_info command line invalid index");
+
4155 Json::Value jv = env.rpc("vault_info", "0", "validated");
+
4156 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "malformedRequest");
+
4157 }
+
4158
+
4159 {
+
4160 testcase("RPC vault_info command line invalid index");
+
4161 Json::Value jv = env.rpc("vault_info", strHex(uint256(42)), "validated");
+
4162 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "entryNotFound");
+
4163 }
+
4164
+
4165 {
+
4166 testcase("RPC vault_info command line invalid ledger");
+
4167 Json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
+
4168 BEAST_EXPECT(jv[jss::result][jss::error].asString() == "lgrNotFound");
+
4169 }
+
4170 }
-
4169
-
4170 void
-
- -
4172 {
-
4173 using namespace test::jtx;
-
4174
-
4175 Env env(*this, testable_amendments());
-
4176 Account alice{"alice"};
-
4177 Account bob{"bob"};
-
4178 Account carol{"carol"};
-
4179
-
4180 struct CaseArgs
-
4181 {
-
4182 PrettyAsset asset = xrpIssue();
-
4183 };
-
4184
-
4185 auto const xrpBalance = [this](Env const& env, Account const& account) -> std::optional<long> {
-
4186 auto sle = env.le(keylet::account(account.id()));
-
4187 if (BEAST_EXPECT(sle != nullptr))
-
4188 return sle->getFieldAmount(sfBalance).xrp().drops();
-
4189 return std::nullopt;
-
4190 };
-
4191
-
4192 auto testCase = [&, this](auto test, CaseArgs args = {}) {
-
4193 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
4194
-
4195 Vault vault{env};
+
4171
+
4172 void
+
+ +
4174 {
+
4175 using namespace test::jtx;
+
4176
+
4177 Env env(*this, testable_amendments());
+
4178 Account alice{"alice"};
+
4179 Account bob{"bob"};
+
4180 Account carol{"carol"};
+
4181
+
4182 struct CaseArgs
+
4183 {
+
4184 PrettyAsset asset = xrpIssue();
+
4185 };
+
4186
+
4187 auto const xrpBalance = [this](Env const& env, Account const& account) -> std::optional<long> {
+
4188 auto sle = env.le(keylet::account(account.id()));
+
4189 if (BEAST_EXPECT(sle != nullptr))
+
4190 return sle->getFieldAmount(sfBalance).xrp().drops();
+
4191 return std::nullopt;
+
4192 };
+
4193
+
4194 auto testCase = [&, this](auto test, CaseArgs args = {}) {
+
4195 Env env{*this, testable_amendments() | featureSingleAssetVault};
4196
-
4197 // use different initial amount to distinguish the source balance
-
4198 env.fund(XRP(10000), alice);
-
4199 env.fund(XRP(20000), bob);
-
4200 env.fund(XRP(30000), carol);
-
4201 env.close();
-
4202
-
4203 env(delegate::set(
-
4204 carol,
-
4205 alice,
-
4206 {"Payment",
-
4207 "VaultCreate",
-
4208 "VaultSet",
-
4209 "VaultDelete",
-
4210 "VaultDeposit",
-
4211 "VaultWithdraw",
-
4212 "VaultClawback"}));
-
4213
-
4214 test(env, vault, args.asset);
-
4215 };
-
4216
-
4217 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
-
4218 testcase("delegated vault creation");
-
4219 auto startBalance = xrpBalance(env, carol);
-
4220 if (!BEAST_EXPECT(startBalance.has_value()))
-
4221 return;
-
4222
-
4223 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
-
4224 env(tx, delegate::as(alice));
-
4225 env.close();
-
4226 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
-
4227 });
-
4228
-
4229 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
-
4230 testcase("delegated deposit and withdrawal");
-
4231 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
-
4232 env(tx);
-
4233 env.close();
-
4234
-
4235 auto const amount = 1513;
-
4236 auto const baseFee = env.current()->fees().base;
-
4237
-
4238 auto startBalance = xrpBalance(env, carol);
-
4239 if (!BEAST_EXPECT(startBalance.has_value()))
-
4240 return;
-
4241
-
4242 tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)});
-
4243 env(tx, delegate::as(alice));
-
4244 env.close();
-
4245 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
-
4246
-
4247 tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(amount - 1)});
-
4248 env(tx, delegate::as(alice));
-
4249 env.close();
-
4250 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
-
4251
-
4252 tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(1)});
-
4253 env(tx);
-
4254 env.close();
-
4255 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
-
4256 });
-
4257
-
4258 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
-
4259 testcase("delegated withdrawal same as base fee and deletion");
-
4260 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
-
4261 env(tx);
-
4262 env.close();
-
4263
-
4264 auto const amount = 25537;
-
4265 auto const baseFee = env.current()->fees().base;
-
4266
-
4267 auto startBalance = xrpBalance(env, carol);
-
4268 if (!BEAST_EXPECT(startBalance.has_value()))
-
4269 return;
-
4270
-
4271 tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)});
-
4272 env(tx);
-
4273 env.close();
-
4274 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount - baseFee);
-
4275
-
4276 tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(baseFee)});
-
4277 env(tx, delegate::as(alice));
-
4278 env.close();
-
4279 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
-
4280
-
4281 tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(amount - baseFee)});
-
4282 env(tx, delegate::as(alice));
-
4283 env.close();
-
4284 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
-
4285
-
4286 tx = vault.del({.owner = carol, .id = keylet.key});
-
4287 env(tx, delegate::as(alice));
-
4288 env.close();
-
4289 });
-
4290 }
+
4197 Vault vault{env};
+
4198
+
4199 // use different initial amount to distinguish the source balance
+
4200 env.fund(XRP(10000), alice);
+
4201 env.fund(XRP(20000), bob);
+
4202 env.fund(XRP(30000), carol);
+
4203 env.close();
+
4204
+
4205 env(delegate::set(
+
4206 carol,
+
4207 alice,
+
4208 {"Payment",
+
4209 "VaultCreate",
+
4210 "VaultSet",
+
4211 "VaultDelete",
+
4212 "VaultDeposit",
+
4213 "VaultWithdraw",
+
4214 "VaultClawback"}));
+
4215
+
4216 test(env, vault, args.asset);
+
4217 };
+
4218
+
4219 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
+
4220 testcase("delegated vault creation");
+
4221 auto startBalance = xrpBalance(env, carol);
+
4222 if (!BEAST_EXPECT(startBalance.has_value()))
+
4223 return;
+
4224
+
4225 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
+
4226 env(tx, delegate::as(alice));
+
4227 env.close();
+
4228 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance);
+
4229 });
+
4230
+
4231 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
+
4232 testcase("delegated deposit and withdrawal");
+
4233 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
+
4234 env(tx);
+
4235 env.close();
+
4236
+
4237 auto const amount = 1513;
+
4238 auto const baseFee = env.current()->fees().base;
+
4239
+
4240 auto startBalance = xrpBalance(env, carol);
+
4241 if (!BEAST_EXPECT(startBalance.has_value()))
+
4242 return;
+
4243
+
4244 tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)});
+
4245 env(tx, delegate::as(alice));
+
4246 env.close();
+
4247 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
+
4248
+
4249 tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(amount - 1)});
+
4250 env(tx, delegate::as(alice));
+
4251 env.close();
+
4252 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - 1);
+
4253
+
4254 tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(1)});
+
4255 env(tx);
+
4256 env.close();
+
4257 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
+
4258 });
+
4259
+
4260 testCase([&, this](Env& env, Vault& vault, PrettyAsset const& asset) {
+
4261 testcase("delegated withdrawal same as base fee and deletion");
+
4262 auto [tx, keylet] = vault.create({.owner = carol, .asset = asset});
+
4263 env(tx);
+
4264 env.close();
+
4265
+
4266 auto const amount = 25537;
+
4267 auto const baseFee = env.current()->fees().base;
+
4268
+
4269 auto startBalance = xrpBalance(env, carol);
+
4270 if (!BEAST_EXPECT(startBalance.has_value()))
+
4271 return;
+
4272
+
4273 tx = vault.deposit({.depositor = carol, .id = keylet.key, .amount = asset(amount)});
+
4274 env(tx);
+
4275 env.close();
+
4276 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount - baseFee);
+
4277
+
4278 tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(baseFee)});
+
4279 env(tx, delegate::as(alice));
+
4280 env.close();
+
4281 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - amount);
+
4282
+
4283 tx = vault.withdraw({.depositor = carol, .id = keylet.key, .amount = asset(amount - baseFee)});
+
4284 env(tx, delegate::as(alice));
+
4285 env.close();
+
4286 BEAST_EXPECT(xrpBalance(env, carol) == *startBalance - baseFee);
+
4287
+
4288 tx = vault.del({.owner = carol, .id = keylet.key});
+
4289 env(tx, delegate::as(alice));
+
4290 env.close();
+
4291 });
+
4292 }
-
4291
-
4292 void
-
- -
4294 {
-
4295 using namespace test::jtx;
-
4296 using namespace loanBroker;
-
4297 using namespace loan;
-
4298 Env env(*this, beast::severities::kWarning);
-
4299
-
4300 auto const vaultAssetBalance = [&](Keylet const& vaultKeylet) {
-
4301 auto const sleVault = env.le(vaultKeylet);
-
4302 BEAST_EXPECT(sleVault != nullptr);
-
4303
-
4304 return std::make_pair(sleVault->at(sfAssetsAvailable), sleVault->at(sfAssetsTotal));
-
4305 };
-
4306
-
4307 auto const vaultShareBalance = [&](Keylet const& vaultKeylet) {
-
4308 auto const sleVault = env.le(vaultKeylet);
-
4309 BEAST_EXPECT(sleVault != nullptr);
-
4310
-
4311 auto const sleIssuance = env.le(keylet::mptIssuance(sleVault->at(sfShareMPTID)));
-
4312 BEAST_EXPECT(sleIssuance != nullptr);
-
4313
-
4314 return sleIssuance->at(sfOutstandingAmount);
-
4315 };
-
4316
-
4317 auto const setupVault =
-
4318 [&](PrettyAsset const& asset, Account const& owner, Account const& depositor) -> std::pair<Vault, Keylet> {
-
4319 Vault vault{env};
-
4320
-
4321 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
-
4322 env(tx, ter(tesSUCCESS), THISLINE);
-
4323 env.close();
-
4324
-
4325 auto const& vaultSle = env.le(vaultKeylet);
-
4326 BEAST_EXPECT(vaultSle != nullptr);
-
4327
-
4328 Asset share = vaultSle->at(sfShareMPTID);
+
4293
+
4294 void
+
+ +
4296 {
+
4297 using namespace test::jtx;
+
4298 using namespace loanBroker;
+
4299 using namespace loan;
+
4300 Env env(*this, beast::severities::kWarning);
+
4301
+
4302 auto const vaultAssetBalance = [&](Keylet const& vaultKeylet) {
+
4303 auto const sleVault = env.le(vaultKeylet);
+
4304 BEAST_EXPECT(sleVault != nullptr);
+
4305
+
4306 return std::make_pair(sleVault->at(sfAssetsAvailable), sleVault->at(sfAssetsTotal));
+
4307 };
+
4308
+
4309 auto const vaultShareBalance = [&](Keylet const& vaultKeylet) {
+
4310 auto const sleVault = env.le(vaultKeylet);
+
4311 BEAST_EXPECT(sleVault != nullptr);
+
4312
+
4313 auto const sleIssuance = env.le(keylet::mptIssuance(sleVault->at(sfShareMPTID)));
+
4314 BEAST_EXPECT(sleIssuance != nullptr);
+
4315
+
4316 return sleIssuance->at(sfOutstandingAmount);
+
4317 };
+
4318
+
4319 auto const setupVault =
+
4320 [&](PrettyAsset const& asset, Account const& owner, Account const& depositor) -> std::pair<Vault, Keylet> {
+
4321 Vault vault{env};
+
4322
+
4323 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
+
4324 env(tx, ter(tesSUCCESS), THISLINE);
+
4325 env.close();
+
4326
+
4327 auto const& vaultSle = env.le(vaultKeylet);
+
4328 BEAST_EXPECT(vaultSle != nullptr);
4329
-
4330 env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
-
4331 ter(tesSUCCESS),
-
4332 THISLINE);
-
4333 env.close();
-
4334
-
4335 auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet);
-
4336 BEAST_EXPECT(availablePreDefault == totalPreDefault);
-
4337 BEAST_EXPECT(availablePreDefault == asset(100).value());
-
4338
-
4339 // attempt to clawback shares while there are assets fails
-
4340 env(vault.clawback(
-
4341 {.issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}),
-
4342 ter(tecNO_PERMISSION),
-
4343 THISLINE);
-
4344 env.close();
-
4345
-
4346 auto const& sharesAvailable = vaultShareBalance(vaultKeylet);
-
4347 auto const& brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner));
-
4348
-
4349 env(set(owner, vaultKeylet.key), THISLINE);
-
4350 env.close();
-
4351
-
4352 auto const& loanKeylet = keylet::loan(brokerKeylet.key, 1);
+
4330 Asset share = vaultSle->at(sfShareMPTID);
+
4331
+
4332 env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
+
4333 ter(tesSUCCESS),
+
4334 THISLINE);
+
4335 env.close();
+
4336
+
4337 auto const& [availablePreDefault, totalPreDefault] = vaultAssetBalance(vaultKeylet);
+
4338 BEAST_EXPECT(availablePreDefault == totalPreDefault);
+
4339 BEAST_EXPECT(availablePreDefault == asset(100).value());
+
4340
+
4341 // attempt to clawback shares while there are assets fails
+
4342 env(vault.clawback(
+
4343 {.issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}),
+
4344 ter(tecNO_PERMISSION),
+
4345 THISLINE);
+
4346 env.close();
+
4347
+
4348 auto const& sharesAvailable = vaultShareBalance(vaultKeylet);
+
4349 auto const& brokerKeylet = keylet::loanbroker(owner.id(), env.seq(owner));
+
4350
+
4351 env(set(owner, vaultKeylet.key), THISLINE);
+
4352 env.close();
4353
-
4354 // Create a simple Loan for the full amount of Vault assets
-
4355 env(set(depositor, brokerKeylet.key, asset(100).value()),
-
4356 loan::interestRate(TenthBips32(0)),
-
4357 gracePeriod(60),
-
4358 paymentInterval(120),
-
4359 paymentTotal(10),
-
4360 sig(sfCounterpartySignature, owner),
-
4361 fee(env.current()->fees().base * 2),
-
4362 ter(tesSUCCESS),
-
4363 THISLINE);
-
4364 env.close();
-
4365
-
4366 // attempt to clawback shares while there assetsAvailable == 0 and
-
4367 // assetsTotal > 0 fails
-
4368 env(vault.clawback(
-
4369 {.issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}),
-
4370 ter(tecNO_PERMISSION),
-
4371 THISLINE);
-
4372 env.close();
-
4373
-
4374 env.close(std::chrono::seconds{120 + 60});
+
4354 auto const& loanKeylet = keylet::loan(brokerKeylet.key, 1);
+
4355
+
4356 // Create a simple Loan for the full amount of Vault assets
+
4357 env(set(depositor, brokerKeylet.key, asset(100).value()),
+
4358 loan::interestRate(TenthBips32(0)),
+
4359 gracePeriod(60),
+
4360 paymentInterval(120),
+
4361 paymentTotal(10),
+
4362 sig(sfCounterpartySignature, owner),
+
4363 fee(env.current()->fees().base * 2),
+
4364 ter(tesSUCCESS),
+
4365 THISLINE);
+
4366 env.close();
+
4367
+
4368 // attempt to clawback shares while there assetsAvailable == 0 and
+
4369 // assetsTotal > 0 fails
+
4370 env(vault.clawback(
+
4371 {.issuer = owner, .id = vaultKeylet.key, .holder = depositor, .amount = share(0).value()}),
+
4372 ter(tecNO_PERMISSION),
+
4373 THISLINE);
+
4374 env.close();
4375
-
4376 env(manage(owner, loanKeylet.key, tfLoanDefault), ter(tesSUCCESS), THISLINE);
+
4376 env.close(std::chrono::seconds{120 + 60});
4377
-
4378 auto const& [availablePostDefault, totalPostDefault] = vaultAssetBalance(vaultKeylet);
+
4378 env(manage(owner, loanKeylet.key, tfLoanDefault), ter(tesSUCCESS), THISLINE);
4379
-
4380 BEAST_EXPECT(availablePostDefault == totalPostDefault);
-
4381 BEAST_EXPECT(availablePostDefault == asset(0).value());
-
4382 BEAST_EXPECT(vaultShareBalance(vaultKeylet) == sharesAvailable);
-
4383
-
4384 return std::make_pair(vault, vaultKeylet);
-
4385 };
-
4386
-
4387 auto const testCase =
-
4388 [&](PrettyAsset const& asset, std::string const& prefix, Account const& owner, Account const& depositor) {
-
4389 {
-
4390 testcase("VaultClawback (share) - " + prefix + " owner asset clawback fails");
-
4391 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
-
4392 env(vault.clawback({
-
4393 .issuer = owner,
-
4394 .id = vaultKeylet.key,
-
4395 .holder = depositor,
-
4396 .amount = asset(100).value(),
-
4397 }),
-
4398 // when asset is XRP or owner is not issuer clawback fail
-
4399 // when owner is issuer precision loss occurs as vault is
-
4400 // empty
-
4401 asset.native() ? ter(temMALFORMED)
-
4402 : asset.raw().getIssuer() != owner.id() ? ter(tecNO_PERMISSION)
-
4403 : ter(tecPRECISION_LOSS),
-
4404 THISLINE);
-
4405 env.close();
-
4406 }
-
4407
-
4408 {
-
4409 testcase("VaultClawback (share) - " + prefix + " owner incomplete share clawback fails");
-
4410 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
-
4411 auto const& vaultSle = env.le(vaultKeylet);
-
4412 BEAST_EXPECT(vaultSle != nullptr);
-
4413 if (!vaultSle)
-
4414 return;
-
4415 Asset share = vaultSle->at(sfShareMPTID);
-
4416 env(vault.clawback({
-
4417 .issuer = owner,
-
4418 .id = vaultKeylet.key,
-
4419 .holder = depositor,
-
4420 .amount = share(1).value(),
-
4421 }),
-
4422 ter(tecLIMIT_EXCEEDED),
-
4423 THISLINE);
-
4424 env.close();
-
4425 }
-
4426
-
4427 {
-
4428 testcase("VaultClawback (share) - " + prefix + " owner implicit complete share clawback");
-
4429 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
-
4430 env(vault.clawback({
-
4431 .issuer = owner,
-
4432 .id = vaultKeylet.key,
-
4433 .holder = depositor,
-
4434 }),
-
4435 // when owner is issuer implicit clawback fails
-
4436 asset.native() || asset.raw().getIssuer() != owner.id() ? ter(tesSUCCESS) : ter(tecWRONG_ASSET),
-
4437 THISLINE);
-
4438 env.close();
-
4439 }
-
4440
-
4441 {
-
4442 testcase("VaultClawback (share) - " + prefix + " owner explicit complete share clawback succeeds");
-
4443 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
-
4444 auto const& vaultSle = env.le(vaultKeylet);
-
4445 BEAST_EXPECT(vaultSle != nullptr);
-
4446 if (!vaultSle)
-
4447 return;
-
4448 Asset share = vaultSle->at(sfShareMPTID);
-
4449 env(vault.clawback({
-
4450 .issuer = owner,
-
4451 .id = vaultKeylet.key,
-
4452 .holder = depositor,
-
4453 .amount = share(vaultShareBalance(vaultKeylet)).value(),
-
4454 }),
-
4455 ter(tesSUCCESS),
-
4456 THISLINE);
-
4457 env.close();
-
4458 }
-
4459 {
-
4460 testcase("VaultClawback (share) - " + prefix + " owner can clawback own shares");
-
4461 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
-
4462 auto const& vaultSle = env.le(vaultKeylet);
-
4463 BEAST_EXPECT(vaultSle != nullptr);
-
4464 if (!vaultSle)
-
4465 return;
-
4466 Asset share = vaultSle->at(sfShareMPTID);
-
4467 env(vault.clawback({
-
4468 .issuer = owner,
-
4469 .id = vaultKeylet.key,
-
4470 .holder = owner,
-
4471 .amount = share(vaultShareBalance(vaultKeylet)).value(),
-
4472 }),
-
4473 ter(tesSUCCESS),
-
4474 THISLINE);
-
4475 env.close();
-
4476 }
-
4477
-
4478 {
-
4479 testcase("VaultClawback (share) - " + prefix + " empty vault share clawback fails");
-
4480 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
-
4481 auto const& vaultSle = env.le(vaultKeylet);
-
4482 if (BEAST_EXPECT(vaultSle != nullptr))
-
4483 return;
-
4484 Asset share = vaultSle->at(sfShareMPTID);
-
4485 env(vault.clawback({
-
4486 .issuer = owner,
-
4487 .id = vaultKeylet.key,
-
4488 .holder = owner,
-
4489 .amount = share(vaultShareBalance(vaultKeylet)).value(),
-
4490 }),
-
4491 ter(tesSUCCESS),
-
4492 THISLINE);
-
4493
-
4494 // Now the vault is empty, clawback again fails
-
4495 env(vault.clawback({
-
4496 .issuer = owner,
-
4497 .id = vaultKeylet.key,
-
4498 .holder = owner,
-
4499 }),
-
4500 ter(tecNO_PERMISSION),
-
4501 THISLINE);
-
4502 env.close();
-
4503 }
-
4504 };
-
4505
-
4506 Account owner{"alice"};
-
4507 Account depositor{"bob"};
-
4508 Account issuer{"issuer"};
-
4509
-
4510 env.fund(XRP(10000), issuer, owner, depositor);
-
4511 env.close();
-
4512
-
4513 // Test XRP
-
4514 PrettyAsset xrp = xrpIssue();
-
4515 testCase(xrp, "XRP", owner, depositor);
-
4516 testCase(xrp, "XRP (depositor is owner)", owner, owner);
-
4517
-
4518 // Test IOU
-
4519 PrettyAsset IOU = issuer["IOU"];
-
4520 env(fset(issuer, asfAllowTrustLineClawback));
-
4521 env.close();
-
4522
-
4523 env.trust(IOU(1000), owner);
-
4524 env.trust(IOU(1000), depositor);
-
4525 env(pay(issuer, owner, IOU(100)));
-
4526 env(pay(issuer, depositor, IOU(100)));
-
4527 env.close();
-
4528 testCase(IOU, "IOU", owner, depositor);
-
4529 testCase(IOU, "IOU (owner is issuer)", issuer, depositor);
-
4530
-
4531 // Test MPT
-
4532 MPTTester mptt{env, issuer, mptInitNoFund};
-
4533 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
-
4534 PrettyAsset MPT = mptt.issuanceID();
-
4535 mptt.authorize({.account = owner});
-
4536 mptt.authorize({.account = depositor});
-
4537 env(pay(issuer, owner, MPT(1000)));
-
4538 env(pay(issuer, depositor, MPT(1000)));
-
4539 env.close();
-
4540 testCase(MPT, "MPT", owner, depositor);
-
4541 testCase(MPT, "MPT (owner is issuer)", issuer, depositor);
-
4542 }
+
4380 auto const& [availablePostDefault, totalPostDefault] = vaultAssetBalance(vaultKeylet);
+
4381
+
4382 BEAST_EXPECT(availablePostDefault == totalPostDefault);
+
4383 BEAST_EXPECT(availablePostDefault == asset(0).value());
+
4384 BEAST_EXPECT(vaultShareBalance(vaultKeylet) == sharesAvailable);
+
4385
+
4386 return std::make_pair(vault, vaultKeylet);
+
4387 };
+
4388
+
4389 auto const testCase =
+
4390 [&](PrettyAsset const& asset, std::string const& prefix, Account const& owner, Account const& depositor) {
+
4391 {
+
4392 testcase("VaultClawback (share) - " + prefix + " owner asset clawback fails");
+
4393 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
+
4394 env(vault.clawback({
+
4395 .issuer = owner,
+
4396 .id = vaultKeylet.key,
+
4397 .holder = depositor,
+
4398 .amount = asset(100).value(),
+
4399 }),
+
4400 // when asset is XRP or owner is not issuer clawback fail
+
4401 // when owner is issuer precision loss occurs as vault is
+
4402 // empty
+
4403 asset.native() ? ter(temMALFORMED)
+
4404 : asset.raw().getIssuer() != owner.id() ? ter(tecNO_PERMISSION)
+
4405 : ter(tecPRECISION_LOSS),
+
4406 THISLINE);
+
4407 env.close();
+
4408 }
+
4409
+
4410 {
+
4411 testcase("VaultClawback (share) - " + prefix + " owner incomplete share clawback fails");
+
4412 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
+
4413 auto const& vaultSle = env.le(vaultKeylet);
+
4414 BEAST_EXPECT(vaultSle != nullptr);
+
4415 if (!vaultSle)
+
4416 return;
+
4417 Asset share = vaultSle->at(sfShareMPTID);
+
4418 env(vault.clawback({
+
4419 .issuer = owner,
+
4420 .id = vaultKeylet.key,
+
4421 .holder = depositor,
+
4422 .amount = share(1).value(),
+
4423 }),
+
4424 ter(tecLIMIT_EXCEEDED),
+
4425 THISLINE);
+
4426 env.close();
+
4427 }
+
4428
+
4429 {
+
4430 testcase("VaultClawback (share) - " + prefix + " owner implicit complete share clawback");
+
4431 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
+
4432 env(vault.clawback({
+
4433 .issuer = owner,
+
4434 .id = vaultKeylet.key,
+
4435 .holder = depositor,
+
4436 }),
+
4437 // when owner is issuer implicit clawback fails
+
4438 asset.native() || asset.raw().getIssuer() != owner.id() ? ter(tesSUCCESS) : ter(tecWRONG_ASSET),
+
4439 THISLINE);
+
4440 env.close();
+
4441 }
+
4442
+
4443 {
+
4444 testcase("VaultClawback (share) - " + prefix + " owner explicit complete share clawback succeeds");
+
4445 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor);
+
4446 auto const& vaultSle = env.le(vaultKeylet);
+
4447 BEAST_EXPECT(vaultSle != nullptr);
+
4448 if (!vaultSle)
+
4449 return;
+
4450 Asset share = vaultSle->at(sfShareMPTID);
+
4451 env(vault.clawback({
+
4452 .issuer = owner,
+
4453 .id = vaultKeylet.key,
+
4454 .holder = depositor,
+
4455 .amount = share(vaultShareBalance(vaultKeylet)).value(),
+
4456 }),
+
4457 ter(tesSUCCESS),
+
4458 THISLINE);
+
4459 env.close();
+
4460 }
+
4461 {
+
4462 testcase("VaultClawback (share) - " + prefix + " owner can clawback own shares");
+
4463 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
+
4464 auto const& vaultSle = env.le(vaultKeylet);
+
4465 BEAST_EXPECT(vaultSle != nullptr);
+
4466 if (!vaultSle)
+
4467 return;
+
4468 Asset share = vaultSle->at(sfShareMPTID);
+
4469 env(vault.clawback({
+
4470 .issuer = owner,
+
4471 .id = vaultKeylet.key,
+
4472 .holder = owner,
+
4473 .amount = share(vaultShareBalance(vaultKeylet)).value(),
+
4474 }),
+
4475 ter(tesSUCCESS),
+
4476 THISLINE);
+
4477 env.close();
+
4478 }
+
4479
+
4480 {
+
4481 testcase("VaultClawback (share) - " + prefix + " empty vault share clawback fails");
+
4482 auto [vault, vaultKeylet] = setupVault(asset, owner, owner);
+
4483 auto const& vaultSle = env.le(vaultKeylet);
+
4484 if (BEAST_EXPECT(vaultSle != nullptr))
+
4485 return;
+
4486 Asset share = vaultSle->at(sfShareMPTID);
+
4487 env(vault.clawback({
+
4488 .issuer = owner,
+
4489 .id = vaultKeylet.key,
+
4490 .holder = owner,
+
4491 .amount = share(vaultShareBalance(vaultKeylet)).value(),
+
4492 }),
+
4493 ter(tesSUCCESS),
+
4494 THISLINE);
+
4495
+
4496 // Now the vault is empty, clawback again fails
+
4497 env(vault.clawback({
+
4498 .issuer = owner,
+
4499 .id = vaultKeylet.key,
+
4500 .holder = owner,
+
4501 }),
+
4502 ter(tecNO_PERMISSION),
+
4503 THISLINE);
+
4504 env.close();
+
4505 }
+
4506 };
+
4507
+
4508 Account owner{"alice"};
+
4509 Account depositor{"bob"};
+
4510 Account issuer{"issuer"};
+
4511
+
4512 env.fund(XRP(10000), issuer, owner, depositor);
+
4513 env.close();
+
4514
+
4515 // Test XRP
+
4516 PrettyAsset xrp = xrpIssue();
+
4517 testCase(xrp, "XRP", owner, depositor);
+
4518 testCase(xrp, "XRP (depositor is owner)", owner, owner);
+
4519
+
4520 // Test IOU
+
4521 PrettyAsset IOU = issuer["IOU"];
+
4522 env(fset(issuer, asfAllowTrustLineClawback));
+
4523 env.close();
+
4524
+
4525 env.trust(IOU(1000), owner);
+
4526 env.trust(IOU(1000), depositor);
+
4527 env(pay(issuer, owner, IOU(100)));
+
4528 env(pay(issuer, depositor, IOU(100)));
+
4529 env.close();
+
4530 testCase(IOU, "IOU", owner, depositor);
+
4531 testCase(IOU, "IOU (owner is issuer)", issuer, depositor);
+
4532
+
4533 // Test MPT
+
4534 MPTTester mptt{env, issuer, mptInitNoFund};
+
4535 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
+
4536 PrettyAsset MPT = mptt.issuanceID();
+
4537 mptt.authorize({.account = owner});
+
4538 mptt.authorize({.account = depositor});
+
4539 env(pay(issuer, owner, MPT(1000)));
+
4540 env(pay(issuer, depositor, MPT(1000)));
+
4541 env.close();
+
4542 testCase(MPT, "MPT", owner, depositor);
+
4543 testCase(MPT, "MPT (owner is issuer)", issuer, depositor);
+
4544 }
-
4543
-
4544 void
-
- -
4546 {
-
4547 using namespace test::jtx;
-
4548 using namespace loanBroker;
-
4549 using namespace loan;
-
4550 Env env(*this);
-
4551
-
4552 auto const setupVault = [&](PrettyAsset const& asset,
-
4553 Account const& owner,
-
4554 Account const& depositor,
-
4555 Account const& issuer) -> std::pair<Vault, Keylet> {
-
4556 Vault vault{env};
-
4557
-
4558 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
-
4559 env(tx, ter(tesSUCCESS), THISLINE);
-
4560 env.close();
-
4561
-
4562 auto const& vaultSle = env.le(vaultKeylet);
-
4563 BEAST_EXPECT(vaultSle != nullptr);
-
4564 env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
-
4565 ter(tesSUCCESS),
-
4566 THISLINE);
-
4567 env.close();
-
4568
-
4569 return std::make_pair(vault, vaultKeylet);
-
4570 };
-
4571
-
4572 auto const testCase = [&](PrettyAsset const& asset,
-
4573 std::string const& prefix,
-
4574 Account const& owner,
-
4575 Account const& depositor,
-
4576 Account const& issuer) {
-
4577 if (asset.native())
-
4578 {
-
4579 testcase("VaultClawback (asset) - " + prefix + " issuer XRP clawback fails");
-
4580 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
-
4581 // If the asset is XRP, clawback with amount fails as malformed
-
4582 // when asset is specified.
-
4583 env(vault.clawback({
-
4584 .issuer = issuer,
-
4585 .id = vaultKeylet.key,
-
4586 .holder = issuer,
-
4587 .amount = asset(1).value(),
-
4588 }),
-
4589 ter(temMALFORMED),
-
4590 THISLINE);
-
4591 // When asset is implicit, clawback fails as no permission.
-
4592 env(vault.clawback({
-
4593 .issuer = issuer,
-
4594 .id = vaultKeylet.key,
-
4595 .holder = issuer,
-
4596 }),
-
4597 ter(tecNO_PERMISSION),
-
4598 THISLINE);
-
4599 return;
-
4600 }
-
4601
-
4602 {
-
4603 testcase("VaultClawback (asset) - " + prefix + " clawback for different asset fails");
-
4604 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
-
4605
-
4606 Account issuer2{"issuer2"};
-
4607 PrettyAsset asset2 = issuer2["FOO"];
-
4608 env(vault.clawback({
-
4609 .issuer = issuer,
-
4610 .id = vaultKeylet.key,
-
4611 .holder = depositor,
-
4612 .amount = asset2(1).value(),
-
4613 }),
-
4614 ter(tecWRONG_ASSET),
-
4615 THISLINE);
-
4616 }
-
4617
-
4618 {
-
4619 testcase("VaultClawback (asset) - " + prefix + " ambiguous owner/issuer asset clawback fails");
-
4620 auto [vault, vaultKeylet] = setupVault(asset, issuer, depositor, issuer);
-
4621 env(vault.clawback({
-
4622 .issuer = issuer,
-
4623 .id = vaultKeylet.key,
-
4624 .holder = issuer,
-
4625 }),
-
4626 ter(tecWRONG_ASSET),
-
4627 THISLINE);
-
4628 }
-
4629
-
4630 {
-
4631 testcase("VaultClawback (asset) - " + prefix + " non-issuer asset clawback fails");
-
4632 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
-
4633
-
4634 env(vault.clawback({
-
4635 .issuer = owner,
-
4636 .id = vaultKeylet.key,
-
4637 .holder = depositor,
-
4638 }),
-
4639 ter(tecNO_PERMISSION),
-
4640 THISLINE);
-
4641
-
4642 env(vault.clawback({
-
4643 .issuer = owner,
-
4644 .id = vaultKeylet.key,
-
4645 .holder = depositor,
-
4646 .amount = asset(1).value(),
-
4647 }),
-
4648 ter(tecNO_PERMISSION),
-
4649 THISLINE);
-
4650 }
-
4651
-
4652 {
-
4653 testcase("VaultClawback (asset) - " + prefix + " issuer clawback from self fails");
-
4654 auto [vault, vaultKeylet] = setupVault(asset, owner, issuer, issuer);
-
4655 env(vault.clawback({
-
4656 .issuer = issuer,
-
4657 .id = vaultKeylet.key,
-
4658 .holder = issuer,
-
4659 }),
-
4660 ter(tecNO_PERMISSION),
-
4661 THISLINE);
-
4662 }
-
4663
-
4664 {
-
4665 testcase("VaultClawback (asset) - " + prefix + " issuer share clawback fails");
-
4666 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
-
4667 auto const& vaultSle = env.le(vaultKeylet);
-
4668 BEAST_EXPECT(vaultSle != nullptr);
-
4669 if (!vaultSle)
-
4670 return;
-
4671 Asset share = vaultSle->at(sfShareMPTID);
-
4672
-
4673 env(vault.clawback({
-
4674 .issuer = issuer,
-
4675 .id = vaultKeylet.key,
-
4676 .holder = depositor,
-
4677 .amount = share(1).value(),
-
4678 }),
-
4679 ter(tecNO_PERMISSION),
-
4680 THISLINE);
-
4681 }
-
4682
-
4683 {
-
4684 testcase("VaultClawback (asset) - " + prefix + " partial issuer asset clawback succeeds");
-
4685 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
-
4686
-
4687 env(vault.clawback({
-
4688 .issuer = issuer,
-
4689 .id = vaultKeylet.key,
-
4690 .holder = depositor,
-
4691 .amount = asset(1).value(),
-
4692 }),
-
4693 ter(tesSUCCESS),
-
4694 THISLINE);
-
4695 }
-
4696
-
4697 {
-
4698 testcase("VaultClawback (asset) - " + prefix + " full issuer asset clawback succeeds");
-
4699 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
-
4700
-
4701 env(vault.clawback({
-
4702 .issuer = issuer,
-
4703 .id = vaultKeylet.key,
-
4704 .holder = depositor,
-
4705 .amount = asset(100).value(),
-
4706 }),
-
4707 ter(tesSUCCESS),
-
4708 THISLINE);
-
4709 }
-
4710
-
4711 {
-
4712 testcase("VaultClawback (asset) - " + prefix + " implicit full issuer asset clawback succeeds");
-
4713 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
-
4714
-
4715 env(vault.clawback({
-
4716 .issuer = issuer,
-
4717 .id = vaultKeylet.key,
-
4718 .holder = depositor,
-
4719 }),
-
4720 ter(tesSUCCESS),
-
4721 THISLINE);
-
4722 }
-
4723 };
-
4724
-
4725 Account owner{"alice"};
-
4726 Account depositor{"bob"};
-
4727 Account issuer{"issuer"};
-
4728
-
4729 env.fund(XRP(10000), issuer, owner, depositor);
-
4730 env.close();
-
4731
-
4732 // Test XRP
-
4733 PrettyAsset xrp = xrpIssue();
-
4734 testCase(xrp, "XRP", owner, depositor, issuer);
-
4735
-
4736 // Test IOU
-
4737 PrettyAsset IOU = issuer["IOU"];
-
4738 env(fset(issuer, asfAllowTrustLineClawback));
-
4739 env.close();
-
4740 env.trust(IOU(1000), owner);
-
4741 env.trust(IOU(1000), depositor);
-
4742 env(pay(issuer, owner, IOU(1000)));
-
4743 env(pay(issuer, depositor, IOU(1000)));
-
4744 env.close();
-
4745 testCase(IOU, "IOU", owner, depositor, issuer);
-
4746
-
4747 // Test MPT
-
4748 MPTTester mptt{env, issuer, mptInitNoFund};
-
4749 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
-
4750 PrettyAsset MPT = mptt.issuanceID();
-
4751 mptt.authorize({.account = owner});
-
4752 mptt.authorize({.account = depositor});
-
4753 env(pay(issuer, depositor, MPT(1000)));
-
4754 env.close();
-
4755 testCase(MPT, "MPT", owner, depositor, issuer);
-
4756 }
+
4545
+
4546 void
+
+ +
4548 {
+
4549 using namespace test::jtx;
+
4550 using namespace loanBroker;
+
4551 using namespace loan;
+
4552 Env env(*this);
+
4553
+
4554 auto const setupVault = [&](PrettyAsset const& asset,
+
4555 Account const& owner,
+
4556 Account const& depositor,
+
4557 Account const& issuer) -> std::pair<Vault, Keylet> {
+
4558 Vault vault{env};
+
4559
+
4560 auto const& [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
+
4561 env(tx, ter(tesSUCCESS), THISLINE);
+
4562 env.close();
+
4563
+
4564 auto const& vaultSle = env.le(vaultKeylet);
+
4565 BEAST_EXPECT(vaultSle != nullptr);
+
4566 env(vault.deposit({.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)}),
+
4567 ter(tesSUCCESS),
+
4568 THISLINE);
+
4569 env.close();
+
4570
+
4571 return std::make_pair(vault, vaultKeylet);
+
4572 };
+
4573
+
4574 auto const testCase = [&](PrettyAsset const& asset,
+
4575 std::string const& prefix,
+
4576 Account const& owner,
+
4577 Account const& depositor,
+
4578 Account const& issuer) {
+
4579 if (asset.native())
+
4580 {
+
4581 testcase("VaultClawback (asset) - " + prefix + " issuer XRP clawback fails");
+
4582 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
+
4583 // If the asset is XRP, clawback with amount fails as malformed
+
4584 // when asset is specified.
+
4585 env(vault.clawback({
+
4586 .issuer = issuer,
+
4587 .id = vaultKeylet.key,
+
4588 .holder = issuer,
+
4589 .amount = asset(1).value(),
+
4590 }),
+
4591 ter(temMALFORMED),
+
4592 THISLINE);
+
4593 // When asset is implicit, clawback fails as no permission.
+
4594 env(vault.clawback({
+
4595 .issuer = issuer,
+
4596 .id = vaultKeylet.key,
+
4597 .holder = issuer,
+
4598 }),
+
4599 ter(tecNO_PERMISSION),
+
4600 THISLINE);
+
4601 return;
+
4602 }
+
4603
+
4604 {
+
4605 testcase("VaultClawback (asset) - " + prefix + " clawback for different asset fails");
+
4606 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
+
4607
+
4608 Account issuer2{"issuer2"};
+
4609 PrettyAsset asset2 = issuer2["FOO"];
+
4610 env(vault.clawback({
+
4611 .issuer = issuer,
+
4612 .id = vaultKeylet.key,
+
4613 .holder = depositor,
+
4614 .amount = asset2(1).value(),
+
4615 }),
+
4616 ter(tecWRONG_ASSET),
+
4617 THISLINE);
+
4618 }
+
4619
+
4620 {
+
4621 testcase("VaultClawback (asset) - " + prefix + " ambiguous owner/issuer asset clawback fails");
+
4622 auto [vault, vaultKeylet] = setupVault(asset, issuer, depositor, issuer);
+
4623 env(vault.clawback({
+
4624 .issuer = issuer,
+
4625 .id = vaultKeylet.key,
+
4626 .holder = issuer,
+
4627 }),
+
4628 ter(tecWRONG_ASSET),
+
4629 THISLINE);
+
4630 }
+
4631
+
4632 {
+
4633 testcase("VaultClawback (asset) - " + prefix + " non-issuer asset clawback fails");
+
4634 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
+
4635
+
4636 env(vault.clawback({
+
4637 .issuer = owner,
+
4638 .id = vaultKeylet.key,
+
4639 .holder = depositor,
+
4640 }),
+
4641 ter(tecNO_PERMISSION),
+
4642 THISLINE);
+
4643
+
4644 env(vault.clawback({
+
4645 .issuer = owner,
+
4646 .id = vaultKeylet.key,
+
4647 .holder = depositor,
+
4648 .amount = asset(1).value(),
+
4649 }),
+
4650 ter(tecNO_PERMISSION),
+
4651 THISLINE);
+
4652 }
+
4653
+
4654 {
+
4655 testcase("VaultClawback (asset) - " + prefix + " issuer clawback from self fails");
+
4656 auto [vault, vaultKeylet] = setupVault(asset, owner, issuer, issuer);
+
4657 env(vault.clawback({
+
4658 .issuer = issuer,
+
4659 .id = vaultKeylet.key,
+
4660 .holder = issuer,
+
4661 }),
+
4662 ter(tecNO_PERMISSION),
+
4663 THISLINE);
+
4664 }
+
4665
+
4666 {
+
4667 testcase("VaultClawback (asset) - " + prefix + " issuer share clawback fails");
+
4668 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
+
4669 auto const& vaultSle = env.le(vaultKeylet);
+
4670 BEAST_EXPECT(vaultSle != nullptr);
+
4671 if (!vaultSle)
+
4672 return;
+
4673 Asset share = vaultSle->at(sfShareMPTID);
+
4674
+
4675 env(vault.clawback({
+
4676 .issuer = issuer,
+
4677 .id = vaultKeylet.key,
+
4678 .holder = depositor,
+
4679 .amount = share(1).value(),
+
4680 }),
+
4681 ter(tecNO_PERMISSION),
+
4682 THISLINE);
+
4683 }
+
4684
+
4685 {
+
4686 testcase("VaultClawback (asset) - " + prefix + " partial issuer asset clawback succeeds");
+
4687 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
+
4688
+
4689 env(vault.clawback({
+
4690 .issuer = issuer,
+
4691 .id = vaultKeylet.key,
+
4692 .holder = depositor,
+
4693 .amount = asset(1).value(),
+
4694 }),
+
4695 ter(tesSUCCESS),
+
4696 THISLINE);
+
4697 }
+
4698
+
4699 {
+
4700 testcase("VaultClawback (asset) - " + prefix + " full issuer asset clawback succeeds");
+
4701 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
+
4702
+
4703 env(vault.clawback({
+
4704 .issuer = issuer,
+
4705 .id = vaultKeylet.key,
+
4706 .holder = depositor,
+
4707 .amount = asset(100).value(),
+
4708 }),
+
4709 ter(tesSUCCESS),
+
4710 THISLINE);
+
4711 }
+
4712
+
4713 {
+
4714 testcase("VaultClawback (asset) - " + prefix + " implicit full issuer asset clawback succeeds");
+
4715 auto [vault, vaultKeylet] = setupVault(asset, owner, depositor, issuer);
+
4716
+
4717 env(vault.clawback({
+
4718 .issuer = issuer,
+
4719 .id = vaultKeylet.key,
+
4720 .holder = depositor,
+
4721 }),
+
4722 ter(tesSUCCESS),
+
4723 THISLINE);
+
4724 }
+
4725 };
+
4726
+
4727 Account owner{"alice"};
+
4728 Account depositor{"bob"};
+
4729 Account issuer{"issuer"};
+
4730
+
4731 env.fund(XRP(10000), issuer, owner, depositor);
+
4732 env.close();
+
4733
+
4734 // Test XRP
+
4735 PrettyAsset xrp = xrpIssue();
+
4736 testCase(xrp, "XRP", owner, depositor, issuer);
+
4737
+
4738 // Test IOU
+
4739 PrettyAsset IOU = issuer["IOU"];
+
4740 env(fset(issuer, asfAllowTrustLineClawback));
+
4741 env.close();
+
4742 env.trust(IOU(1000), owner);
+
4743 env.trust(IOU(1000), depositor);
+
4744 env(pay(issuer, owner, IOU(1000)));
+
4745 env(pay(issuer, depositor, IOU(1000)));
+
4746 env.close();
+
4747 testCase(IOU, "IOU", owner, depositor, issuer);
+
4748
+
4749 // Test MPT
+
4750 MPTTester mptt{env, issuer, mptInitNoFund};
+
4751 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
+
4752 PrettyAsset MPT = mptt.issuanceID();
+
4753 mptt.authorize({.account = owner});
+
4754 mptt.authorize({.account = depositor});
+
4755 env(pay(issuer, depositor, MPT(1000)));
+
4756 env.close();
+
4757 testCase(MPT, "MPT", owner, depositor, issuer);
+
4758 }
-
4757
-
4758 void
-
- -
4760 {
-
4761 testcase("Assets Maximum");
-
4762
-
4763 using namespace test::jtx;
+
4759
+
4760 void
+
+ +
4762 {
+
4763 testcase("Assets Maximum");
4764
-
4765 Env env{*this, testable_amendments() | featureSingleAssetVault};
-
4766 Account const owner{"owner"};
-
4767 Account const issuer{"issuer"};
-
4768
-
4769 Vault vault{env};
-
4770 env.fund(XRP(1'000'000), issuer, owner);
-
4771 env.close();
-
4772
- -
4774 BEAST_EXPECT(maxInt64 == "9223372036854775807");
-
4775
-
4776 // Naming things is hard
-
4777 auto const maxInt64Plus1 =
- -
4779 BEAST_EXPECT(maxInt64Plus1 == "9223372036854775808");
-
4780
-
4781 auto const initialXRP = to_string(INITIAL_XRP);
-
4782 BEAST_EXPECT(initialXRP == "100000000000000000");
-
4783
-
4784 auto const initialXRPPlus1 = to_string(INITIAL_XRP + 1);
-
4785 BEAST_EXPECT(initialXRPPlus1 == "100000000000000001");
-
4786
-
4787 {
-
4788 testcase("Assets Maximum: XRP");
-
4789
-
4790 PrettyAsset const xrpAsset = xrpIssue();
+
4765 using namespace test::jtx;
+
4766
+
4767 Env env{*this, testable_amendments() | featureSingleAssetVault};
+
4768 Account const owner{"owner"};
+
4769 Account const issuer{"issuer"};
+
4770
+
4771 Vault vault{env};
+
4772 env.fund(XRP(1'000'000), issuer, owner);
+
4773 env.close();
+
4774
+ +
4776 BEAST_EXPECT(maxInt64 == "9223372036854775807");
+
4777
+
4778 // Naming things is hard
+
4779 auto const maxInt64Plus1 =
+ +
4781 BEAST_EXPECT(maxInt64Plus1 == "9223372036854775808");
+
4782
+
4783 auto const initialXRP = to_string(INITIAL_XRP);
+
4784 BEAST_EXPECT(initialXRP == "100000000000000000");
+
4785
+
4786 auto const initialXRPPlus1 = to_string(INITIAL_XRP + 1);
+
4787 BEAST_EXPECT(initialXRPPlus1 == "100000000000000001");
+
4788
+
4789 {
+
4790 testcase("Assets Maximum: XRP");
4791
-
4792 auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
-
4793 tx[sfData] = "4D65746144617461";
-
4794
-
4795 tx[sfAssetsMaximum] = maxInt64;
-
4796 env(tx, ter(tefEXCEPTION), THISLINE);
-
4797 env.close();
-
4798
-
4799 tx[sfAssetsMaximum] = initialXRPPlus1;
-
4800 env(tx, ter(tefEXCEPTION), THISLINE);
-
4801 env.close();
-
4802
-
4803 tx[sfAssetsMaximum] = initialXRP;
-
4804 env(tx, THISLINE);
-
4805 env.close();
-
4806
-
4807 tx[sfAssetsMaximum] = maxInt64Plus1;
-
4808 env(tx, ter(tefEXCEPTION), THISLINE);
-
4809 env.close();
-
4810
-
4811 // This value will be rounded
-
4812 auto const insertAt = maxInt64Plus1.size() - 3;
-
4813 auto const decimalTest =
-
4814 maxInt64Plus1.substr(0, insertAt) + "." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 1000
-
4815 BEAST_EXPECT(decimalTest == "9223372036854775.808");
-
4816 tx[sfAssetsMaximum] = decimalTest;
-
4817 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
-
4818 env(tx, THISLINE);
-
4819 env.close();
-
4820
-
4821 auto const vaultSle = env.le(newKeylet);
-
4822 if (!BEAST_EXPECT(vaultSle))
-
4823 return;
-
4824
-
4825 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 9223372036854776);
-
4826 }
-
4827
-
4828 {
-
4829 testcase("Assets Maximum: MPT");
-
4830
-
4831 PrettyAsset const mptAsset = [&]() {
-
4832 MPTTester mptt{env, issuer, mptInitNoFund};
-
4833 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
-
4834 env.close();
-
4835 PrettyAsset const mptAsset = mptt["MPT"];
-
4836 mptt.authorize({.account = owner});
-
4837 env.close();
-
4838 return mptAsset;
-
4839 }();
-
4840
-
4841 env(pay(issuer, owner, mptAsset(100'000)), THISLINE);
-
4842 env.close();
-
4843
-
4844 auto [tx, keylet] = vault.create({.owner = owner, .asset = mptAsset});
-
4845 tx[sfData] = "4D65746144617461";
-
4846
-
4847 tx[sfAssetsMaximum] = maxInt64;
-
4848 env(tx, THISLINE);
-
4849 env.close();
-
4850
-
4851 tx[sfAssetsMaximum] = initialXRPPlus1;
-
4852 env(tx, THISLINE);
-
4853 env.close();
-
4854
-
4855 tx[sfAssetsMaximum] = initialXRP;
-
4856 env(tx, THISLINE);
-
4857 env.close();
-
4858
-
4859 tx[sfAssetsMaximum] = maxInt64Plus1;
-
4860 env(tx, ter(tefEXCEPTION), THISLINE);
-
4861 env.close();
-
4862
-
4863 // This value will be rounded
-
4864 auto const insertAt = maxInt64Plus1.size() - 1;
-
4865 auto const decimalTest =
-
4866 maxInt64Plus1.substr(0, insertAt) + "." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
-
4867 BEAST_EXPECT(decimalTest == "922337203685477580.8");
-
4868 tx[sfAssetsMaximum] = decimalTest;
-
4869 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
-
4870 env(tx, THISLINE);
-
4871 env.close();
-
4872
-
4873 auto const vaultSle = env.le(newKeylet);
-
4874 if (!BEAST_EXPECT(vaultSle))
-
4875 return;
-
4876
-
4877 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 922337203685477581);
-
4878 }
-
4879
-
4880 {
-
4881 testcase("Assets Maximum: IOU");
-
4882
-
4883 // Almost anything goes with IOUs
-
4884 PrettyAsset iouAsset = issuer["IOU"];
-
4885 env.trust(iouAsset(1000), owner);
-
4886 env(pay(issuer, owner, iouAsset(200)));
-
4887 env.close();
-
4888
-
4889 auto [tx, keylet] = vault.create({.owner = owner, .asset = iouAsset});
-
4890 tx[sfData] = "4D65746144617461";
-
4891
-
4892 tx[sfAssetsMaximum] = maxInt64;
-
4893 env(tx, THISLINE);
-
4894 env.close();
-
4895
-
4896 tx[sfAssetsMaximum] = initialXRPPlus1;
-
4897 env(tx, THISLINE);
-
4898 env.close();
-
4899
-
4900 tx[sfAssetsMaximum] = initialXRP;
-
4901 env(tx, THISLINE);
-
4902 env.close();
-
4903
-
4904 tx[sfAssetsMaximum] = maxInt64Plus1;
-
4905 env(tx, THISLINE);
-
4906 env.close();
-
4907
-
4908 tx[sfAssetsMaximum] = "1000000000000000e80";
-
4909 env.close();
-
4910
-
4911 tx[sfAssetsMaximum] = "1000000000000000e-96";
-
4912 env.close();
-
4913
-
4914 // These values will be rounded to 15 significant digits
-
4915 {
-
4916 auto const insertAt = maxInt64Plus1.size() - 1;
-
4917 auto const decimalTest =
-
4918 maxInt64Plus1.substr(0, insertAt) + "." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
-
4919 BEAST_EXPECT(decimalTest == "922337203685477580.8");
-
4920 tx[sfAssetsMaximum] = decimalTest;
-
4921 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
-
4922 env(tx, THISLINE);
-
4923 env.close();
-
4924
-
4925 auto const vaultSle = env.le(newKeylet);
-
4926 if (!BEAST_EXPECT(vaultSle))
-
4927 return;
-
4928
-
4929 BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) == Number{9223372036854776, 2, Number::normalized{}}));
-
4930 }
-
4931 {
-
4932 tx[sfAssetsMaximum] = "9223372036854775807e40"; // max int64 * 10^40
-
4933 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
-
4934 env(tx, THISLINE);
-
4935 env.close();
-
4936
-
4937 auto const vaultSle = env.le(newKeylet);
-
4938 if (!BEAST_EXPECT(vaultSle))
-
4939 return;
-
4940
-
4941 BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) == Number{9223372036854776, 43, Number::normalized{}}));
-
4942 }
-
4943 {
-
4944 tx[sfAssetsMaximum] = "9223372036854775807e-40"; // max int64 * 10^-40
-
4945 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
-
4946 env(tx, THISLINE);
-
4947 env.close();
-
4948
-
4949 auto const vaultSle = env.le(newKeylet);
-
4950 if (!BEAST_EXPECT(vaultSle))
-
4951 return;
-
4952
-
4953 BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) == Number{9223372036854776, -37, Number::normalized{}}));
-
4954 }
-
4955 {
-
4956 tx[sfAssetsMaximum] = "9223372036854775807e-100"; // max int64 * 10^-100
-
4957 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
-
4958 env(tx, THISLINE);
-
4959 env.close();
-
4960
-
4961 // Field 'AssetsMaximum' may not be explicitly set to default.
-
4962 auto const vaultSle = env.le(newKeylet);
-
4963 if (!BEAST_EXPECT(vaultSle))
-
4964 return;
-
4965
-
4966 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == numZero);
-
4967 }
-
4968
-
4969 // What _can't_ IOUs do?
-
4970 // 1. Exceed maximum exponent / offset
-
4971 tx[sfAssetsMaximum] = "1000000000000000e81";
-
4972 env(tx, ter(tefEXCEPTION), THISLINE);
-
4973 env.close();
-
4974
-
4975 // 2. Mantissa larger than uint64 max
-
4976 try
-
4977 {
-
4978 tx[sfAssetsMaximum] = "18446744073709551617e5"; // uint64 max + 1
-
4979 env(tx, THISLINE);
-
4980 BEAST_EXPECT(false);
-
4981 }
-
4982 catch (parse_error const& e)
-
4983 {
-
4984 using namespace std::string_literals;
-
4985 BEAST_EXPECT(
-
4986 e.what() ==
-
4987 "invalidParamsField 'tx_json.AssetsMaximum' has invalid "
-
4988 "data."s);
-
4989 }
-
4990 }
-
4991 }
+
4792 PrettyAsset const xrpAsset = xrpIssue();
+
4793
+
4794 auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpAsset});
+
4795 tx[sfData] = "4D65746144617461";
+
4796
+
4797 tx[sfAssetsMaximum] = maxInt64;
+
4798 env(tx, ter(tefEXCEPTION), THISLINE);
+
4799 env.close();
+
4800
+
4801 tx[sfAssetsMaximum] = initialXRPPlus1;
+
4802 env(tx, ter(tefEXCEPTION), THISLINE);
+
4803 env.close();
+
4804
+
4805 tx[sfAssetsMaximum] = initialXRP;
+
4806 env(tx, THISLINE);
+
4807 env.close();
+
4808
+
4809 tx[sfAssetsMaximum] = maxInt64Plus1;
+
4810 env(tx, ter(tefEXCEPTION), THISLINE);
+
4811 env.close();
+
4812
+
4813 // This value will be rounded
+
4814 auto const insertAt = maxInt64Plus1.size() - 3;
+
4815 auto const decimalTest =
+
4816 maxInt64Plus1.substr(0, insertAt) + "." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 1000
+
4817 BEAST_EXPECT(decimalTest == "9223372036854775.808");
+
4818 tx[sfAssetsMaximum] = decimalTest;
+
4819 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
+
4820 env(tx, THISLINE);
+
4821 env.close();
+
4822
+
4823 auto const vaultSle = env.le(newKeylet);
+
4824 if (!BEAST_EXPECT(vaultSle))
+
4825 return;
+
4826
+
4827 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 9223372036854776);
+
4828 }
+
4829
+
4830 {
+
4831 testcase("Assets Maximum: MPT");
+
4832
+
4833 PrettyAsset const mptAsset = [&]() {
+
4834 MPTTester mptt{env, issuer, mptInitNoFund};
+
4835 mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
+
4836 env.close();
+
4837 PrettyAsset const mptAsset = mptt["MPT"];
+
4838 mptt.authorize({.account = owner});
+
4839 env.close();
+
4840 return mptAsset;
+
4841 }();
+
4842
+
4843 env(pay(issuer, owner, mptAsset(100'000)), THISLINE);
+
4844 env.close();
+
4845
+
4846 auto [tx, keylet] = vault.create({.owner = owner, .asset = mptAsset});
+
4847 tx[sfData] = "4D65746144617461";
+
4848
+
4849 tx[sfAssetsMaximum] = maxInt64;
+
4850 env(tx, THISLINE);
+
4851 env.close();
+
4852
+
4853 tx[sfAssetsMaximum] = initialXRPPlus1;
+
4854 env(tx, THISLINE);
+
4855 env.close();
+
4856
+
4857 tx[sfAssetsMaximum] = initialXRP;
+
4858 env(tx, THISLINE);
+
4859 env.close();
+
4860
+
4861 tx[sfAssetsMaximum] = maxInt64Plus1;
+
4862 env(tx, ter(tefEXCEPTION), THISLINE);
+
4863 env.close();
+
4864
+
4865 // This value will be rounded
+
4866 auto const insertAt = maxInt64Plus1.size() - 1;
+
4867 auto const decimalTest =
+
4868 maxInt64Plus1.substr(0, insertAt) + "." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
+
4869 BEAST_EXPECT(decimalTest == "922337203685477580.8");
+
4870 tx[sfAssetsMaximum] = decimalTest;
+
4871 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
+
4872 env(tx, THISLINE);
+
4873 env.close();
+
4874
+
4875 auto const vaultSle = env.le(newKeylet);
+
4876 if (!BEAST_EXPECT(vaultSle))
+
4877 return;
+
4878
+
4879 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 922337203685477581);
+
4880 }
+
4881
+
4882 {
+
4883 testcase("Assets Maximum: IOU");
+
4884
+
4885 // Almost anything goes with IOUs
+
4886 PrettyAsset iouAsset = issuer["IOU"];
+
4887 env.trust(iouAsset(1000), owner);
+
4888 env(pay(issuer, owner, iouAsset(200)));
+
4889 env.close();
+
4890
+
4891 auto [tx, keylet] = vault.create({.owner = owner, .asset = iouAsset});
+
4892 tx[sfData] = "4D65746144617461";
+
4893
+
4894 tx[sfAssetsMaximum] = maxInt64;
+
4895 env(tx, THISLINE);
+
4896 env.close();
+
4897
+
4898 tx[sfAssetsMaximum] = initialXRPPlus1;
+
4899 env(tx, THISLINE);
+
4900 env.close();
+
4901
+
4902 tx[sfAssetsMaximum] = initialXRP;
+
4903 env(tx, THISLINE);
+
4904 env.close();
+
4905
+
4906 tx[sfAssetsMaximum] = maxInt64Plus1;
+
4907 env(tx, THISLINE);
+
4908 env.close();
+
4909
+
4910 tx[sfAssetsMaximum] = "1000000000000000e80";
+
4911 env.close();
+
4912
+
4913 tx[sfAssetsMaximum] = "1000000000000000e-96";
+
4914 env.close();
+
4915
+
4916 // These values will be rounded to 15 significant digits
+
4917 {
+
4918 auto const insertAt = maxInt64Plus1.size() - 1;
+
4919 auto const decimalTest =
+
4920 maxInt64Plus1.substr(0, insertAt) + "." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
+
4921 BEAST_EXPECT(decimalTest == "922337203685477580.8");
+
4922 tx[sfAssetsMaximum] = decimalTest;
+
4923 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
+
4924 env(tx, THISLINE);
+
4925 env.close();
+
4926
+
4927 auto const vaultSle = env.le(newKeylet);
+
4928 if (!BEAST_EXPECT(vaultSle))
+
4929 return;
+
4930
+
4931 BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) == Number{9223372036854776, 2, Number::normalized{}}));
+
4932 }
+
4933 {
+
4934 tx[sfAssetsMaximum] = "9223372036854775807e40"; // max int64 * 10^40
+
4935 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
+
4936 env(tx, THISLINE);
+
4937 env.close();
+
4938
+
4939 auto const vaultSle = env.le(newKeylet);
+
4940 if (!BEAST_EXPECT(vaultSle))
+
4941 return;
+
4942
+
4943 BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) == Number{9223372036854776, 43, Number::normalized{}}));
+
4944 }
+
4945 {
+
4946 tx[sfAssetsMaximum] = "9223372036854775807e-40"; // max int64 * 10^-40
+
4947 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
+
4948 env(tx, THISLINE);
+
4949 env.close();
+
4950
+
4951 auto const vaultSle = env.le(newKeylet);
+
4952 if (!BEAST_EXPECT(vaultSle))
+
4953 return;
+
4954
+
4955 BEAST_EXPECT((vaultSle->at(sfAssetsMaximum) == Number{9223372036854776, -37, Number::normalized{}}));
+
4956 }
+
4957 {
+
4958 tx[sfAssetsMaximum] = "9223372036854775807e-100"; // max int64 * 10^-100
+
4959 auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
+
4960 env(tx, THISLINE);
+
4961 env.close();
+
4962
+
4963 // Field 'AssetsMaximum' may not be explicitly set to default.
+
4964 auto const vaultSle = env.le(newKeylet);
+
4965 if (!BEAST_EXPECT(vaultSle))
+
4966 return;
+
4967
+
4968 BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == numZero);
+
4969 }
+
4970
+
4971 // What _can't_ IOUs do?
+
4972 // 1. Exceed maximum exponent / offset
+
4973 tx[sfAssetsMaximum] = "1000000000000000e81";
+
4974 env(tx, ter(tefEXCEPTION), THISLINE);
+
4975 env.close();
+
4976
+
4977 // 2. Mantissa larger than uint64 max
+
4978 try
+
4979 {
+
4980 tx[sfAssetsMaximum] = "18446744073709551617e5"; // uint64 max + 1
+
4981 env(tx, THISLINE);
+
4982 BEAST_EXPECT(false);
+
4983 }
+
4984 catch (parse_error const& e)
+
4985 {
+
4986 using namespace std::string_literals;
+
4987 BEAST_EXPECT(
+
4988 e.what() ==
+
4989 "invalidParamsField 'tx_json.AssetsMaximum' has invalid "
+
4990 "data."s);
+
4991 }
+
4992 }
+
4993 }
-
4992
-
4993public:
-
4994 void
-
-
4995 run() override
-
4996 {
-
4997 testSequences();
-
4998 testPreflight();
-
4999 testCreateFailXRP();
-
5000 testCreateFailIOU();
-
5001 testCreateFailMPT();
-
5002 testWithMPT();
-
5003 testWithIOU();
-
5004 testWithDomainCheck();
-
5005 testWithDomainCheckXRP();
-
5006 testNonTransferableShares();
-
5007 testFailedPseudoAccount();
-
5008 testScaleIOU();
-
5009 testRPC();
-
5010 testDelegate();
-
5011 testVaultClawbackBurnShares();
-
5012 testVaultClawbackAssets();
-
5013 testAssetsMaximum();
-
5014 }
+
4994
+
4995public:
+
4996 void
+
+
4997 run() override
+
4998 {
+
4999 testSequences();
+
5000 testPreflight();
+
5001 testCreateFailXRP();
+
5002 testCreateFailIOU();
+
5003 testCreateFailMPT();
+
5004 testWithMPT();
+
5005 testWithIOU();
+
5006 testWithDomainCheck();
+
5007 testWithDomainCheckXRP();
+
5008 testNonTransferableShares();
+
5009 testFailedPseudoAccount();
+
5010 testScaleIOU();
+
5011 testRPC();
+
5012 testDelegate();
+
5013 testVaultClawbackBurnShares();
+
5014 testVaultClawbackAssets();
+
5015 testAssetsMaximum();
+
5016 }
-
5015};
+
5017};
-
5016
-
5017BEAST_DEFINE_TESTSUITE_PRIO(Vault, app, xrpl, 1);
5018
-
5019} // namespace xrpl
+
5019BEAST_DEFINE_TESTSUITE_PRIO(Vault, app, xrpl, 1);
+
5020
+
5021} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
A generic endpoint for log messages.
Definition Journal.h:40
@@ -5163,25 +5165,25 @@ $(document).ready(function() { init_codefold(0); });
void apply(RawView &to)
Definition Sandbox.h:35
-
void testVaultClawbackAssets()
-
void testVaultClawbackBurnShares()
-
void run() override
Runs the suite.
- - +
void testVaultClawbackAssets()
+
void testVaultClawbackBurnShares()
+
void run() override
Runs the suite.
+ + -
void testWithDomainCheckXRP()
- - -
void testFailedPseudoAccount()
- +
void testWithDomainCheckXRP()
+ + +
void testFailedPseudoAccount()
+
static auto constexpr negativeAmount
- -
void testNonTransferableShares()
- + +
void testNonTransferableShares()
+ - - + +
xrpl::test::jtx::PrettyAsset PrettyAsset
constexpr value_type drops() const
Returns the number of drops.
Definition XRPAmount.h:157
Integers of any length that is a multiple of 32-bits.
Definition base_uint.h:66
diff --git a/View_8cpp_source.html b/View_8cpp_source.html index 0ab99818f3..a86dfed922 100644 --- a/View_8cpp_source.html +++ b/View_8cpp_source.html @@ -3736,14 +3736,14 @@ $(document).ready(function() { init_codefold(0); });
AccountID const & getIssuer() const
Definition Issue.h:25
Item const * findByType(KeyType type) const
Retrieve a format based on its type.
static LedgerFormats const & getInstance()
-
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:113
+
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:114
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:26
AccountID const & getIssuer() const
Definition MPTIssue.cpp:21
std::chrono::time_point< NetClock > time_point
Definition chrono.h:45
std::chrono::duration< rep, period > duration
Definition chrono.h:44
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
-
Number truncate() const noexcept
Definition Number.cpp:805
+
Number truncate() const noexcept
Definition Number.cpp:806
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition ReadView.h:90
@@ -3763,8 +3763,8 @@ $(document).ready(function() { init_codefold(0); });
constexpr TIss const & get() const
std::string getFullText() const override
Definition STAmount.cpp:629
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
-
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:554
-
void negate()
Definition STAmount.h:530
+
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:556
+
void negate()
Definition STAmount.h:532
std::uint64_t mantissa() const noexcept
Definition STAmount.h:435
bool negative() const noexcept
Definition STAmount.h:429
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
Definition STAmount.h:478
@@ -3781,7 +3781,7 @@ $(document).ready(function() { init_codefold(0); });
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
-
std::size_t size() const
+
std::size_t size() const
@@ -3855,7 +3855,7 @@ $(document).ready(function() { init_codefold(0); });
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Definition View.cpp:392
WaiveTransferFee
Definition View.h:24
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
static TER accountSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:2410
static TER rippleCreditMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2209
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
Definition View.cpp:112
@@ -3884,7 +3884,7 @@ $(document).ready(function() { init_codefold(0); });
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:3149
static TER rippleSendMultiIOU(ApplyView &view, AccountID const &senderID, Issue const &issue, MultiplePaymentDestinations const &receivers, STAmount &actual, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:1900
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
Definition STAmount.cpp:474
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
static STAmount getTrustLineBalance(ReadView const &view, SLE::const_ref sle, AccountID const &account, Currency const &currency, AccountID const &issuer, bool includeOppositeLimit, beast::Journal j)
Definition View.cpp:354
static TER withdrawToDestExceedsLimit(ReadView const &view, AccountID const &from, AccountID const &to, STAmount const &amount)
Definition View.cpp:1138
@@ -3912,7 +3912,7 @@ $(document).ready(function() { init_codefold(0); });
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2616
static TER accountSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:1969
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Definition STAmount.cpp:545
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:3174
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:3195
LedgerEntryType
Identifiers for on-ledger objects.
diff --git a/View_8h_source.html b/View_8h_source.html index 6a4fc5ab00..28bcea55b4 100644 --- a/View_8h_source.html +++ b/View_8h_source.html @@ -892,7 +892,7 @@ $(document).ready(function() { init_codefold(0); });
WaiveTransferFee
Definition View.h:24
-
Number root(Number f, unsigned d)
Definition Number.cpp:938
+
Number root(Number f, unsigned d)
Definition Number.cpp:939
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
Definition View.cpp:112
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
TER authorizeMPToken(ApplyView &view, XRPAmount const &priorBalance, MPTID const &mptIssuanceID, AccountID const &account, beast::Journal journal, std::uint32_t flags=0, std::optional< AccountID > holderID=std::nullopt)
Definition View.cpp:1326
@@ -911,7 +911,7 @@ $(document).ready(function() { init_codefold(0); });
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Definition View.cpp:138
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition View.cpp:878
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:3149
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:254
base_uint< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:44
diff --git a/View__test_8cpp_source.html b/View__test_8cpp_source.html index efa7978d8f..1596ff1d6a 100644 --- a/View__test_8cpp_source.html +++ b/View__test_8cpp_source.html @@ -1233,7 +1233,7 @@ $(document).ready(function() { init_codefold(0); });
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition Indexes.cpp:319
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
diff --git a/WSClient__test_8cpp_source.html b/WSClient__test_8cpp_source.html index ab4e249277..417079a708 100644 --- a/WSClient__test_8cpp_source.html +++ b/WSClient__test_8cpp_source.html @@ -119,7 +119,7 @@ $(document).ready(function() { init_codefold(0); });
32} // namespace test
33} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
-
Value & append(Value const &value)
Append value to array at the end.
+
Value & append(Value const &value)
Append value to array at the end.
A testsuite class.
Definition suite.h:51
void pass()
Record a successful test condition.
Definition suite.h:494
virtual Config & config()=0
@@ -132,7 +132,7 @@ $(document).ready(function() { init_codefold(0); });
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:261
@ arrayValue
array value (ordered list)
Definition json_value.h:25
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition WSClient.cpp:285
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/WalletPropose_8cpp_source.html b/WalletPropose_8cpp_source.html index 462ac3c7fa..37001df049 100644 --- a/WalletPropose_8cpp_source.html +++ b/WalletPropose_8cpp_source.html @@ -249,7 +249,7 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isMember(char const *key) const
Return true if the object has a member named key.
T floor(T... args)
diff --git a/Wallet_8cpp_source.html b/Wallet_8cpp_source.html index 71a32cf217..d9e833a938 100644 --- a/Wallet_8cpp_source.html +++ b/Wallet_8cpp_source.html @@ -222,150 +222,151 @@ $(document).ready(function() { init_codefold(0); });
124 auto [newpublicKey, newsecretKey] = randomKeyPair(KeyType::secp256k1);
125
126 session << str(
-
127 boost::format("INSERT INTO NodeIdentity (PublicKey,PrivateKey) "
-
128 "VALUES ('%s','%s');") %
-
129 toBase58(TokenType::NodePublic, newpublicKey) % toBase58(TokenType::NodePrivate, newsecretKey));
-
130
-
131 return {newpublicKey, newsecretKey};
-
132}
+
127 boost::format(
+
128 "INSERT INTO NodeIdentity (PublicKey,PrivateKey) "
+
129 "VALUES ('%s','%s');") %
+
130 toBase58(TokenType::NodePublic, newpublicKey) % toBase58(TokenType::NodePrivate, newsecretKey));
+
131
+
132 return {newpublicKey, newsecretKey};
+
133}
-
133
- -
-
135getPeerReservationTable(soci::session& session, beast::Journal j)
-
136{
- -
138 // These values must be boost::optionals (not std) because SOCI expects
-
139 // boost::optionals.
-
140 boost::optional<std::string> valPubKey, valDesc;
-
141 // We should really abstract the table and column names into constants,
-
142 // but no one else does. Because it is too tedious? It would be easy if we
-
143 // had a jOOQ for C++.
-
144 soci::statement st =
-
145 (session.prepare << "SELECT PublicKey, Description FROM PeerReservations;",
-
146 soci::into(valPubKey),
-
147 soci::into(valDesc));
-
148 st.execute();
-
149 while (st.fetch())
-
150 {
-
151 if (!valPubKey || !valDesc)
-
152 {
-
153 // This represents a `NULL` in a `NOT NULL` column. It should be
-
154 // unreachable.
-
155 continue;
-
156 }
-
157 auto const optNodeId = parseBase58<PublicKey>(TokenType::NodePublic, *valPubKey);
-
158 if (!optNodeId)
-
159 {
-
160 JLOG(j.warn()) << "load: not a public key: " << valPubKey;
-
161 continue;
-
162 }
-
163 table.insert(PeerReservation{*optNodeId, *valDesc});
-
164 }
-
165
-
166 return table;
-
167}
+
134
+ +
+
136getPeerReservationTable(soci::session& session, beast::Journal j)
+
137{
+ +
139 // These values must be boost::optionals (not std) because SOCI expects
+
140 // boost::optionals.
+
141 boost::optional<std::string> valPubKey, valDesc;
+
142 // We should really abstract the table and column names into constants,
+
143 // but no one else does. Because it is too tedious? It would be easy if we
+
144 // had a jOOQ for C++.
+
145 soci::statement st =
+
146 (session.prepare << "SELECT PublicKey, Description FROM PeerReservations;",
+
147 soci::into(valPubKey),
+
148 soci::into(valDesc));
+
149 st.execute();
+
150 while (st.fetch())
+
151 {
+
152 if (!valPubKey || !valDesc)
+
153 {
+
154 // This represents a `NULL` in a `NOT NULL` column. It should be
+
155 // unreachable.
+
156 continue;
+
157 }
+
158 auto const optNodeId = parseBase58<PublicKey>(TokenType::NodePublic, *valPubKey);
+
159 if (!optNodeId)
+
160 {
+
161 JLOG(j.warn()) << "load: not a public key: " << valPubKey;
+
162 continue;
+
163 }
+
164 table.insert(PeerReservation{*optNodeId, *valDesc});
+
165 }
+
166
+
167 return table;
+
168}
-
168
-
169void
-
-
170insertPeerReservation(soci::session& session, PublicKey const& nodeId, std::string const& description)
-
171{
-
172 auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
-
173 session << "INSERT INTO PeerReservations (PublicKey, Description) "
-
174 "VALUES (:nodeId, :desc) "
-
175 "ON CONFLICT (PublicKey) DO UPDATE SET "
-
176 "Description=excluded.Description",
-
177 soci::use(sNodeId), soci::use(description);
-
178}
+
169
+
170void
+
+
171insertPeerReservation(soci::session& session, PublicKey const& nodeId, std::string const& description)
+
172{
+
173 auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
+
174 session << "INSERT INTO PeerReservations (PublicKey, Description) "
+
175 "VALUES (:nodeId, :desc) "
+
176 "ON CONFLICT (PublicKey) DO UPDATE SET "
+
177 "Description=excluded.Description",
+
178 soci::use(sNodeId), soci::use(description);
+
179}
-
179
-
180void
-
-
181deletePeerReservation(soci::session& session, PublicKey const& nodeId)
-
182{
-
183 auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
-
184 session << "DELETE FROM PeerReservations WHERE PublicKey = :nodeId", soci::use(sNodeId);
-
185}
+
180
+
181void
+
+
182deletePeerReservation(soci::session& session, PublicKey const& nodeId)
+
183{
+
184 auto const sNodeId = toBase58(TokenType::NodePublic, nodeId);
+
185 session << "DELETE FROM PeerReservations WHERE PublicKey = :nodeId", soci::use(sNodeId);
+
186}
-
186
-
187bool
-
-
188createFeatureVotes(soci::session& session)
-
189{
-
190 soci::transaction tr(session);
-
191 std::string sql =
-
192 "SELECT count(*) FROM sqlite_master "
-
193 "WHERE type='table' AND name='FeatureVotes'";
-
194 // SOCI requires boost::optional (not std::optional) as the parameter.
-
195 boost::optional<int> featureVotesCount;
-
196 session << sql, soci::into(featureVotesCount);
-
197 bool exists = static_cast<bool>(*featureVotesCount);
-
198
-
199 // Create FeatureVotes table in WalletDB if it doesn't exist
-
200 if (!exists)
-
201 {
-
202 session << "CREATE TABLE FeatureVotes ( "
-
203 "AmendmentHash CHARACTER(64) NOT NULL, "
-
204 "AmendmentName TEXT, "
-
205 "Veto INTEGER NOT NULL );";
-
206 tr.commit();
-
207 }
-
208 return exists;
-
209}
+
187
+
188bool
+
+
189createFeatureVotes(soci::session& session)
+
190{
+
191 soci::transaction tr(session);
+
192 std::string sql =
+
193 "SELECT count(*) FROM sqlite_master "
+
194 "WHERE type='table' AND name='FeatureVotes'";
+
195 // SOCI requires boost::optional (not std::optional) as the parameter.
+
196 boost::optional<int> featureVotesCount;
+
197 session << sql, soci::into(featureVotesCount);
+
198 bool exists = static_cast<bool>(*featureVotesCount);
+
199
+
200 // Create FeatureVotes table in WalletDB if it doesn't exist
+
201 if (!exists)
+
202 {
+
203 session << "CREATE TABLE FeatureVotes ( "
+
204 "AmendmentHash CHARACTER(64) NOT NULL, "
+
205 "AmendmentName TEXT, "
+
206 "Veto INTEGER NOT NULL );";
+
207 tr.commit();
+
208 }
+
209 return exists;
+
210}
-
210
-
211void
-
- -
213 soci::session& session,
-
214 std::function<void(
-
215 boost::optional<std::string> amendment_hash,
-
216 boost::optional<std::string> amendment_name,
-
217 boost::optional<AmendmentVote> vote)> const& callback)
-
218{
-
219 // lambda that converts the internally stored int to an AmendmentVote.
-
220 auto intToVote = [](boost::optional<int> const& dbVote) -> boost::optional<AmendmentVote> {
-
221 return safe_cast<AmendmentVote>(dbVote.value_or(1));
-
222 };
-
223
-
224 soci::transaction tr(session);
-
225 std::string sql =
-
226 "SELECT AmendmentHash, AmendmentName, Veto FROM "
-
227 "( SELECT AmendmentHash, AmendmentName, Veto, RANK() OVER "
-
228 "( PARTITION BY AmendmentHash ORDER BY ROWID DESC ) "
-
229 "as rnk FROM FeatureVotes ) WHERE rnk = 1";
-
230 // SOCI requires boost::optional (not std::optional) as parameters.
-
231 boost::optional<std::string> amendment_hash;
-
232 boost::optional<std::string> amendment_name;
-
233 boost::optional<int> vote_to_veto;
-
234 soci::statement st =
-
235 (session.prepare << sql, soci::into(amendment_hash), soci::into(amendment_name), soci::into(vote_to_veto));
-
236 st.execute();
-
237 while (st.fetch())
-
238 {
-
239 callback(amendment_hash, amendment_name, intToVote(vote_to_veto));
-
240 }
-
241}
+
211
+
212void
+
+ +
214 soci::session& session,
+
215 std::function<void(
+
216 boost::optional<std::string> amendment_hash,
+
217 boost::optional<std::string> amendment_name,
+
218 boost::optional<AmendmentVote> vote)> const& callback)
+
219{
+
220 // lambda that converts the internally stored int to an AmendmentVote.
+
221 auto intToVote = [](boost::optional<int> const& dbVote) -> boost::optional<AmendmentVote> {
+
222 return safe_cast<AmendmentVote>(dbVote.value_or(1));
+
223 };
+
224
+
225 soci::transaction tr(session);
+
226 std::string sql =
+
227 "SELECT AmendmentHash, AmendmentName, Veto FROM "
+
228 "( SELECT AmendmentHash, AmendmentName, Veto, RANK() OVER "
+
229 "( PARTITION BY AmendmentHash ORDER BY ROWID DESC ) "
+
230 "as rnk FROM FeatureVotes ) WHERE rnk = 1";
+
231 // SOCI requires boost::optional (not std::optional) as parameters.
+
232 boost::optional<std::string> amendment_hash;
+
233 boost::optional<std::string> amendment_name;
+
234 boost::optional<int> vote_to_veto;
+
235 soci::statement st =
+
236 (session.prepare << sql, soci::into(amendment_hash), soci::into(amendment_name), soci::into(vote_to_veto));
+
237 st.execute();
+
238 while (st.fetch())
+
239 {
+
240 callback(amendment_hash, amendment_name, intToVote(vote_to_veto));
+
241 }
+
242}
-
242
-
243void
-
-
244voteAmendment(soci::session& session, uint256 const& amendment, std::string const& name, AmendmentVote vote)
-
245{
-
246 soci::transaction tr(session);
-
247 std::string sql =
-
248 "INSERT INTO FeatureVotes (AmendmentHash, AmendmentName, Veto) VALUES "
-
249 "('";
-
250 sql += to_string(amendment);
-
251 sql += "', '" + name;
-
252 sql += "', '" + std::to_string(safe_cast<int>(vote)) + "');";
-
253 session << sql;
-
254 tr.commit();
-
255}
+
243
+
244void
+
+
245voteAmendment(soci::session& session, uint256 const& amendment, std::string const& name, AmendmentVote vote)
+
246{
+
247 soci::transaction tr(session);
+
248 std::string sql =
+
249 "INSERT INTO FeatureVotes (AmendmentHash, AmendmentName, Veto) VALUES "
+
250 "('";
+
251 sql += to_string(amendment);
+
252 sql += "', '" + name;
+
253 sql += "', '" + std::to_string(safe_cast<int>(vote)) + "');";
+
254 session << sql;
+
255 tr.commit();
+
256}
-
256
-
257} // namespace xrpl
+
257
+
258} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
@@ -388,23 +389,23 @@ $(document).ready(function() { init_codefold(0); });
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
static void saveManifest(soci::session &session, std::string const &dbTable, std::string const &serialized)
Definition Wallet.cpp:52
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
-
void deletePeerReservation(soci::session &session, PublicKey const &nodeId)
deletePeerReservation Deletes an entry from the peer reservation table.
Definition Wallet.cpp:181
+
void deletePeerReservation(soci::session &session, PublicKey const &nodeId)
deletePeerReservation Deletes an entry from the peer reservation table.
Definition Wallet.cpp:182
std::pair< PublicKey, SecretKey > getNodeIdentity(soci::session &session)
Returns a stable public and private key for this node.
Definition Wallet.cpp:102
-
void insertPeerReservation(soci::session &session, PublicKey const &nodeId, std::string const &description)
insertPeerReservation Adds an entry to the peer reservation table.
Definition Wallet.cpp:170
-
std::unordered_set< PeerReservation, beast::uhash<>, KeyEqual > getPeerReservationTable(soci::session &session, beast::Journal j)
getPeerReservationTable Returns the peer reservation table.
Definition Wallet.cpp:135
+
void insertPeerReservation(soci::session &session, PublicKey const &nodeId, std::string const &description)
insertPeerReservation Adds an entry to the peer reservation table.
Definition Wallet.cpp:171
+
std::unordered_set< PeerReservation, beast::uhash<>, KeyEqual > getPeerReservationTable(soci::session &session, beast::Journal j)
getPeerReservationTable Returns the peer reservation table.
Definition Wallet.cpp:136
constexpr auto WalletDBName
Definition DBInit.h:85
void addValidatorManifest(soci::session &session, std::string const &serialized)
addValidatorManifest Saves the manifest of a validator to the database.
Definition Wallet.cpp:88
std::optional< Manifest > deserializeManifest(Slice s, beast::Journal journal)
Constructs Manifest from serialized string.
Definition Manifest.cpp:34
-
void readAmendments(soci::session &session, std::function< void(boost::optional< std::string > amendment_hash, boost::optional< std::string > amendment_name, boost::optional< AmendmentVote > vote)> const &callback)
readAmendments Reads all amendments from the FeatureVotes table.
Definition Wallet.cpp:212
+
void readAmendments(soci::session &session, std::function< void(boost::optional< std::string > amendment_hash, boost::optional< std::string > amendment_name, boost::optional< AmendmentVote > vote)> const &callback)
readAmendments Reads all amendments from the FeatureVotes table.
Definition Wallet.cpp:213
std::unique_ptr< DatabaseCon > makeWalletDB(DatabaseCon::Setup const &setup, beast::Journal j)
makeWalletDB Opens the wallet database and returns it.
Definition Wallet.cpp:9
-
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:188
+
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:189
AmendmentVote
Definition Wallet.h:119
void clearNodeIdentity(soci::session &session)
Delete any saved public/private key associated with this node.
Definition Wallet.cpp:96
constexpr std::array< char const *, 6 > WalletDBInit
Definition DBInit.h:87
std::unique_ptr< DatabaseCon > makeTestWalletDB(DatabaseCon::Setup const &setup, std::string const &dbname, beast::Journal j)
makeTestWalletDB Opens a test wallet database with an arbitrary name.
Definition Wallet.cpp:16
-
void voteAmendment(soci::session &session, uint256 const &amendment, std::string const &name, AmendmentVote vote)
voteAmendment Set the veto value for a particular amendment.
Definition Wallet.cpp:244
+
void voteAmendment(soci::session &session, uint256 const &amendment, std::string const &name, AmendmentVote vote)
voteAmendment Set the veto value for a particular amendment.
Definition Wallet.cpp:245
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition SociDB.cpp:124
diff --git a/Wallet_8h_source.html b/Wallet_8h_source.html index 56c16ba457..fa8e04b347 100644 --- a/Wallet_8h_source.html +++ b/Wallet_8h_source.html @@ -149,22 +149,22 @@ $(document).ready(function() { init_codefold(0); });
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void getManifests(soci::session &session, std::string const &dbTable, ManifestCache &mCache, beast::Journal j)
getManifests Loads a manifest from the wallet database and stores it in the cache.
Definition Wallet.cpp:23
void saveManifests(soci::session &session, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted, hash_map< PublicKey, Manifest > const &map, beast::Journal j)
saveManifests Saves all given manifests to the database.
Definition Wallet.cpp:63
-
void deletePeerReservation(soci::session &session, PublicKey const &nodeId)
deletePeerReservation Deletes an entry from the peer reservation table.
Definition Wallet.cpp:181
+
void deletePeerReservation(soci::session &session, PublicKey const &nodeId)
deletePeerReservation Deletes an entry from the peer reservation table.
Definition Wallet.cpp:182
std::pair< PublicKey, SecretKey > getNodeIdentity(soci::session &session)
Returns a stable public and private key for this node.
Definition Wallet.cpp:102
-
void insertPeerReservation(soci::session &session, PublicKey const &nodeId, std::string const &description)
insertPeerReservation Adds an entry to the peer reservation table.
Definition Wallet.cpp:170
-
std::unordered_set< PeerReservation, beast::uhash<>, KeyEqual > getPeerReservationTable(soci::session &session, beast::Journal j)
getPeerReservationTable Returns the peer reservation table.
Definition Wallet.cpp:135
+
void insertPeerReservation(soci::session &session, PublicKey const &nodeId, std::string const &description)
insertPeerReservation Adds an entry to the peer reservation table.
Definition Wallet.cpp:171
+
std::unordered_set< PeerReservation, beast::uhash<>, KeyEqual > getPeerReservationTable(soci::session &session, beast::Journal j)
getPeerReservationTable Returns the peer reservation table.
Definition Wallet.cpp:136
void addValidatorManifest(soci::session &session, std::string const &serialized)
addValidatorManifest Saves the manifest of a validator to the database.
Definition Wallet.cpp:88
base_uint< 256 > uint256
Definition base_uint.h:526
-
void readAmendments(soci::session &session, std::function< void(boost::optional< std::string > amendment_hash, boost::optional< std::string > amendment_name, boost::optional< AmendmentVote > vote)> const &callback)
readAmendments Reads all amendments from the FeatureVotes table.
Definition Wallet.cpp:212
+
void readAmendments(soci::session &session, std::function< void(boost::optional< std::string > amendment_hash, boost::optional< std::string > amendment_name, boost::optional< AmendmentVote > vote)> const &callback)
readAmendments Reads all amendments from the FeatureVotes table.
Definition Wallet.cpp:213
std::unique_ptr< DatabaseCon > makeWalletDB(DatabaseCon::Setup const &setup, beast::Journal j)
makeWalletDB Opens the wallet database and returns it.
Definition Wallet.cpp:9
-
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:188
+
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:189
AmendmentVote
Definition Wallet.h:119
void clearNodeIdentity(soci::session &session)
Delete any saved public/private key associated with this node.
Definition Wallet.cpp:96
std::unique_ptr< DatabaseCon > makeTestWalletDB(DatabaseCon::Setup const &setup, std::string const &dbname, beast::Journal j)
makeTestWalletDB Opens a test wallet database with an arbitrary name.
Definition Wallet.cpp:16
-
void voteAmendment(soci::session &session, uint256 const &amendment, std::string const &name, AmendmentVote vote)
voteAmendment Set the veto value for a particular amendment.
Definition Wallet.cpp:244
+
void voteAmendment(soci::session &session, uint256 const &amendment, std::string const &name, AmendmentVote vote)
voteAmendment Set the veto value for a particular amendment.
Definition Wallet.cpp:245
diff --git a/XChainAttestations_8cpp_source.html b/XChainAttestations_8cpp_source.html index d8b299c09e..d96a63392e 100644 --- a/XChainAttestations_8cpp_source.html +++ b/XChainAttestations_8cpp_source.html @@ -877,8 +877,8 @@ $(document).ready(function() { init_codefold(0); });
Represents a JSON value.
Definition json_value.h:130
-
bool isObject() const
-
bool isMember(char const *key) const
Return true if the object has a member named key.
+
bool isObject() const
+
bool isMember(char const *key) const
Return true if the object has a member named key.
Like std::vector<char> but better.
Definition Buffer.h:16
A public key.
Definition PublicKey.h:42
Identifies fields.
Definition SField.h:126
@@ -911,7 +911,7 @@ $(document).ready(function() { init_codefold(0); });
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig) noexcept
Verify a signature on a message.
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
SField const sfGeneric
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition base_uint.h:552
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
diff --git a/XChainBridge_8cpp_source.html b/XChainBridge_8cpp_source.html index 968df8682e..adfe339345 100644 --- a/XChainBridge_8cpp_source.html +++ b/XChainBridge_8cpp_source.html @@ -2244,14 +2244,14 @@ $(document).ready(function() { init_codefold(0); });
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
@ terNO_ACCOUNT
Definition TER.h:197
-
bool isTerRetry(TER x) noexcept
Definition TER.h:643
+
bool isTerRetry(TER x) noexcept
Definition TER.h:637
Unexpected(E(&)[N]) -> Unexpected< E const * >
bool isXRP(AccountID const &c)
Definition AccountID.h:70
@ tefBAD_LEDGER
Definition TER.h:150
-
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
+
bool isLegalNet(STAmount const &value)
Definition STAmount.h:568
constexpr std::uint32_t tfBridgeModifyMask
Definition TxFlags.h:247
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition Seed.cpp:57
@@ -2259,9 +2259,9 @@ $(document).ready(function() { init_codefold(0); });
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
STLedgerEntry SLE
constexpr std::uint32_t tfClearAccountCreateAmount
Definition TxFlags.h:246
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
-
bool isTefFailure(TER x) noexcept
Definition TER.h:637
+
bool isTefFailure(TER x) noexcept
Definition TER.h:631
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
Definition StrandFlow.h:81
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:955
AccountID calcAccountID(PublicKey const &pk)
@@ -2274,7 +2274,7 @@ $(document).ready(function() { init_codefold(0); });
@ temBAD_AMOUNT
Definition TER.h:69
@ temXCHAIN_EQUAL_DOOR_ACCOUNTS
Definition TER.h:111
@ temXCHAIN_BAD_PROOF
Definition TER.h:112
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
@ tecXCHAIN_INSUFF_CREATE_AMOUNT
Definition TER.h:327
@ tecDIR_FULL
Definition TER.h:268
@ tecUNFUNDED_PAYMENT
Definition TER.h:266
@@ -2303,7 +2303,7 @@ $(document).ready(function() { init_codefold(0); });
@ tecNO_ISSUER
Definition TER.h:280
@ tecDUPLICATE
Definition TER.h:296
@ tecNO_DST
Definition TER.h:271
-
bool isTecClaim(TER x) noexcept
Definition TER.h:656
+
bool isTecClaim(TER x) noexcept
Definition TER.h:650
@ lsfAllowTrustLineClawback
@ lsfDepositAuth
@ lsfRequireDestTag
@@ -2311,7 +2311,7 @@ $(document).ready(function() { init_codefold(0); });
@ no
Definition Steps.h:25
@ yes
Definition Steps.h:25
constexpr size_t xbridgeMaxAccountCreateClaims
Definition XChainBridge.h:9
-
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
+
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:574
@ tesSUCCESS
Definition TER.h:225
T push_back(T... args)
diff --git a/XChain__test_8cpp_source.html b/XChain__test_8cpp_source.html index 065fa1d0e6..eacbb55862 100644 --- a/XChain__test_8cpp_source.html +++ b/XChain__test_8cpp_source.html @@ -4580,7 +4580,7 @@ $(document).ready(function() { init_codefold(0); });
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
JValueVec claim_attestations(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, std::vector< jtx::Account > const &rewardAccounts, bool wasLockingChainSend, std::uint64_t claimID, std::optional< jtx::Account > const &dst, std::vector< jtx::signer > const &signers, std::size_t const numAtts, std::size_t const fromIdx)
Json::Value xchain_claim(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, Account const &dst)
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Json::Value bridge_modify(Account const &acc, Json::Value const &bridge, std::optional< STAmount > const &reward, std::optional< STAmount > const &minAccountCreate)
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
diff --git a/amount_8cpp_source.html b/amount_8cpp_source.html index 065c4d4f21..94f8da1fa2 100644 --- a/amount_8cpp_source.html +++ b/amount_8cpp_source.html @@ -112,102 +112,103 @@ $(document).ready(function() { init_codefold(0); });
29#endif
30
-
31PrettyAmount::operator AnyAmount() const
-
32{
-
33 return {amount_};
-
34}
+
31PrettyAmount::
+
32operator AnyAmount() const
+
33{
+
34 return {amount_};
+
35}
-
35
-
36template <typename T>
-
37static std::string
-
-
38to_places(T const d, std::uint8_t places)
-
39{
-
40 assert(places <= std::numeric_limits<T>::digits10);
-
41
- -
43 oss << std::setprecision(places) << std::fixed << d;
-
44
-
45 std::string out = oss.str();
-
46 out.erase(out.find_last_not_of('0') + 1, std::string::npos);
-
47 if (out.back() == '.')
-
48 out.pop_back();
-
49
-
50 return out;
-
51}
+
36
+
37template <typename T>
+
38static std::string
+
+
39to_places(T const d, std::uint8_t places)
+
40{
+
41 assert(places <= std::numeric_limits<T>::digits10);
+
42
+ +
44 oss << std::setprecision(places) << std::fixed << d;
+
45
+
46 std::string out = oss.str();
+
47 out.erase(out.find_last_not_of('0') + 1, std::string::npos);
+
48 if (out.back() == '.')
+
49 out.pop_back();
+
50
+
51 return out;
+
52}
-
52
-
- - -
55{
-
56 if (amount.value().native())
-
57 {
-
58 // measure in hundredths
-
59 auto const c = dropsPerXRP.drops() / 100;
-
60 auto const n = amount.value().mantissa();
-
61 if (n < c)
-
62 {
-
63 if (amount.value().negative())
-
64 os << "-" << n << " drops";
-
65 else
-
66 os << n << " drops";
-
67 return os;
-
68 }
-
69 auto const d = double(n) / dropsPerXRP.drops();
-
70 if (amount.value().negative())
-
71 os << "-";
-
72
-
73 os << to_places(d, 6) << " XRP";
-
74 }
-
75 else if (amount.value().holds<Issue>())
-
76 {
-
77 os << amount.value().getText() << "/" << to_string(amount.value().issue().currency) << "(" << amount.name()
-
78 << ")";
-
79 }
-
80 else
-
81 {
-
82 auto const& mptIssue = amount.value().asset().get<MPTIssue>();
-
83 os << amount.value().getText() << "/" << to_string(mptIssue) << "(" << amount.name() << ")";
-
84 }
-
85 return os;
-
86}
+
53
+
+ + +
56{
+
57 if (amount.value().native())
+
58 {
+
59 // measure in hundredths
+
60 auto const c = dropsPerXRP.drops() / 100;
+
61 auto const n = amount.value().mantissa();
+
62 if (n < c)
+
63 {
+
64 if (amount.value().negative())
+
65 os << "-" << n << " drops";
+
66 else
+
67 os << n << " drops";
+
68 return os;
+
69 }
+
70 auto const d = double(n) / dropsPerXRP.drops();
+
71 if (amount.value().negative())
+
72 os << "-";
+
73
+
74 os << to_places(d, 6) << " XRP";
+
75 }
+
76 else if (amount.value().holds<Issue>())
+
77 {
+
78 os << amount.value().getText() << "/" << to_string(amount.value().issue().currency) << "(" << amount.name()
+
79 << ")";
+
80 }
+
81 else
+
82 {
+
83 auto const& mptIssue = amount.value().asset().get<MPTIssue>();
+
84 os << amount.value().getText() << "/" << to_string(mptIssue) << "(" << amount.name() << ")";
+
85 }
+
86 return os;
+
87}
-
87
-
88//------------------------------------------------------------------------------
-
89
-
90XRP_t const XRP{};
-
91
- -
- -
94{
-
95 return {STAmount(issue(), 1, -81), account.name()};
-
96}
+
88
+
89//------------------------------------------------------------------------------
+
90
+
91XRP_t const XRP{};
+
92
+ +
+ +
95{
+
96 return {STAmount(issue(), 1, -81), account.name()};
+
97}
-
97
- -
- -
100{
-
101 return {STAmount(issue(), safe_cast<std::uint64_t>(m.n), -81), account.name()};
-
102}
+
98
+ +
+ +
101{
+
102 return {STAmount(issue(), safe_cast<std::uint64_t>(m.n), -81), account.name()};
+
103}
-
103
-
- -
105operator<<(std::ostream& os, IOU const& iou)
-
106{
-
107 os << to_string(iou.issue().currency) << "(" << iou.account.name() << ")";
-
108 return os;
-
109}
+
104
+
+ +
106operator<<(std::ostream& os, IOU const& iou)
+
107{
+
108 os << to_string(iou.issue().currency) << "(" << iou.account.name() << ")";
+
109 return os;
+
110}
-
110
-
111any_t const any{};
-
112
-
113} // namespace jtx
-
114} // namespace test
-
115} // namespace xrpl
+
111
+
112any_t const any{};
+
113
+
114} // namespace jtx
+
115} // namespace test
+
116} // namespace xrpl
@@ -224,10 +225,10 @@ $(document).ready(function() { init_codefold(0); });
T fixed(T... args)
-
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:111
-
static std::string to_places(T const d, std::uint8_t places)
Definition amount.cpp:38
-
std::ostream & operator<<(std::ostream &os, PrettyAmount const &amount)
Definition amount.cpp:54
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:112
+
static std::string to_places(T const d, std::uint8_t places)
Definition amount.cpp:39
+
std::ostream & operator<<(std::ostream &os, PrettyAmount const &amount)
Definition amount.cpp:55
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
constexpr XRPAmount dropsPerXRP
auto const amount
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/applySteps_8cpp_source.html b/applySteps_8cpp_source.html index b86d084946..a1dde22a24 100644 --- a/applySteps_8cpp_source.html +++ b/applySteps_8cpp_source.html @@ -571,13 +571,13 @@ $(document).ready(function() { init_codefold(0); });
PreflightResult preflight(Application &app, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
static std::pair< NotTEC, TxConsequences > invoke_preflight(PreflightContext const &ctx)
static TER invoke_preclaim(PreclaimContext const &ctx)
-
TERSubset< CanCvtToTER > TER
Definition TER.h:620
+
TERSubset< CanCvtToTER > TER
Definition TER.h:614
TxConsequences consequences_helper(PreflightContext const &ctx)
ApplyResult doApply(PreclaimResult const &preclaimResult, Application &app, OpenView &view)
Apply a prechecked transaction to an OpenView.
XRPAmount calculateDefaultBaseFee(ReadView const &view, STTx const &tx)
Return the minimum fee that an "ordinary" transaction would pay.
ApplyFlags
Definition ApplyView.h:10
@ temUNKNOWN
Definition TER.h:104
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
PreclaimResult preclaim(PreflightResult const &preflightResult, Application &app, OpenView const &view)
Gate a transaction based on static ledger information.
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
@ tesSUCCESS
Definition TER.h:225
diff --git a/applySteps_8h_source.html b/applySteps_8h_source.html index c702d48013..cc4c05edbb 100644 --- a/applySteps_8h_source.html +++ b/applySteps_8h_source.html @@ -339,7 +339,7 @@ $(document).ready(function() { init_codefold(0); });
@ tapRETRY
Definition ApplyView.h:19
PreclaimResult preclaim(PreflightResult const &preflightResult, Application &app, OpenView const &view)
Gate a transaction based on static ledger information.
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
-
bool isTecClaim(TER x) noexcept
Definition TER.h:656
+
bool isTecClaim(TER x) noexcept
Definition TER.h:650
@ tesSUCCESS
Definition TER.h:225
diff --git a/apply_8cpp_source.html b/apply_8cpp_source.html index 4c89497e75..b82641223f 100644 --- a/apply_8cpp_source.html +++ b/apply_8cpp_source.html @@ -347,7 +347,7 @@ $(document).ready(function() { init_codefold(0); });
Routing table for objects identified by hash.
Definition HashRouter.h:77
HashRouterFlags getFlags(uint256 const &key)
bool setFlags(uint256 const &key, HashRouterFlags flags)
Set the flags on a hash.
-
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:190
+
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:192
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:45
void apply(TxsRawView &to) const
Apply changes.
Definition OpenView.cpp:101
Rules const & rules() const override
Returns the tx processing rules.
Definition OpenView.cpp:123
@@ -387,7 +387,7 @@ $(document).ready(function() { init_codefold(0); });
constexpr std::uint32_t tfAllOrNothing
Definition TxFlags.h:256
std::string transToken(TER code)
Definition TER.cpp:243
constexpr HashRouterFlags SF_SIGGOOD
Definition apply.cpp:15
-
bool isTefFailure(TER x) noexcept
Definition TER.h:637
+
bool isTefFailure(TER x) noexcept
Definition TER.h:631
bool passesLocalChecks(STObject const &st, std::string &)
Definition STTx.cpp:737
HashRouterFlags
Definition HashRouter.h:14
@@ -402,14 +402,14 @@ $(document).ready(function() { init_codefold(0); });
ApplyFlags
Definition ApplyView.h:10
@ tapRETRY
Definition ApplyView.h:19
@ tapBATCH
Definition ApplyView.h:25
-
bool isTelLocal(TER x) noexcept
Definition TER.h:625
+
bool isTelLocal(TER x) noexcept
Definition TER.h:619
static bool applyBatchTransactions(Application &app, OpenView &batchView, STTx const &batchTxn, beast::Journal j)
Definition apply.cpp:136
-
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
+
bool isTesSuccess(TER x) noexcept
Definition TER.h:643
PreclaimResult preclaim(PreflightResult const &preflightResult, Application &app, OpenView const &view)
Gate a transaction based on static ledger information.
constexpr std::uint32_t tfUntilFailure
Definition TxFlags.h:258
-
bool isTecClaim(TER x) noexcept
Definition TER.h:656
+
bool isTecClaim(TER x) noexcept
Definition TER.h:650
void forceValidity(HashRouter &router, uint256 const &txid, Validity validity)
Sets the validity of a given transaction in the cache.
Definition apply.cpp:90
-
bool isTemMalformed(TER x) noexcept
Definition TER.h:631
+
bool isTemMalformed(TER x) noexcept
Definition TER.h:625
T what(T... args)
diff --git a/balance_8h_source.html b/balance_8h_source.html index cfc329f017..f716d51739 100644 --- a/balance_8h_source.html +++ b/balance_8h_source.html @@ -136,7 +136,7 @@ $(document).ready(function() { init_codefold(0); });
balance(Account const &account, STAmount const &value)
Definition balance.h:34
Account const account_
Definition balance.h:22
STAmount const value_
Definition balance.h:23
-
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
+
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:91
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
diff --git a/batch_8h_source.html b/batch_8h_source.html index 4bf8cb08b7..c4f7c9fc86 100644 --- a/batch_8h_source.html +++ b/batch_8h_source.html @@ -236,7 +236,7 @@ $(document).ready(function() { init_codefold(0); });
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
UInt asUInt() const
-
Value removeMember(char const *key)
Remove and return the named member.
+
Value removeMember(char const *key)
Remove and return the named member.
Immutable cryptographic account descriptor.
Definition Account.h:19
diff --git a/classJson_1_1Value.html b/classJson_1_1Value.html index cfa14cf3f2..63d74999f5 100644 --- a/classJson_1_1Value.html +++ b/classJson_1_1Value.html @@ -961,7 +961,7 @@ Friends

isNull() tests to see if this field is null.

Don't use this method to test for emptiness: use empty().

-

Definition at line 1019 of file json_value.cpp.

+

Definition at line 1020 of file json_value.cpp.

@@ -980,7 +980,7 @@ Friends
-

Definition at line 1025 of file json_value.cpp.

+

Definition at line 1026 of file json_value.cpp.

@@ -999,7 +999,7 @@ Friends
-

Definition at line 1031 of file json_value.cpp.

+

Definition at line 1032 of file json_value.cpp.

@@ -1018,7 +1018,7 @@ Friends
-

Definition at line 1037 of file json_value.cpp.

+

Definition at line 1038 of file json_value.cpp.

@@ -1037,7 +1037,7 @@ Friends
-

Definition at line 1043 of file json_value.cpp.

+

Definition at line 1044 of file json_value.cpp.

@@ -1056,7 +1056,7 @@ Friends
-

Definition at line 1049 of file json_value.cpp.

+

Definition at line 1050 of file json_value.cpp.

@@ -1075,7 +1075,7 @@ Friends
-

Definition at line 1055 of file json_value.cpp.

+

Definition at line 1056 of file json_value.cpp.

@@ -1094,7 +1094,7 @@ Friends
-

Definition at line 1061 of file json_value.cpp.

+

Definition at line 1062 of file json_value.cpp.

@@ -1113,7 +1113,7 @@ Friends
-

Definition at line 1067 of file json_value.cpp.

+

Definition at line 1068 of file json_value.cpp.

@@ -1132,7 +1132,7 @@ Friends
-

Definition at line 1073 of file json_value.cpp.

+

Definition at line 1074 of file json_value.cpp.

@@ -1151,7 +1151,7 @@ Friends
-

Definition at line 1079 of file json_value.cpp.

+

Definition at line 1080 of file json_value.cpp.

@@ -1170,7 +1170,7 @@ Friends
-

Definition at line 1085 of file json_value.cpp.

+

Definition at line 1086 of file json_value.cpp.

@@ -1263,7 +1263,7 @@ Friends
Precondition
type() is arrayValue, objectValue, or nullValue
Postcondition
type() is unchanged
-

Definition at line 795 of file json_value.cpp.

+

Definition at line 796 of file json_value.cpp.

@@ -1286,7 +1286,7 @@ Friends

Access an array element (zero based index ).

If the array contains less than index element, then null value are inserted in the array so that its size is index+1. (You may need to say 'value[0u]' to get your compiler to distinguish this from the operator[] which takes a string.)

-

Definition at line 812 of file json_value.cpp.

+

Definition at line 813 of file json_value.cpp.

@@ -1308,7 +1308,7 @@ Friends

Access an array element (zero based index ) (You may need to say 'value[0u]' to get your compiler to distinguish this from the operator[] which takes a string.)

-

Definition at line 831 of file json_value.cpp.

+

Definition at line 832 of file json_value.cpp.

@@ -1340,7 +1340,7 @@ Friends

If the array contains at least index+1 elements, returns the element value, otherwise returns defaultValue.

-

Definition at line 874 of file json_value.cpp.

+

Definition at line 875 of file json_value.cpp.

@@ -1362,7 +1362,7 @@ Friends

Return true if index < size().

-

Definition at line 881 of file json_value.cpp.

+

Definition at line 882 of file json_value.cpp.

@@ -1385,7 +1385,7 @@ Friends

Append value to array at the end.

Equivalent to jsonvalue[jsonvalue.size()] = value;

-

Definition at line 928 of file json_value.cpp.

+

Definition at line 929 of file json_value.cpp.

@@ -1405,7 +1405,7 @@ Friends
-

Definition at line 934 of file json_value.cpp.

+

Definition at line 935 of file json_value.cpp.

@@ -1427,7 +1427,7 @@ Friends

Access an object value by name, create a null member if it does not exist.

-

Definition at line 848 of file json_value.cpp.

+

Definition at line 849 of file json_value.cpp.

@@ -1449,7 +1449,7 @@ Friends

Access an object value by name, returns null if there is no member with that name.

-

Definition at line 887 of file json_value.cpp.

+

Definition at line 888 of file json_value.cpp.

@@ -1471,7 +1471,7 @@ Friends

Access an object value by name, create a null member if it does not exist.

-

Definition at line 904 of file json_value.cpp.

+

Definition at line 905 of file json_value.cpp.

@@ -1493,7 +1493,7 @@ Friends

Access an object value by name, returns null if there is no member with that name.

-

Definition at line 910 of file json_value.cpp.

+

Definition at line 911 of file json_value.cpp.

@@ -1518,7 +1518,7 @@ Friends
static const StaticString code("code");
object[code] = 1234;
-

Definition at line 916 of file json_value.cpp.

+

Definition at line 917 of file json_value.cpp.

@@ -1538,7 +1538,7 @@ Friends
-

Definition at line 922 of file json_value.cpp.

+

Definition at line 923 of file json_value.cpp.

@@ -1570,7 +1570,7 @@ Friends

Return the member named key if it exist, defaultValue otherwise.

-

Definition at line 940 of file json_value.cpp.

+

Definition at line 941 of file json_value.cpp.

@@ -1602,7 +1602,7 @@ Friends

Return the member named key if it exist, defaultValue otherwise.

-

Definition at line 947 of file json_value.cpp.

+

Definition at line 948 of file json_value.cpp.

@@ -1627,7 +1627,7 @@ Friends
Precondition
type() is objectValue or nullValue
Postcondition
type() is unchanged
-

Definition at line 953 of file json_value.cpp.

+

Definition at line 954 of file json_value.cpp.

@@ -1649,7 +1649,7 @@ Friends

Same as removeMember(const char*)

-

Definition at line 972 of file json_value.cpp.

+

Definition at line 973 of file json_value.cpp.

@@ -1671,7 +1671,7 @@ Friends

Return true if the object has a member named key.

-

Definition at line 978 of file json_value.cpp.

+

Definition at line 979 of file json_value.cpp.

@@ -1693,7 +1693,7 @@ Friends

Return true if the object has a member named key.

-

Definition at line 988 of file json_value.cpp.

+

Definition at line 989 of file json_value.cpp.

@@ -1715,7 +1715,7 @@ Friends

Return true if the object has a member named key.

-

Definition at line 994 of file json_value.cpp.

+

Definition at line 995 of file json_value.cpp.

@@ -1738,7 +1738,7 @@ Friends

If null, return an empty list.

Precondition
type() is objectValue or nullValue
Postcondition
if type() was nullValue, it remains nullValue
-

Definition at line 1000 of file json_value.cpp.

+

Definition at line 1001 of file json_value.cpp.

@@ -1757,7 +1757,7 @@ Friends
-

Definition at line 1091 of file json_value.cpp.

+

Definition at line 1092 of file json_value.cpp.

@@ -1776,7 +1776,7 @@ Friends
-

Definition at line 1098 of file json_value.cpp.

+

Definition at line 1099 of file json_value.cpp.

@@ -1795,7 +1795,7 @@ Friends
-

Definition at line 1116 of file json_value.cpp.

+

Definition at line 1117 of file json_value.cpp.

@@ -1814,7 +1814,7 @@ Friends
-

Definition at line 1134 of file json_value.cpp.

+

Definition at line 1135 of file json_value.cpp.

@@ -1833,7 +1833,7 @@ Friends
-

Definition at line 1151 of file json_value.cpp.

+

Definition at line 1152 of file json_value.cpp.

@@ -1871,7 +1871,7 @@ Friends
-

Definition at line 854 of file json_value.cpp.

+

Definition at line 855 of file json_value.cpp.

diff --git a/classxrpl_1_1Application.html b/classxrpl_1_1Application.html index 13da16f9d1..39a898e45f 100644 --- a/classxrpl_1_1Application.html +++ b/classxrpl_1_1Application.html @@ -360,7 +360,7 @@ Private Attributes
-

Definition at line 1995 of file Application.cpp.

+

Definition at line 1999 of file Application.cpp.

diff --git a/classxrpl_1_1ApplicationImp.html b/classxrpl_1_1ApplicationImp.html index 08ca267eb9..62d945371b 100644 --- a/classxrpl_1_1ApplicationImp.html +++ b/classxrpl_1_1ApplicationImp.html @@ -571,7 +571,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 1036 of file Application.cpp.

+

Definition at line 1040 of file Application.cpp.

@@ -601,7 +601,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 1365 of file Application.cpp.

+

Definition at line 1369 of file Application.cpp.

@@ -630,7 +630,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 1390 of file Application.cpp.

+

Definition at line 1394 of file Application.cpp.

@@ -660,7 +660,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 1481 of file Application.cpp.

+

Definition at line 1485 of file Application.cpp.

@@ -689,7 +689,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 1495 of file Application.cpp.

+

Definition at line 1499 of file Application.cpp.

@@ -719,7 +719,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 1501 of file Application.cpp.

+

Definition at line 1505 of file Application.cpp.

@@ -748,7 +748,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 1507 of file Application.cpp.

+

Definition at line 1511 of file Application.cpp.

@@ -777,7 +777,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 1513 of file Application.cpp.

+

Definition at line 1517 of file Application.cpp.

@@ -808,7 +808,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 441 of file Application.cpp.

+

Definition at line 445 of file Application.cpp.

@@ -837,7 +837,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 447 of file Application.cpp.

+

Definition at line 451 of file Application.cpp.

@@ -866,7 +866,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 453 of file Application.cpp.

+

Definition at line 457 of file Application.cpp.

@@ -895,7 +895,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 459 of file Application.cpp.

+

Definition at line 463 of file Application.cpp.

@@ -924,7 +924,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 465 of file Application.cpp.

+

Definition at line 469 of file Application.cpp.

@@ -953,7 +953,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 471 of file Application.cpp.

+

Definition at line 475 of file Application.cpp.

@@ -982,7 +982,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 477 of file Application.cpp.

+

Definition at line 481 of file Application.cpp.

@@ -1011,7 +1011,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 483 of file Application.cpp.

+

Definition at line 487 of file Application.cpp.

@@ -1040,7 +1040,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 492 of file Application.cpp.

+

Definition at line 496 of file Application.cpp.

@@ -1069,7 +1069,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 501 of file Application.cpp.

+

Definition at line 505 of file Application.cpp.

@@ -1098,7 +1098,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 507 of file Application.cpp.

+

Definition at line 511 of file Application.cpp.

@@ -1127,7 +1127,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 517 of file Application.cpp.

+

Definition at line 521 of file Application.cpp.

@@ -1156,7 +1156,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 523 of file Application.cpp.

+

Definition at line 527 of file Application.cpp.

@@ -1185,7 +1185,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 529 of file Application.cpp.

+

Definition at line 533 of file Application.cpp.

@@ -1214,7 +1214,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 535 of file Application.cpp.

+

Definition at line 539 of file Application.cpp.

@@ -1243,7 +1243,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 541 of file Application.cpp.

+

Definition at line 545 of file Application.cpp.

@@ -1272,7 +1272,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 547 of file Application.cpp.

+

Definition at line 551 of file Application.cpp.

@@ -1301,7 +1301,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 553 of file Application.cpp.

+

Definition at line 557 of file Application.cpp.

@@ -1330,7 +1330,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 559 of file Application.cpp.

+

Definition at line 563 of file Application.cpp.

@@ -1360,7 +1360,7 @@ Private Attributes
-

Definition at line 565 of file Application.cpp.

+

Definition at line 569 of file Application.cpp.

@@ -1389,7 +1389,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 572 of file Application.cpp.

+

Definition at line 576 of file Application.cpp.

@@ -1418,7 +1418,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 578 of file Application.cpp.

+

Definition at line 582 of file Application.cpp.

@@ -1447,7 +1447,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 584 of file Application.cpp.

+

Definition at line 588 of file Application.cpp.

@@ -1476,7 +1476,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 590 of file Application.cpp.

+

Definition at line 594 of file Application.cpp.

@@ -1505,7 +1505,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 596 of file Application.cpp.

+

Definition at line 600 of file Application.cpp.

@@ -1534,7 +1534,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 602 of file Application.cpp.

+

Definition at line 606 of file Application.cpp.

@@ -1563,7 +1563,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 608 of file Application.cpp.

+

Definition at line 612 of file Application.cpp.

@@ -1592,7 +1592,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 614 of file Application.cpp.

+

Definition at line 618 of file Application.cpp.

@@ -1621,7 +1621,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 620 of file Application.cpp.

+

Definition at line 624 of file Application.cpp.

@@ -1650,7 +1650,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 626 of file Application.cpp.

+

Definition at line 630 of file Application.cpp.

@@ -1679,7 +1679,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 632 of file Application.cpp.

+

Definition at line 636 of file Application.cpp.

@@ -1708,7 +1708,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 638 of file Application.cpp.

+

Definition at line 642 of file Application.cpp.

@@ -1737,7 +1737,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 644 of file Application.cpp.

+

Definition at line 648 of file Application.cpp.

@@ -1766,7 +1766,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 650 of file Application.cpp.

+

Definition at line 654 of file Application.cpp.

@@ -1795,7 +1795,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 656 of file Application.cpp.

+

Definition at line 660 of file Application.cpp.

@@ -1824,7 +1824,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 662 of file Application.cpp.

+

Definition at line 666 of file Application.cpp.

@@ -1853,7 +1853,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 668 of file Application.cpp.

+

Definition at line 672 of file Application.cpp.

@@ -1882,7 +1882,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 674 of file Application.cpp.

+

Definition at line 678 of file Application.cpp.

@@ -1911,7 +1911,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 680 of file Application.cpp.

+

Definition at line 684 of file Application.cpp.

@@ -1940,7 +1940,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 686 of file Application.cpp.

+

Definition at line 690 of file Application.cpp.

@@ -1969,7 +1969,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 692 of file Application.cpp.

+

Definition at line 696 of file Application.cpp.

@@ -1998,7 +1998,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 698 of file Application.cpp.

+

Definition at line 702 of file Application.cpp.

@@ -2027,7 +2027,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 704 of file Application.cpp.

+

Definition at line 708 of file Application.cpp.

@@ -2056,7 +2056,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 710 of file Application.cpp.

+

Definition at line 714 of file Application.cpp.

@@ -2085,7 +2085,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 716 of file Application.cpp.

+

Definition at line 720 of file Application.cpp.

@@ -2114,7 +2114,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 723 of file Application.cpp.

+

Definition at line 727 of file Application.cpp.

@@ -2143,7 +2143,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 730 of file Application.cpp.

+

Definition at line 734 of file Application.cpp.

@@ -2174,7 +2174,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 740 of file Application.cpp.

+

Definition at line 744 of file Application.cpp.

@@ -2204,7 +2204,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 1930 of file Application.cpp.

+

Definition at line 1934 of file Application.cpp.

@@ -2234,7 +2234,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 1978 of file Application.cpp.

+

Definition at line 1982 of file Application.cpp.

@@ -2253,7 +2253,7 @@ Private Attributes
-

Definition at line 755 of file Application.cpp.

+

Definition at line 759 of file Application.cpp.

@@ -2272,7 +2272,7 @@ Private Attributes
-

Definition at line 782 of file Application.cpp.

+

Definition at line 786 of file Application.cpp.

@@ -2305,7 +2305,7 @@ Private Attributes

Reimplemented from beast::PropertyStream::Source.

-

Definition at line 816 of file Application.cpp.

+

Definition at line 820 of file Application.cpp.

@@ -2324,7 +2324,7 @@ Private Attributes
-

Definition at line 823 of file Application.cpp.

+

Definition at line 827 of file Application.cpp.

@@ -2343,7 +2343,7 @@ Private Attributes
-

Definition at line 848 of file Application.cpp.

+

Definition at line 852 of file Application.cpp.

@@ -2362,7 +2362,7 @@ Private Attributes
-

Definition at line 873 of file Application.cpp.

+

Definition at line 877 of file Application.cpp.

@@ -2393,7 +2393,7 @@ Private Attributes

Implements xrpl::Application.

-

Definition at line 994 of file Application.cpp.

+

Definition at line 998 of file Application.cpp.

@@ -2422,7 +2422,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 1000 of file Application.cpp.

+

Definition at line 1004 of file Application.cpp.

@@ -2449,7 +2449,7 @@ Private Attributes
-

Definition at line 1538 of file Application.cpp.

+

Definition at line 1542 of file Application.cpp.

@@ -2476,7 +2476,7 @@ Private Attributes
-

Definition at line 1559 of file Application.cpp.

+

Definition at line 1563 of file Application.cpp.

@@ -2504,7 +2504,7 @@ Private Attributes
-

Definition at line 1602 of file Application.cpp.

+

Definition at line 1606 of file Application.cpp.

@@ -2554,7 +2554,7 @@ Private Attributes
-

Definition at line 1734 of file Application.cpp.

+

Definition at line 1738 of file Application.cpp.

@@ -2581,7 +2581,7 @@ Private Attributes
-

Definition at line 1984 of file Application.cpp.

+

Definition at line 1988 of file Application.cpp.

@@ -2610,7 +2610,7 @@ Private Attributes

Implements xrpl::ServiceRegistry.

-

Definition at line 1026 of file Application.cpp.

+

Definition at line 1030 of file Application.cpp.

@@ -4010,7 +4010,7 @@ template<class Derived >
-

Definition at line 1008 of file Application.cpp.

+

Definition at line 1012 of file Application.cpp.

diff --git a/classxrpl_1_1ApplyContext.html b/classxrpl_1_1ApplyContext.html index ee51f76d82..e4686cf5e0 100644 --- a/classxrpl_1_1ApplyContext.html +++ b/classxrpl_1_1ApplyContext.html @@ -623,7 +623,7 @@ Private Attributes
-

Definition at line 100 of file ApplyContext.h.

+

Definition at line 101 of file ApplyContext.h.

@@ -842,7 +842,7 @@ template<std::size_t... Is>
-

Definition at line 122 of file ApplyContext.h.

+

Definition at line 123 of file ApplyContext.h.

@@ -866,7 +866,7 @@ template<std::size_t... Is>
-

Definition at line 123 of file ApplyContext.h.

+

Definition at line 124 of file ApplyContext.h.

@@ -890,7 +890,7 @@ template<std::size_t... Is>
-

Definition at line 124 of file ApplyContext.h.

+

Definition at line 125 of file ApplyContext.h.

@@ -914,7 +914,7 @@ template<std::size_t... Is>
-

Definition at line 127 of file ApplyContext.h.

+

Definition at line 128 of file ApplyContext.h.

diff --git a/classxrpl_1_1BaseWSPeer.html b/classxrpl_1_1BaseWSPeer.html index 01461b3813..6168156e7d 100644 --- a/classxrpl_1_1BaseWSPeer.html +++ b/classxrpl_1_1BaseWSPeer.html @@ -629,7 +629,7 @@ template<class Handler , class Impl >

Implements xrpl::WSSession.

-

Definition at line 191 of file BaseWSPeer.h.

+

Definition at line 192 of file BaseWSPeer.h.

@@ -660,7 +660,7 @@ template<class Handler , class Impl >

Reimplemented from xrpl::BasePeer< Handler, Impl >.

-

Definition at line 213 of file BaseWSPeer.h.

+

Definition at line 214 of file BaseWSPeer.h.

@@ -692,7 +692,7 @@ template<class Handler , class Impl >

Implements xrpl::WSSession.

-

Definition at line 220 of file BaseWSPeer.h.

+

Definition at line 221 of file BaseWSPeer.h.

@@ -726,7 +726,7 @@ template<class Handler , class Impl >

Implements xrpl::WSSession.

-

Definition at line 242 of file BaseWSPeer.h.

+

Definition at line 243 of file BaseWSPeer.h.

@@ -785,7 +785,7 @@ template<class Handler , class Impl >
-

Definition at line 251 of file BaseWSPeer.h.

+

Definition at line 252 of file BaseWSPeer.h.

@@ -814,7 +814,7 @@ template<class Handler , class Impl >
-

Definition at line 261 of file BaseWSPeer.h.

+

Definition at line 262 of file BaseWSPeer.h.

@@ -844,7 +844,7 @@ template<class Handler , class Impl >
-

Definition at line 270 of file BaseWSPeer.h.

+

Definition at line 271 of file BaseWSPeer.h.

@@ -874,7 +874,7 @@ template<class Handler , class Impl >
-

Definition at line 294 of file BaseWSPeer.h.

+

Definition at line 295 of file BaseWSPeer.h.

@@ -903,7 +903,7 @@ template<class Handler , class Impl >
-

Definition at line 311 of file BaseWSPeer.h.

+

Definition at line 312 of file BaseWSPeer.h.

@@ -933,7 +933,7 @@ template<class Handler , class Impl >
-

Definition at line 321 of file BaseWSPeer.h.

+

Definition at line 322 of file BaseWSPeer.h.

@@ -963,7 +963,7 @@ template<class Handler , class Impl >
-

Definition at line 337 of file BaseWSPeer.h.

+

Definition at line 338 of file BaseWSPeer.h.

@@ -992,7 +992,7 @@ template<class Handler , class Impl >
-

Definition at line 344 of file BaseWSPeer.h.

+

Definition at line 345 of file BaseWSPeer.h.

@@ -1021,7 +1021,7 @@ template<class Handler , class Impl >
-

Definition at line 366 of file BaseWSPeer.h.

+

Definition at line 367 of file BaseWSPeer.h.

@@ -1051,7 +1051,7 @@ template<class Handler , class Impl >
-

Definition at line 380 of file BaseWSPeer.h.

+

Definition at line 381 of file BaseWSPeer.h.

@@ -1091,7 +1091,7 @@ template<class Handler , class Impl >
-

Definition at line 392 of file BaseWSPeer.h.

+

Definition at line 393 of file BaseWSPeer.h.

@@ -1121,7 +1121,7 @@ template<class Handler , class Impl >
-

Definition at line 411 of file BaseWSPeer.h.

+

Definition at line 412 of file BaseWSPeer.h.

@@ -1163,7 +1163,7 @@ template<class String >
-

Definition at line 439 of file BaseWSPeer.h.

+

Definition at line 440 of file BaseWSPeer.h.

@@ -1249,7 +1249,7 @@ template<class Handler , class Impl >
-

Definition at line 439 of file BaseWSPeer.h.

+

Definition at line 440 of file BaseWSPeer.h.

diff --git a/classxrpl_1_1CanCvtToNotTEC.html b/classxrpl_1_1CanCvtToNotTEC.html index c4d4d4b16f..7d994f1bd8 100644 --- a/classxrpl_1_1CanCvtToNotTEC.html +++ b/classxrpl_1_1CanCvtToNotTEC.html @@ -101,7 +101,7 @@ Collaboration diagram for xrpl::CanCvtToNotTEC< FROM >:

Detailed Description

template<typename FROM>
class xrpl::CanCvtToNotTEC< FROM >
-

Definition at line 556 of file TER.h.

+

Definition at line 550 of file TER.h.

[legend]

Detailed Description

-

Definition at line 568 of file TER.h.

+

Definition at line 562 of file TER.h.

[legend]

Detailed Description

-

Definition at line 560 of file TER.h.

+

Definition at line 554 of file TER.h.

[legend]

Detailed Description

-

Definition at line 564 of file TER.h.

+

Definition at line 558 of file TER.h.

[legend]

Detailed Description

-

Definition at line 572 of file TER.h.

+

Definition at line 566 of file TER.h.

[legend]

Detailed Description

-

Definition at line 576 of file TER.h.

+

Definition at line 570 of file TER.h.

Detailed Description

template<typename FROM>
class xrpl::CanCvtToTER< FROM >
-

Definition at line 587 of file TER.h.

+

Definition at line 581 of file TER.h.

[legend]

Detailed Description

-

Definition at line 615 of file TER.h.

+

Definition at line 609 of file TER.h.

[legend]

Detailed Description

-

Definition at line 611 of file TER.h.

+

Definition at line 605 of file TER.h.

[legend]

Detailed Description

-

Definition at line 599 of file TER.h.

+

Definition at line 593 of file TER.h.

[legend]

Detailed Description

-

Definition at line 591 of file TER.h.

+

Definition at line 585 of file TER.h.

[legend]

Detailed Description

-

Definition at line 595 of file TER.h.

+

Definition at line 589 of file TER.h.

[legend]

Detailed Description

-

Definition at line 603 of file TER.h.

+

Definition at line 597 of file TER.h.

[legend]

Detailed Description

-

Definition at line 607 of file TER.h.

+

Definition at line 601 of file TER.h.

-

Definition at line 40 of file CanonicalTXSet.cpp.

+

Definition at line 41 of file CanonicalTXSet.cpp.

diff --git a/classxrpl_1_1ConnectAttempt.html b/classxrpl_1_1ConnectAttempt.html index 73a3c1f27e..3e3860f7f2 100644 --- a/classxrpl_1_1ConnectAttempt.html +++ b/classxrpl_1_1ConnectAttempt.html @@ -860,7 +860,7 @@ Static Private Attributes

Cancel both global and step timers.

Used during cleanup and when connection completes successfully. Exceptions from timer cancellation are safely ignored.

-

Definition at line 234 of file ConnectAttempt.cpp.

+

Definition at line 236 of file ConnectAttempt.cpp.

@@ -897,7 +897,7 @@ Static Private Attributes

Determines which timer expired (global vs step) and logs appropriate diagnostic information before terminating the connection.

-

Definition at line 248 of file ConnectAttempt.cpp.

+

Definition at line 250 of file ConnectAttempt.cpp.

@@ -925,7 +925,7 @@ Static Private Attributes
-

Definition at line 286 of file ConnectAttempt.cpp.

+

Definition at line 288 of file ConnectAttempt.cpp.

@@ -953,7 +953,7 @@ Static Private Attributes
-

Definition at line 321 of file ConnectAttempt.cpp.

+

Definition at line 323 of file ConnectAttempt.cpp.

@@ -981,7 +981,7 @@ Static Private Attributes
-

Definition at line 370 of file ConnectAttempt.cpp.

+

Definition at line 372 of file ConnectAttempt.cpp.

@@ -1009,7 +1009,7 @@ Static Private Attributes
-

Definition at line 398 of file ConnectAttempt.cpp.

+

Definition at line 400 of file ConnectAttempt.cpp.

@@ -1214,7 +1214,7 @@ Static Private Attributes

Process the HTTP upgrade response from peer.

Validates the peer's response, extracts protocol information, verifies handshake, and either creates a PeerImp or handles redirect responses.

-

Definition at line 427 of file ConnectAttempt.cpp.

+

Definition at line 429 of file ConnectAttempt.cpp.

diff --git a/classxrpl_1_1DepositPreauth.html b/classxrpl_1_1DepositPreauth.html index a87c5c8f98..6d01193016 100644 --- a/classxrpl_1_1DepositPreauth.html +++ b/classxrpl_1_1DepositPreauth.html @@ -537,7 +537,7 @@ Static Private Member Functions

Implements xrpl::Transactor.

-

Definition at line 131 of file DepositPreauth.cpp.

+

Definition at line 132 of file DepositPreauth.cpp.

@@ -581,7 +581,7 @@ Static Private Member Functions
-

Definition at line 241 of file DepositPreauth.cpp.

+

Definition at line 242 of file DepositPreauth.cpp.

diff --git a/classxrpl_1_1GRPCServer.html b/classxrpl_1_1GRPCServer.html index 9fc4048a1f..934dc95186 100644 --- a/classxrpl_1_1GRPCServer.html +++ b/classxrpl_1_1GRPCServer.html @@ -240,7 +240,7 @@ Private Attributes
-

Definition at line 550 of file GRPCServer.cpp.

+

Definition at line 554 of file GRPCServer.cpp.

@@ -286,7 +286,7 @@ Private Attributes
-

Definition at line 525 of file GRPCServer.cpp.

+

Definition at line 529 of file GRPCServer.cpp.

@@ -305,7 +305,7 @@ Private Attributes
-

Definition at line 540 of file GRPCServer.cpp.

+

Definition at line 544 of file GRPCServer.cpp.

@@ -324,7 +324,7 @@ Private Attributes
-

Definition at line 556 of file GRPCServer.cpp.

+

Definition at line 560 of file GRPCServer.cpp.

diff --git a/classxrpl_1_1GRPCServerImpl.html b/classxrpl_1_1GRPCServerImpl.html index cf9f56cec7..1858c8fbae 100644 --- a/classxrpl_1_1GRPCServerImpl.html +++ b/classxrpl_1_1GRPCServerImpl.html @@ -400,7 +400,7 @@ template<class Request , class Response >
-

Definition at line 490 of file GRPCServer.cpp.

+

Definition at line 494 of file GRPCServer.cpp.

@@ -457,7 +457,7 @@ template<class Request , class Response >
-

Definition at line 518 of file GRPCServer.cpp.

+

Definition at line 522 of file GRPCServer.cpp.

diff --git a/classxrpl_1_1IOUAmount.html b/classxrpl_1_1IOUAmount.html index 16b549df33..cba035645f 100644 --- a/classxrpl_1_1IOUAmount.html +++ b/classxrpl_1_1IOUAmount.html @@ -480,7 +480,7 @@ Friends
-

Definition at line 119 of file IOUAmount.h.

+

Definition at line 120 of file IOUAmount.h.

@@ -499,7 +499,7 @@ Friends
-

Definition at line 126 of file IOUAmount.h.

+

Definition at line 127 of file IOUAmount.h.

@@ -519,7 +519,7 @@ Friends
-

Definition at line 132 of file IOUAmount.h.

+

Definition at line 133 of file IOUAmount.h.

@@ -539,7 +539,7 @@ Friends
-

Definition at line 137 of file IOUAmount.h.

+

Definition at line 138 of file IOUAmount.h.

@@ -568,7 +568,7 @@ Friends

Returns true if the amount is not zero.

-

Definition at line 143 of file IOUAmount.h.

+

Definition at line 144 of file IOUAmount.h.

@@ -597,7 +597,7 @@ Friends

Return the sign of the amount.

-

Definition at line 149 of file IOUAmount.h.

+

Definition at line 151 of file IOUAmount.h.

@@ -624,7 +624,7 @@ Friends
-

Definition at line 155 of file IOUAmount.h.

+

Definition at line 157 of file IOUAmount.h.

@@ -651,7 +651,7 @@ Friends
-

Definition at line 161 of file IOUAmount.h.

+

Definition at line 163 of file IOUAmount.h.

diff --git a/classxrpl_1_1LedgerReplayTask.html b/classxrpl_1_1LedgerReplayTask.html index 79d2b596f2..dc42e296c4 100644 --- a/classxrpl_1_1LedgerReplayTask.html +++ b/classxrpl_1_1LedgerReplayTask.html @@ -463,7 +463,7 @@ Friends
-

Definition at line 83 of file LedgerReplayTask.cpp.

+

Definition at line 84 of file LedgerReplayTask.cpp.

@@ -485,7 +485,7 @@ Friends

Start the task.

-

Definition at line 89 of file LedgerReplayTask.cpp.

+

Definition at line 90 of file LedgerReplayTask.cpp.

@@ -514,7 +514,7 @@ Friends
Note
the LedgerDeltaAcquire subtasks must be added in order
-

Definition at line 231 of file LedgerReplayTask.cpp.

+

Definition at line 232 of file LedgerReplayTask.cpp.

@@ -554,7 +554,7 @@ Friends

return if the task is finished

-

Definition at line 258 of file LedgerReplayTask.cpp.

+

Definition at line 259 of file LedgerReplayTask.cpp.

@@ -596,7 +596,7 @@ Friends

Implements xrpl::TimeoutCounter.

-

Definition at line 210 of file LedgerReplayTask.cpp.

+

Definition at line 211 of file LedgerReplayTask.cpp.

@@ -627,7 +627,7 @@ Friends

Implements xrpl::TimeoutCounter.

-

Definition at line 225 of file LedgerReplayTask.cpp.

+

Definition at line 226 of file LedgerReplayTask.cpp.

@@ -681,7 +681,7 @@ Friends -

Definition at line 189 of file LedgerReplayTask.cpp.

+

Definition at line 190 of file LedgerReplayTask.cpp.

@@ -717,7 +717,7 @@ Friends -

Definition at line 142 of file LedgerReplayTask.cpp.

+

Definition at line 143 of file LedgerReplayTask.cpp.

@@ -753,7 +753,7 @@ Friends -

Definition at line 118 of file LedgerReplayTask.cpp.

+

Definition at line 119 of file LedgerReplayTask.cpp.

@@ -789,7 +789,7 @@ Friends -

Definition at line 151 of file LedgerReplayTask.cpp.

+

Definition at line 152 of file LedgerReplayTask.cpp.

diff --git a/classxrpl_1_1MPTAmount.html b/classxrpl_1_1MPTAmount.html index c262c29f50..e621fc629e 100644 --- a/classxrpl_1_1MPTAmount.html +++ b/classxrpl_1_1MPTAmount.html @@ -555,7 +555,7 @@ Protected Attributes

Return the sign of the amount.

-

Definition at line 103 of file MPTAmount.h.

+

Definition at line 104 of file MPTAmount.h.

@@ -585,7 +585,7 @@ Protected Attributes

Returns the underlying value.

Code SHOULD NOT call this function unless the type has been abstracted away, e.g. in a templated function.

-

Definition at line 113 of file MPTAmount.h.

+

Definition at line 114 of file MPTAmount.h.

diff --git a/classxrpl_1_1ManifestCache.html b/classxrpl_1_1ManifestCache.html index 02ec95644f..ed2b343073 100644 --- a/classxrpl_1_1ManifestCache.html +++ b/classxrpl_1_1ManifestCache.html @@ -572,7 +572,7 @@ Private Attributes
Thread Safety

May be called concurrently

-

Definition at line 548 of file Manifest.cpp.

+

Definition at line 549 of file Manifest.cpp.

diff --git a/classxrpl_1_1NodeFamily.html b/classxrpl_1_1NodeFamily.html index 9b99243f92..9af83edf85 100644 --- a/classxrpl_1_1NodeFamily.html +++ b/classxrpl_1_1NodeFamily.html @@ -604,7 +604,7 @@ Private Attributes

Implements xrpl::Family.

-

Definition at line 30 of file NodeFamily.cpp.

+

Definition at line 32 of file NodeFamily.cpp.

@@ -633,7 +633,7 @@ Private Attributes

Implements xrpl::Family.

-

Definition at line 37 of file NodeFamily.cpp.

+

Definition at line 39 of file NodeFamily.cpp.

@@ -682,7 +682,7 @@ Private Attributes

Implements xrpl::Family.

-

Definition at line 49 of file NodeFamily.cpp.

+

Definition at line 51 of file NodeFamily.cpp.

@@ -769,7 +769,7 @@ Private Attributes
-

Definition at line 78 of file NodeFamily.cpp.

+

Definition at line 80 of file NodeFamily.cpp.

diff --git a/classxrpl_1_1Number.html b/classxrpl_1_1Number.html index 42c199be77..7a25ae533a 100644 --- a/classxrpl_1_1Number.html +++ b/classxrpl_1_1Number.html @@ -1119,7 +1119,7 @@ Friends
-

Definition at line 805 of file Number.cpp.

+

Definition at line 806 of file Number.cpp.

@@ -2071,7 +2071,7 @@ template<>
-

Definition at line 823 of file Number.cpp.

+

Definition at line 824 of file Number.cpp.

@@ -2109,7 +2109,7 @@ template<>
-

Definition at line 938 of file Number.cpp.

+

Definition at line 939 of file Number.cpp.

@@ -2137,7 +2137,7 @@ template<>
-

Definition at line 1010 of file Number.cpp.

+

Definition at line 1011 of file Number.cpp.

diff --git a/classxrpl_1_1NumberSO.html b/classxrpl_1_1NumberSO.html index 8e899cd4e5..7bd628de58 100644 --- a/classxrpl_1_1NumberSO.html +++ b/classxrpl_1_1NumberSO.html @@ -105,7 +105,7 @@ Private Attributes

Detailed Description

RAII class to set and restore the Number switchover.

-

Definition at line 189 of file IOUAmount.h.

+

Definition at line 191 of file IOUAmount.h.

Constructor & Destructor Documentation

◆ ~NumberSO()

@@ -122,7 +122,7 @@ Private Attributes
-

Definition at line 194 of file IOUAmount.h.

+

Definition at line 196 of file IOUAmount.h.

@@ -176,7 +176,7 @@ Private Attributes
-

Definition at line 203 of file IOUAmount.h.

+

Definition at line 205 of file IOUAmount.h.

@@ -228,7 +228,7 @@ Private Attributes
-

Definition at line 191 of file IOUAmount.h.

+

Definition at line 193 of file IOUAmount.h.

diff --git a/classxrpl_1_1OverlayImpl.html b/classxrpl_1_1OverlayImpl.html index eea42df106..1699b893f1 100644 --- a/classxrpl_1_1OverlayImpl.html +++ b/classxrpl_1_1OverlayImpl.html @@ -840,7 +840,7 @@ Private Attributes
-

Definition at line 93 of file OverlayImpl.cpp.

+

Definition at line 94 of file OverlayImpl.cpp.

@@ -922,7 +922,7 @@ Private Attributes

Reimplemented from xrpl::Overlay.

-

Definition at line 404 of file OverlayImpl.cpp.

+

Definition at line 405 of file OverlayImpl.cpp.

@@ -951,7 +951,7 @@ Private Attributes

Reimplemented from xrpl::Overlay.

-

Definition at line 482 of file OverlayImpl.cpp.

+

Definition at line 483 of file OverlayImpl.cpp.

@@ -1056,7 +1056,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 128 of file OverlayImpl.cpp.

+

Definition at line 129 of file OverlayImpl.cpp.

@@ -1089,7 +1089,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 327 of file OverlayImpl.cpp.

+

Definition at line 328 of file OverlayImpl.cpp.

@@ -1120,7 +1120,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 623 of file OverlayImpl.cpp.

+

Definition at line 624 of file OverlayImpl.cpp.

@@ -1151,7 +1151,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 616 of file OverlayImpl.cpp.

+

Definition at line 617 of file OverlayImpl.cpp.

@@ -1182,7 +1182,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 734 of file OverlayImpl.cpp.

+

Definition at line 735 of file OverlayImpl.cpp.

@@ -1214,7 +1214,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 947 of file OverlayImpl.cpp.

+

Definition at line 948 of file OverlayImpl.cpp.

@@ -1268,7 +1268,7 @@ Private Attributes
Returns
active peers less peers in toSkip
-

Definition at line 958 of file OverlayImpl.cpp.

+

Definition at line 959 of file OverlayImpl.cpp.

@@ -1306,7 +1306,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 993 of file OverlayImpl.cpp.

+

Definition at line 994 of file OverlayImpl.cpp.

@@ -1338,7 +1338,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 999 of file OverlayImpl.cpp.

+

Definition at line 1000 of file OverlayImpl.cpp.

@@ -1370,7 +1370,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 1011 of file OverlayImpl.cpp.

+

Definition at line 1012 of file OverlayImpl.cpp.

@@ -1402,7 +1402,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 1028 of file OverlayImpl.cpp.

+

Definition at line 1029 of file OverlayImpl.cpp.

@@ -1434,7 +1434,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 1050 of file OverlayImpl.cpp.

+

Definition at line 1051 of file OverlayImpl.cpp.

@@ -1491,7 +1491,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 1035 of file OverlayImpl.cpp.

+

Definition at line 1036 of file OverlayImpl.cpp.

@@ -1548,7 +1548,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 1057 of file OverlayImpl.cpp.

+

Definition at line 1058 of file OverlayImpl.cpp.

@@ -1604,7 +1604,7 @@ Private Attributes

Implements xrpl::Overlay.

-

Definition at line 1099 of file OverlayImpl.cpp.

+

Definition at line 1100 of file OverlayImpl.cpp.

@@ -1623,7 +1623,7 @@ Private Attributes
-

Definition at line 1072 of file OverlayImpl.cpp.

+

Definition at line 1073 of file OverlayImpl.cpp.

@@ -1643,7 +1643,7 @@ Private Attributes
-

Definition at line 365 of file OverlayImpl.cpp.

+

Definition at line 366 of file OverlayImpl.cpp.

@@ -1663,7 +1663,7 @@ Private Attributes
-

Definition at line 395 of file OverlayImpl.cpp.

+

Definition at line 396 of file OverlayImpl.cpp.

@@ -1687,7 +1687,7 @@ Private Attributes

A peer has connected successfully This is called after the peer handshake has been completed and during peer activation.

At this point, the peer address and the public key are known.

-

Definition at line 521 of file OverlayImpl.cpp.

+

Definition at line 522 of file OverlayImpl.cpp.

@@ -1707,7 +1707,7 @@ Private Attributes
-

Definition at line 541 of file OverlayImpl.cpp.

+

Definition at line 542 of file OverlayImpl.cpp.

@@ -1729,7 +1729,7 @@ template<class UnaryFunc >
-

Definition at line 251 of file OverlayImpl.h.

+

Definition at line 252 of file OverlayImpl.h.

@@ -1759,7 +1759,7 @@ template<class UnaryFunc >
-

Definition at line 548 of file OverlayImpl.cpp.

+

Definition at line 549 of file OverlayImpl.cpp.

@@ -1787,7 +1787,7 @@ template<class UnaryFunc >
-

Definition at line 263 of file OverlayImpl.cpp.

+

Definition at line 264 of file OverlayImpl.cpp.

@@ -1817,7 +1817,7 @@ template<class Body >
-

Definition at line 281 of file OverlayImpl.h.

+

Definition at line 282 of file OverlayImpl.h.

@@ -1847,7 +1847,7 @@ template<class Fields >
-

Definition at line 290 of file OverlayImpl.h.

+

Definition at line 291 of file OverlayImpl.h.

@@ -1877,7 +1877,7 @@ template<class Fields >
-

Definition at line 303 of file OverlayImpl.h.

+

Definition at line 304 of file OverlayImpl.h.

@@ -1905,7 +1905,7 @@ template<class Fields >
-

Definition at line 272 of file OverlayImpl.cpp.

+

Definition at line 273 of file OverlayImpl.cpp.

@@ -1935,7 +1935,7 @@ template<class Fields >
-

Definition at line 601 of file OverlayImpl.cpp.

+

Definition at line 602 of file OverlayImpl.cpp.

@@ -1965,7 +1965,7 @@ template<class Fields >
-

Definition at line 607 of file OverlayImpl.cpp.

+

Definition at line 608 of file OverlayImpl.cpp.

@@ -1996,7 +1996,7 @@ template<class Fields >

Implements xrpl::Overlay.

-

Definition at line 322 of file OverlayImpl.h.

+

Definition at line 323 of file OverlayImpl.h.

@@ -2025,7 +2025,7 @@ template<class Fields >

Implements xrpl::Overlay.

-

Definition at line 328 of file OverlayImpl.h.

+

Definition at line 329 of file OverlayImpl.h.

@@ -2056,7 +2056,7 @@ template<class Fields >

Implements xrpl::Overlay.

-

Definition at line 334 of file OverlayImpl.h.

+

Definition at line 335 of file OverlayImpl.h.

@@ -2085,7 +2085,7 @@ template<class Fields >

Implements xrpl::Overlay.

-

Definition at line 340 of file OverlayImpl.h.

+

Definition at line 341 of file OverlayImpl.h.

@@ -2114,7 +2114,7 @@ template<class Fields >

Implements xrpl::Overlay.

-

Definition at line 346 of file OverlayImpl.h.

+

Definition at line 347 of file OverlayImpl.h.

@@ -2143,7 +2143,7 @@ template<class Fields >

Implements xrpl::Overlay.

-

Definition at line 352 of file OverlayImpl.h.

+

Definition at line 353 of file OverlayImpl.h.

@@ -2176,7 +2176,7 @@ template<class Fields >

Implements xrpl::Overlay.

-

Definition at line 358 of file OverlayImpl.h.

+

Definition at line 359 of file OverlayImpl.h.

@@ -2229,7 +2229,7 @@ template<class Fields > -

Definition at line 1297 of file OverlayImpl.cpp.

+

Definition at line 1298 of file OverlayImpl.cpp.

@@ -2273,7 +2273,7 @@ template<class Fields >

Overload to reduce allocation in case of single peer.

-

Definition at line 1320 of file OverlayImpl.cpp.

+

Definition at line 1321 of file OverlayImpl.cpp.

@@ -2301,7 +2301,7 @@ template<class Fields > -

Definition at line 1342 of file OverlayImpl.cpp.

+

Definition at line 1343 of file OverlayImpl.cpp.

@@ -2333,7 +2333,7 @@ template<class Fields >

Implements xrpl::Overlay.

-

Definition at line 393 of file OverlayImpl.h.

+

Definition at line 394 of file OverlayImpl.h.

@@ -2357,7 +2357,7 @@ template<typename... Args>

Add tx reduce-relay metrics.

-

Definition at line 401 of file OverlayImpl.h.

+

Definition at line 402 of file OverlayImpl.h.

@@ -2413,7 +2413,7 @@ template<typename... Args>

Implements xrpl::reduce_relay::SquelchHandler.

-

Definition at line 1288 of file OverlayImpl.cpp.

+

Definition at line 1289 of file OverlayImpl.cpp.

@@ -2462,7 +2462,7 @@ template<typename... Args>

Implements xrpl::reduce_relay::SquelchHandler.

-

Definition at line 1277 of file OverlayImpl.cpp.

+

Definition at line 1278 of file OverlayImpl.cpp.

@@ -2506,7 +2506,7 @@ template<typename... Args>
-

Definition at line 280 of file OverlayImpl.cpp.

+

Definition at line 281 of file OverlayImpl.cpp.

@@ -2556,7 +2556,7 @@ template<typename... Args>
-

Definition at line 307 of file OverlayImpl.cpp.

+

Definition at line 308 of file OverlayImpl.cpp.

@@ -2598,7 +2598,7 @@ template<typename... Args>

Crawl returns information about the node and its peers so crawlers can map the network.

Returns
true if the request was handled.
-

Definition at line 745 of file OverlayImpl.cpp.

+

Definition at line 746 of file OverlayImpl.cpp.

@@ -2640,7 +2640,7 @@ template<typename... Args>

Using a /vl/<hex-encoded public key> URL, will retrieve the latest validator list (or UNL) that this node has for that public key, if the node trusts that public key.

Returns
true if the request was handled.
-

Definition at line 781 of file OverlayImpl.cpp.

+

Definition at line 782 of file OverlayImpl.cpp.

@@ -2682,7 +2682,7 @@ template<typename... Args>

Health returns information about the health of the node.

Returns
true if the request was handled.
-

Definition at line 847 of file OverlayImpl.cpp.

+

Definition at line 848 of file OverlayImpl.cpp.

@@ -2723,7 +2723,7 @@ template<typename... Args>

Handles non-peer protocol requests.

Returns
true if the request was handled.
-

Definition at line 940 of file OverlayImpl.cpp.

+

Definition at line 941 of file OverlayImpl.cpp.

@@ -2753,7 +2753,7 @@ template<typename... Args>

Returns information about peers on the overlay network.

Reported through the /crawl API Controlled through the config section [crawl] overlay=[0|1]

-

Definition at line 629 of file OverlayImpl.cpp.

+

Definition at line 630 of file OverlayImpl.cpp.

@@ -2783,7 +2783,7 @@ template<typename... Args>

Returns information about the local server.

Reported through the /crawl API Controlled through the config section [crawl] server=[0|1]

-

Definition at line 671 of file OverlayImpl.cpp.

+

Definition at line 672 of file OverlayImpl.cpp.

@@ -2813,7 +2813,7 @@ template<typename... Args>

Returns information about the local server's performance counters.

Reported through the /crawl API Controlled through the config section [crawl] counts=[0|1]

-

Definition at line 698 of file OverlayImpl.cpp.

+

Definition at line 699 of file OverlayImpl.cpp.

@@ -2843,7 +2843,7 @@ template<typename... Args>

Returns information about the local server's UNL.

Reported through the /crawl API Controlled through the config section [crawl] unl=[0|1]

-

Definition at line 704 of file OverlayImpl.cpp.

+

Definition at line 705 of file OverlayImpl.cpp.

@@ -2876,7 +2876,7 @@ template<typename... Args>

Reimplemented from beast::PropertyStream::Source.

-

Definition at line 499 of file OverlayImpl.cpp.

+

Definition at line 500 of file OverlayImpl.cpp.

@@ -2904,7 +2904,7 @@ template<typename... Args>
-

Definition at line 1190 of file OverlayImpl.cpp.

+

Definition at line 1191 of file OverlayImpl.cpp.

@@ -2931,7 +2931,7 @@ template<typename... Args>
-

Definition at line 1199 of file OverlayImpl.cpp.

+

Definition at line 1200 of file OverlayImpl.cpp.

@@ -2958,7 +2958,7 @@ template<typename... Args>
-

Definition at line 1231 of file OverlayImpl.cpp.

+

Definition at line 1232 of file OverlayImpl.cpp.

@@ -2985,7 +2985,7 @@ template<typename... Args>
-

Definition at line 1239 of file OverlayImpl.cpp.

+

Definition at line 1240 of file OverlayImpl.cpp.

@@ -3014,7 +3014,7 @@ template<typename... Args>

Send once a second transactions' hashes aggregated by peers.

-

Definition at line 1257 of file OverlayImpl.cpp.

+

Definition at line 1258 of file OverlayImpl.cpp.

@@ -3043,7 +3043,7 @@ template<typename... Args>

Check if peers stopped relaying messages and if slots stopped receiving messages from the validator.

-

Definition at line 1351 of file OverlayImpl.cpp.

+

Definition at line 1352 of file OverlayImpl.cpp.

@@ -3070,7 +3070,7 @@ template<typename... Args>
-

Definition at line 563 of file OverlayImpl.h.

+

Definition at line 564 of file OverlayImpl.h.

@@ -4234,7 +4234,7 @@ template<class Derived >
-

Definition at line 558 of file OverlayImpl.h.

+

Definition at line 559 of file OverlayImpl.h.

@@ -4258,7 +4258,7 @@ template<class Derived >
-

Definition at line 559 of file OverlayImpl.h.

+

Definition at line 560 of file OverlayImpl.h.

diff --git a/classxrpl_1_1PeerImp.html b/classxrpl_1_1PeerImp.html index a57922a813..3120f60816 100644 --- a/classxrpl_1_1PeerImp.html +++ b/classxrpl_1_1PeerImp.html @@ -1656,7 +1656,7 @@ template<class FwdIt , class > -

Definition at line 1872 of file PeerImp.cpp.

+

Definition at line 1871 of file PeerImp.cpp.

@@ -1686,7 +1686,7 @@ template<class FwdIt , class >
-

Definition at line 1891 of file PeerImp.cpp.

+

Definition at line 1890 of file PeerImp.cpp.

@@ -2122,7 +2122,7 @@ template<class FwdIt , class >

Implements xrpl::Peer.

-

Definition at line 3210 of file PeerImp.cpp.

+

Definition at line 3209 of file PeerImp.cpp.

@@ -2151,7 +2151,7 @@ template<class FwdIt , class >

Implements xrpl::Peer.

-

Definition at line 3248 of file PeerImp.cpp.

+

Definition at line 3247 of file PeerImp.cpp.

@@ -2329,7 +2329,7 @@ template<class FwdIt , class >

This is the primary entry point to start closing a peer connection. It marks the peer for shutdown and cancels any outstanding asynchronous operations. This cancellation allows the graceful shutdown to proceed once the handlers for the cancelled operations have completed.

Note
This method must be called on the peer's strand.
-

Definition at line 572 of file PeerImp.cpp.

+

Definition at line 571 of file PeerImp.cpp.

@@ -2360,7 +2360,7 @@ template<class FwdIt , class >

This helper function checks if the peer is in a state where a graceful SSL shutdown can be performed (i.e., shutdown has been requested and no I/O operations are currently in progress).

Note
This method must be called on the peer's strand.
-

Definition at line 552 of file PeerImp.cpp.

+

Definition at line 551 of file PeerImp.cpp.

@@ -2398,7 +2398,7 @@ template<class FwdIt , class > -

Definition at line 587 of file PeerImp.cpp.

+

Definition at line 586 of file PeerImp.cpp.

@@ -2430,7 +2430,7 @@ template<class FwdIt , class >

After closing, it notifies the overlay manager of the disconnection.

Note
This function must be called from within the object's strand.
-

Definition at line 611 of file PeerImp.cpp.

+

Definition at line 610 of file PeerImp.cpp.

@@ -2462,7 +2462,7 @@ template<class FwdIt , class >

This function starts timer, which is used to detect inactivity and prevent stalled connections. It sets the timer to expire after the predefined peerTimerInterval.

Note
This function will terminate the connection in case of any errors.
-

Definition at line 635 of file PeerImp.cpp.

+

Definition at line 634 of file PeerImp.cpp.

@@ -2499,7 +2499,7 @@ template<class FwdIt , class > -

Definition at line 661 of file PeerImp.cpp.

+

Definition at line 660 of file PeerImp.cpp.

@@ -2529,7 +2529,7 @@ template<class FwdIt , class >

Cancels any pending wait on the peer activity timer.

This function is called to stop the timer. It gracefully manages any errors that might occur during the cancellation process.

-

Definition at line 724 of file PeerImp.cpp.

+

Definition at line 723 of file PeerImp.cpp.

@@ -2557,7 +2557,7 @@ template<class FwdIt , class >
-

Definition at line 653 of file PeerImp.cpp.

+

Definition at line 652 of file PeerImp.cpp.

@@ -2584,7 +2584,7 @@ template<class FwdIt , class >
-

Definition at line 738 of file PeerImp.cpp.

+

Definition at line 737 of file PeerImp.cpp.

@@ -2611,7 +2611,7 @@ template<class FwdIt , class >
-

Definition at line 804 of file PeerImp.cpp.

+

Definition at line 803 of file PeerImp.cpp.

@@ -2638,7 +2638,7 @@ template<class FwdIt , class >
-

Definition at line 811 of file PeerImp.cpp.

+

Definition at line 810 of file PeerImp.cpp.

@@ -2665,7 +2665,7 @@ template<class FwdIt , class >
-

Definition at line 821 of file PeerImp.cpp.

+

Definition at line 820 of file PeerImp.cpp.

@@ -2703,7 +2703,7 @@ template<class FwdIt , class >
-

Definition at line 854 of file PeerImp.cpp.

+

Definition at line 853 of file PeerImp.cpp.

@@ -2741,7 +2741,7 @@ template<class FwdIt , class >
-

Definition at line 933 of file PeerImp.cpp.

+

Definition at line 932 of file PeerImp.cpp.

@@ -2795,7 +2795,7 @@ template<class FwdIt , class > -

Definition at line 1218 of file PeerImp.cpp.

+

Definition at line 1217 of file PeerImp.cpp.

@@ -2831,7 +2831,7 @@ template<class FwdIt , class > -

Definition at line 2443 of file PeerImp.cpp.

+

Definition at line 2442 of file PeerImp.cpp.

@@ -2907,7 +2907,7 @@ template<class FwdIt , class >
-

Definition at line 985 of file PeerImp.cpp.

+

Definition at line 984 of file PeerImp.cpp.

@@ -2955,7 +2955,7 @@ template<class FwdIt , class >
-

Definition at line 991 of file PeerImp.cpp.

+

Definition at line 990 of file PeerImp.cpp.

@@ -2985,7 +2985,7 @@ template<class FwdIt , class >
-

Definition at line 1028 of file PeerImp.cpp.

+

Definition at line 1027 of file PeerImp.cpp.

@@ -3005,7 +3005,7 @@ template<class FwdIt , class >
-

Definition at line 1035 of file PeerImp.cpp.

+

Definition at line 1034 of file PeerImp.cpp.

@@ -3025,7 +3025,7 @@ template<class FwdIt , class >
-

Definition at line 1053 of file PeerImp.cpp.

+

Definition at line 1052 of file PeerImp.cpp.

@@ -3045,7 +3045,7 @@ template<class FwdIt , class >
-

Definition at line 1089 of file PeerImp.cpp.

+

Definition at line 1088 of file PeerImp.cpp.

@@ -3065,7 +3065,7 @@ template<class FwdIt , class >
-

Definition at line 1158 of file PeerImp.cpp.

+

Definition at line 1157 of file PeerImp.cpp.

@@ -3085,7 +3085,7 @@ template<class FwdIt , class >
-

Definition at line 1212 of file PeerImp.cpp.

+

Definition at line 1211 of file PeerImp.cpp.

@@ -3105,7 +3105,7 @@ template<class FwdIt , class >
-

Definition at line 1338 of file PeerImp.cpp.

+

Definition at line 1337 of file PeerImp.cpp.

@@ -3125,7 +3125,7 @@ template<class FwdIt , class >
-

Definition at line 1515 of file PeerImp.cpp.

+

Definition at line 1514 of file PeerImp.cpp.

@@ -3145,7 +3145,7 @@ template<class FwdIt , class >
-

Definition at line 1599 of file PeerImp.cpp.

+

Definition at line 1598 of file PeerImp.cpp.

@@ -3165,7 +3165,7 @@ template<class FwdIt , class >
-

Definition at line 1701 of file PeerImp.cpp.

+

Definition at line 1700 of file PeerImp.cpp.

@@ -3185,7 +3185,7 @@ template<class FwdIt , class >
-

Definition at line 1912 of file PeerImp.cpp.

+

Definition at line 1911 of file PeerImp.cpp.

@@ -3205,7 +3205,7 @@ template<class FwdIt , class >
-

Definition at line 2126 of file PeerImp.cpp.

+

Definition at line 2125 of file PeerImp.cpp.

@@ -3225,7 +3225,7 @@ template<class FwdIt , class >
-

Definition at line 2149 of file PeerImp.cpp.

+

Definition at line 2148 of file PeerImp.cpp.

@@ -3245,7 +3245,7 @@ template<class FwdIt , class >
-

Definition at line 2180 of file PeerImp.cpp.

+

Definition at line 2179 of file PeerImp.cpp.

@@ -3265,7 +3265,7 @@ template<class FwdIt , class >
-

Definition at line 2275 of file PeerImp.cpp.

+

Definition at line 2274 of file PeerImp.cpp.

@@ -3285,7 +3285,7 @@ template<class FwdIt , class >
-

Definition at line 2426 of file PeerImp.cpp.

+

Definition at line 2425 of file PeerImp.cpp.

@@ -3305,7 +3305,7 @@ template<class FwdIt , class >
-

Definition at line 2489 of file PeerImp.cpp.

+

Definition at line 2488 of file PeerImp.cpp.

@@ -3325,7 +3325,7 @@ template<class FwdIt , class >
-

Definition at line 2510 of file PeerImp.cpp.

+

Definition at line 2509 of file PeerImp.cpp.

@@ -3345,7 +3345,7 @@ template<class FwdIt , class >
-

Definition at line 1423 of file PeerImp.cpp.

+

Definition at line 1422 of file PeerImp.cpp.

@@ -3365,7 +3365,7 @@ template<class FwdIt , class >
-

Definition at line 1454 of file PeerImp.cpp.

+

Definition at line 1453 of file PeerImp.cpp.

@@ -3385,7 +3385,7 @@ template<class FwdIt , class >
-

Definition at line 1469 of file PeerImp.cpp.

+

Definition at line 1468 of file PeerImp.cpp.

@@ -3405,7 +3405,7 @@ template<class FwdIt , class >
-

Definition at line 1500 of file PeerImp.cpp.

+

Definition at line 1499 of file PeerImp.cpp.

@@ -3443,7 +3443,7 @@ template<class FwdIt , class >
-

Definition at line 2549 of file PeerImp.cpp.

+

Definition at line 2548 of file PeerImp.cpp.

@@ -3471,7 +3471,7 @@ template<class FwdIt , class >
-

Definition at line 2562 of file PeerImp.cpp.

+

Definition at line 2561 of file PeerImp.cpp.

@@ -3521,7 +3521,7 @@ template<class FwdIt , class >
-

Definition at line 1937 of file PeerImp.cpp.

+

Definition at line 1936 of file PeerImp.cpp.

@@ -3557,7 +3557,7 @@ template<class FwdIt , class > -

Definition at line 2594 of file PeerImp.cpp.

+

Definition at line 2593 of file PeerImp.cpp.

@@ -3607,7 +3607,7 @@ template<class FwdIt , class >
-

Definition at line 2643 of file PeerImp.cpp.

+

Definition at line 2642 of file PeerImp.cpp.

@@ -3651,7 +3651,7 @@ template<class FwdIt , class >
-

Definition at line 2775 of file PeerImp.cpp.

+

Definition at line 2774 of file PeerImp.cpp.

@@ -3695,7 +3695,7 @@ template<class FwdIt , class >
-

Definition at line 2810 of file PeerImp.cpp.

+

Definition at line 2809 of file PeerImp.cpp.

@@ -3733,7 +3733,7 @@ template<class FwdIt , class >
-

Definition at line 2897 of file PeerImp.cpp.

+

Definition at line 2896 of file PeerImp.cpp.

@@ -3761,7 +3761,7 @@ template<class FwdIt , class >
-

Definition at line 2932 of file PeerImp.cpp.

+

Definition at line 2931 of file PeerImp.cpp.

@@ -3789,7 +3789,7 @@ template<class FwdIt , class >
-

Definition at line 3015 of file PeerImp.cpp.

+

Definition at line 3014 of file PeerImp.cpp.

@@ -3817,7 +3817,7 @@ template<class FwdIt , class >
-

Definition at line 3047 of file PeerImp.cpp.

+

Definition at line 3046 of file PeerImp.cpp.

diff --git a/classxrpl_1_1PeerImp_1_1Metrics.html b/classxrpl_1_1PeerImp_1_1Metrics.html index 5e85fa7540..ebbdceb0d9 100644 --- a/classxrpl_1_1PeerImp_1_1Metrics.html +++ b/classxrpl_1_1PeerImp_1_1Metrics.html @@ -277,7 +277,7 @@ Private Attributes
-

Definition at line 3255 of file PeerImp.cpp.

+

Definition at line 3254 of file PeerImp.cpp.

@@ -296,7 +296,7 @@ Private Attributes
-

Definition at line 3279 of file PeerImp.cpp.

+

Definition at line 3278 of file PeerImp.cpp.

@@ -315,7 +315,7 @@ Private Attributes
-

Definition at line 3286 of file PeerImp.cpp.

+

Definition at line 3285 of file PeerImp.cpp.

diff --git a/classxrpl_1_1STAmount.html b/classxrpl_1_1STAmount.html index 287d81a6c2..b9be5901e5 100644 --- a/classxrpl_1_1STAmount.html +++ b/classxrpl_1_1STAmount.html @@ -1058,7 +1058,7 @@ template<AssetType A>
-

Definition at line 488 of file STAmount.h.

+

Definition at line 489 of file STAmount.h.

@@ -1420,7 +1420,7 @@ template<ValidIssueType TIss>
-

Definition at line 560 of file STAmount.h.

+

Definition at line 562 of file STAmount.h.

@@ -1507,7 +1507,7 @@ template<ValidIssueType TIss>
-

Definition at line 498 of file STAmount.h.

+

Definition at line 500 of file STAmount.h.

@@ -1527,7 +1527,7 @@ template<ValidIssueType TIss>
-

Definition at line 505 of file STAmount.h.

+

Definition at line 507 of file STAmount.h.

@@ -1566,7 +1566,7 @@ template<ValidIssueType TIss>
-

Definition at line 530 of file STAmount.h.

+

Definition at line 532 of file STAmount.h.

@@ -1585,7 +1585,7 @@ template<ValidIssueType TIss>
-

Definition at line 537 of file STAmount.h.

+

Definition at line 539 of file STAmount.h.

@@ -1605,7 +1605,7 @@ template<ValidIssueType TIss>
-

Definition at line 547 of file STAmount.h.

+

Definition at line 549 of file STAmount.h.

@@ -1625,7 +1625,7 @@ template<ValidIssueType TIss>
-

Definition at line 554 of file STAmount.h.

+

Definition at line 556 of file STAmount.h.

@@ -1950,7 +1950,7 @@ template<AssetType A>
-

Definition at line 513 of file STAmount.h.

+

Definition at line 515 of file STAmount.h.

diff --git a/classxrpl_1_1STBitString.html b/classxrpl_1_1STBitString.html index c27e4aeba4..75a961291c 100644 --- a/classxrpl_1_1STBitString.html +++ b/classxrpl_1_1STBitString.html @@ -527,7 +527,7 @@ template<int Bits>

Reimplemented from xrpl::STBase.

-

Definition at line 179 of file STBitString.h.

+

Definition at line 180 of file STBitString.h.

diff --git a/classxrpl_1_1STLedgerEntry.html b/classxrpl_1_1STLedgerEntry.html index b97b1413d7..5edfe615ea 100644 --- a/classxrpl_1_1STLedgerEntry.html +++ b/classxrpl_1_1STLedgerEntry.html @@ -656,7 +656,7 @@ Friends withAllFields  -

Definition at line 411 of file STObject.h.

+

Definition at line 414 of file STObject.h.

@@ -1190,7 +1190,7 @@ Friends
-

Definition at line 927 of file STObject.h.

+

Definition at line 933 of file STObject.h.

@@ -1217,7 +1217,7 @@ Friends
-

Definition at line 933 of file STObject.h.

+

Definition at line 939 of file STObject.h.

@@ -1244,7 +1244,7 @@ Friends
-

Definition at line 939 of file STObject.h.

+

Definition at line 945 of file STObject.h.

@@ -1272,7 +1272,7 @@ Friends
-

Definition at line 945 of file STObject.h.

+

Definition at line 951 of file STObject.h.

@@ -1355,7 +1355,7 @@ Friends
-

Definition at line 951 of file STObject.h.

+

Definition at line 957 of file STObject.h.

@@ -1635,7 +1635,7 @@ Friends
-

Definition at line 957 of file STObject.h.

+

Definition at line 963 of file STObject.h.

@@ -1662,7 +1662,7 @@ Friends
-

Definition at line 966 of file STObject.h.

+

Definition at line 972 of file STObject.h.

@@ -1692,7 +1692,7 @@ template<class... Args>
-

Definition at line 975 of file STObject.h.

+

Definition at line 981 of file STObject.h.

@@ -1719,7 +1719,7 @@ template<class... Args>
-

Definition at line 982 of file STObject.h.

+

Definition at line 988 of file STObject.h.

@@ -1915,7 +1915,7 @@ template<class... Args>
-

Definition at line 988 of file STObject.h.

+

Definition at line 994 of file STObject.h.

@@ -1943,7 +1943,7 @@ template<class... Args>
-

Definition at line 994 of file STObject.h.

+

Definition at line 1000 of file STObject.h.

@@ -1971,7 +1971,7 @@ template<class... Args>
-

Definition at line 1000 of file STObject.h.

+

Definition at line 1006 of file STObject.h.

@@ -1999,7 +1999,7 @@ template<class... Args>
-

Definition at line 1006 of file STObject.h.

+

Definition at line 1012 of file STObject.h.

@@ -2726,7 +2726,7 @@ template<class T > -

Definition at line 1013 of file STObject.h.

+

Definition at line 1019 of file STObject.h.

@@ -2765,7 +2765,7 @@ template<class T >
Returns
std::nullopt if the field is not present, else the value of the specified field.
-

Definition at line 1020 of file STObject.h.

+

Definition at line 1026 of file STObject.h.

@@ -2876,7 +2876,7 @@ template<class T >
-

Definition at line 1027 of file STObject.h.

+

Definition at line 1033 of file STObject.h.

@@ -2907,7 +2907,7 @@ template<class T >
-

Definition at line 1034 of file STObject.h.

+

Definition at line 1040 of file STObject.h.

@@ -2952,7 +2952,7 @@ template<class T > -

Definition at line 1041 of file STObject.h.

+

Definition at line 1047 of file STObject.h.

@@ -2991,7 +2991,7 @@ template<class T >
Returns
std::nullopt if the field is not present, else the value of the specified field.
-

Definition at line 1068 of file STObject.h.

+

Definition at line 1074 of file STObject.h.

@@ -3102,7 +3102,7 @@ template<class T >
-

Definition at line 1094 of file STObject.h.

+

Definition at line 1100 of file STObject.h.

@@ -3133,7 +3133,7 @@ template<class T >
-

Definition at line 1101 of file STObject.h.

+

Definition at line 1107 of file STObject.h.

@@ -3857,7 +3857,7 @@ template<class Tag >
-

Definition at line 1108 of file STObject.h.

+

Definition at line 1114 of file STObject.h.

@@ -4193,7 +4193,7 @@ template<class Tag >
-

Definition at line 1126 of file STObject.h.

+

Definition at line 1132 of file STObject.h.

@@ -4317,7 +4317,7 @@ template<typename T , typename V >
-

Definition at line 1133 of file STObject.h.

+

Definition at line 1139 of file STObject.h.

@@ -4395,7 +4395,7 @@ template<typename T , typename V >
-

Definition at line 1160 of file STObject.h.

+

Definition at line 1166 of file STObject.h.

@@ -4435,7 +4435,7 @@ template<typename T , typename V >
-

Definition at line 1183 of file STObject.h.

+

Definition at line 1189 of file STObject.h.

@@ -4475,7 +4475,7 @@ template<typename T >
-

Definition at line 1206 of file STObject.h.

+

Definition at line 1212 of file STObject.h.

@@ -4505,7 +4505,7 @@ template<typename T >
-

Definition at line 1227 of file STObject.h.

+

Definition at line 1233 of file STObject.h.

diff --git a/classxrpl_1_1STObject.html b/classxrpl_1_1STObject.html index 073b158bef..ccf7a4278b 100644 --- a/classxrpl_1_1STObject.html +++ b/classxrpl_1_1STObject.html @@ -578,7 +578,7 @@ Friends withAllFields  -

Definition at line 411 of file STObject.h.

+

Definition at line 414 of file STObject.h.

@@ -820,7 +820,7 @@ template<typename F >
-

Definition at line 922 of file STObject.h.

+

Definition at line 928 of file STObject.h.

@@ -942,7 +942,7 @@ template<typename F >
-

Definition at line 927 of file STObject.h.

+

Definition at line 933 of file STObject.h.

@@ -961,7 +961,7 @@ template<typename F >
-

Definition at line 933 of file STObject.h.

+

Definition at line 939 of file STObject.h.

@@ -980,7 +980,7 @@ template<typename F >
-

Definition at line 939 of file STObject.h.

+

Definition at line 945 of file STObject.h.

@@ -1000,7 +1000,7 @@ template<typename F >
-

Definition at line 945 of file STObject.h.

+

Definition at line 951 of file STObject.h.

@@ -1059,7 +1059,7 @@ template<typename F >
-

Definition at line 951 of file STObject.h.

+

Definition at line 957 of file STObject.h.

@@ -1341,7 +1341,7 @@ template<typename F >
-

Definition at line 957 of file STObject.h.

+

Definition at line 963 of file STObject.h.

@@ -1360,7 +1360,7 @@ template<typename F >
-

Definition at line 966 of file STObject.h.

+

Definition at line 972 of file STObject.h.

@@ -1382,7 +1382,7 @@ template<class... Args>
-

Definition at line 975 of file STObject.h.

+

Definition at line 981 of file STObject.h.

@@ -1401,7 +1401,7 @@ template<class... Args>
-

Definition at line 982 of file STObject.h.

+

Definition at line 988 of file STObject.h.

@@ -1541,7 +1541,7 @@ template<class... Args>
-

Definition at line 988 of file STObject.h.

+

Definition at line 994 of file STObject.h.

@@ -1561,7 +1561,7 @@ template<class... Args>
-

Definition at line 994 of file STObject.h.

+

Definition at line 1000 of file STObject.h.

@@ -1581,7 +1581,7 @@ template<class... Args>
-

Definition at line 1000 of file STObject.h.

+

Definition at line 1006 of file STObject.h.

@@ -1601,7 +1601,7 @@ template<class... Args>
-

Definition at line 1006 of file STObject.h.

+

Definition at line 1012 of file STObject.h.

@@ -2128,7 +2128,7 @@ template<class T > -

Definition at line 1013 of file STObject.h.

+

Definition at line 1019 of file STObject.h.

@@ -2159,7 +2159,7 @@ template<class T >
Returns
std::nullopt if the field is not present, else the value of the specified field.
-

Definition at line 1020 of file STObject.h.

+

Definition at line 1026 of file STObject.h.

@@ -2260,7 +2260,7 @@ template<class T > -

Definition at line 1041 of file STObject.h.

+

Definition at line 1047 of file STObject.h.

@@ -2291,7 +2291,7 @@ template<class T >
Returns
std::nullopt if the field is not present, else the value of the specified field.
-

Definition at line 1068 of file STObject.h.

+

Definition at line 1074 of file STObject.h.

@@ -2970,7 +2970,7 @@ template<class Tag >
-

Definition at line 1108 of file STObject.h.

+

Definition at line 1114 of file STObject.h.

@@ -3190,7 +3190,7 @@ template<class Tag >
-

Definition at line 1126 of file STObject.h.

+

Definition at line 1132 of file STObject.h.

@@ -3372,7 +3372,7 @@ template<typename T , typename V >
-

Definition at line 1183 of file STObject.h.

+

Definition at line 1189 of file STObject.h.

@@ -3412,7 +3412,7 @@ template<typename T >
-

Definition at line 1206 of file STObject.h.

+

Definition at line 1212 of file STObject.h.

@@ -3442,7 +3442,7 @@ template<typename T >
-

Definition at line 1227 of file STObject.h.

+

Definition at line 1233 of file STObject.h.

@@ -3549,7 +3549,7 @@ template<class T >
-

Definition at line 1027 of file STObject.h.

+

Definition at line 1033 of file STObject.h.

@@ -3572,7 +3572,7 @@ template<class T >
-

Definition at line 1034 of file STObject.h.

+

Definition at line 1040 of file STObject.h.

@@ -3595,7 +3595,7 @@ template<class T >
-

Definition at line 1094 of file STObject.h.

+

Definition at line 1100 of file STObject.h.

@@ -3618,7 +3618,7 @@ template<class T >
-

Definition at line 1101 of file STObject.h.

+

Definition at line 1107 of file STObject.h.

@@ -3640,7 +3640,7 @@ template<typename T , typename V >
-

Definition at line 1133 of file STObject.h.

+

Definition at line 1139 of file STObject.h.

@@ -3672,7 +3672,7 @@ template<typename T , typename V >
-

Definition at line 1160 of file STObject.h.

+

Definition at line 1166 of file STObject.h.

@@ -3997,7 +3997,7 @@ template<class T >
-

Definition at line 468 of file STObject.h.

+

Definition at line 471 of file STObject.h.

diff --git a/classxrpl_1_1STObject_1_1FieldErr.html b/classxrpl_1_1STObject_1_1FieldErr.html index 75ce9c0330..31cc22ab26 100644 --- a/classxrpl_1_1STObject_1_1FieldErr.html +++ b/classxrpl_1_1STObject_1_1FieldErr.html @@ -113,7 +113,7 @@ Public Member Functions

Detailed Description

-

Definition at line 689 of file STObject.h.

+

Definition at line 692 of file STObject.h.