-
Notifications
You must be signed in to change notification settings - Fork 68
feat: Support single asset vault #1979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
kuznetsss
merged 28 commits into
XRPLF:develop
from
PeterChen13579:SupportSingleAssetVault
Jun 27, 2025
Merged
Changes from all commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
1e50ec2
save work
PeterChen13579 0fd18fd
feat: Add Support for Single Asset Vault
PeterChen13579 c9c2c98
add test for Supplement Json method
PeterChen13579 d733294
add handler + tests
PeterChen13579 c83fcd7
Merge branch 'develop' into SupportSingleAssetVault
PeterChen13579 ee676aa
Merge branch 'develop' into SupportSingleAssetVault
PeterChen13579 c1c2ec1
more test
PeterChen13579 6a4573e
more tests
PeterChen13579 2805001
save work for now
PeterChen13579 0037eba
finish vault
PeterChen13579 fdc024b
Merge branch 'develop' into SupportSingleAssetVault
PeterChen13579 c9a2d68
Trigger pre-commit hook
PeterChen13579 5f1aafd
Merge branch 'develop' into SupportSingleAssetVault
PeterChen13579 9ecda27
fix ut and doxygen
PeterChen13579 7b72774
Merge remote-tracking branch 'upstream/develop' into SupportSingleAss…
PeterChen13579 f512e28
more test coverage
PeterChen13579 07cc14f
Merge branch 'develop' into SupportSingleAssetVault
kuznetsss 2354bf8
Remove duplicated amendment
kuznetsss bf99e5f
Fix build
kuznetsss 320faf2
Merge branch 'develop' into SupportSingleAssetVault
kuznetsss ff8f2c5
Improve tests
kuznetsss 6b44d46
One more JSON
kuznetsss 980d07d
Merge branch 'develop' into SupportSingleAssetVault
kuznetsss b0076ef
Fix review comments
kuznetsss f04da55
Fix review comments
kuznetsss 517847d
Update tests/unit/rpc/handlers/LedgerEntryTests.cpp
kuznetsss 1a068ec
Update tests/unit/rpc/handlers/VaultInfoTests.cpp
kuznetsss 99803fb
Update tests/unit/rpc/handlers/LedgerEntryTests.cpp
kuznetsss File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
//------------------------------------------------------------------------------ | ||
/* | ||
This file is part of clio: https://github.com/XRPLF/clio | ||
Copyright (c) 2025, the clio developers. | ||
|
||
Permission to use, copy, modify, and distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | ||
//============================================================================== | ||
|
||
#include "rpc/handlers/VaultInfo.hpp" | ||
|
||
#include "data/BackendInterface.hpp" | ||
#include "rpc/Errors.hpp" | ||
#include "rpc/JS.hpp" | ||
#include "rpc/RPCHelpers.hpp" | ||
#include "rpc/common/Types.hpp" | ||
#include "util/Assert.hpp" | ||
|
||
#include <boost/json/conversion.hpp> | ||
#include <boost/json/object.hpp> | ||
#include <boost/json/value.hpp> | ||
#include <xrpl/basics/base_uint.h> | ||
#include <xrpl/basics/strHex.h> | ||
#include <xrpl/protocol/Indexes.h> | ||
#include <xrpl/protocol/Keylet.h> | ||
#include <xrpl/protocol/LedgerHeader.h> | ||
#include <xrpl/protocol/SField.h> | ||
#include <xrpl/protocol/STBase.h> | ||
#include <xrpl/protocol/STLedgerEntry.h> | ||
#include <xrpl/protocol/Serializer.h> | ||
#include <xrpl/protocol/jss.h> | ||
|
||
#include <cstdint> | ||
#include <memory> | ||
#include <optional> | ||
#include <string> | ||
|
||
namespace rpc { | ||
|
||
namespace { | ||
|
||
/** | ||
* @brief Ensures that the input contains either a `vaultID` alone, or both `owner` and `tnxSequence`. | ||
* Any other combination is considered malformed. | ||
kuznetsss marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* @param input The input object containing optional fields for the vault request. | ||
* @return Returns true if the input is valid, false otherwise. | ||
*/ | ||
bool | ||
validate(VaultInfoHandler::Input const& input) | ||
{ | ||
bool const hasVaultId = input.vaultID.has_value(); | ||
bool const hasOwner = input.owner.has_value(); | ||
bool const hasSeq = input.tnxSequence.has_value(); | ||
|
||
// Only valid combinations: (vaultID) or (owner + ledgerIndex) | ||
// NOLINTNEXTLINE(readability-simplify-boolean-expr) | ||
return (hasVaultId && !hasOwner && !hasSeq) || (!hasVaultId && hasOwner && hasSeq); | ||
} | ||
|
||
} // namespace | ||
|
||
VaultInfoHandler::VaultInfoHandler(std::shared_ptr<BackendInterface> const& sharedPtrBackend) | ||
: sharedPtrBackend_{sharedPtrBackend} | ||
{ | ||
} | ||
|
||
VaultInfoHandler::Result | ||
VaultInfoHandler::process(VaultInfoHandler::Input input, Context const& ctx) const | ||
{ | ||
// vault info input must either have owner and sequence, or vault_id only. | ||
if (not validate(input)) | ||
return Error{ClioError::RpcMalformedRequest}; | ||
|
||
auto const range = sharedPtrBackend_->fetchLedgerRange(); | ||
ASSERT(range.has_value(), "VaultInfo's ledger range must be available"); | ||
|
||
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( | ||
*sharedPtrBackend_, ctx.yield, std::nullopt, input.ledgerIndex, range->maxSequence | ||
); | ||
|
||
if (not expectedLgrInfo.has_value()) | ||
return Error{expectedLgrInfo.error()}; | ||
|
||
auto const& lgrInfo = *expectedLgrInfo; | ||
|
||
// Extract the vault keylet based on input | ||
auto const vaultKeylet = [&]() -> std::expected<ripple::Keylet, Status> { | ||
if (input.owner && input.tnxSequence) { | ||
auto const accountStr = *input.owner; | ||
auto const accountID = accountFromStringStrict(accountStr); | ||
|
||
// checks that account exists | ||
{ | ||
auto const accountKeylet = ripple::keylet::account(*accountID); | ||
auto const accountLedgerObject = | ||
sharedPtrBackend_->fetchLedgerObject(accountKeylet.key, lgrInfo.seq, ctx.yield); | ||
|
||
if (!accountLedgerObject) | ||
return std::unexpected{Status{ClioError::RpcEntryNotFound}}; | ||
} | ||
|
||
return ripple::keylet::vault(*accountID, *input.tnxSequence); | ||
} | ||
ripple::uint256 nodeIndex; | ||
if (nodeIndex.parseHex(*input.vaultID)) | ||
return ripple::keylet::vault(nodeIndex); | ||
|
||
return std::unexpected{Status{ClioError::RpcEntryNotFound}}; | ||
}(); | ||
|
||
if (not vaultKeylet.has_value()) | ||
return Error{vaultKeylet.error()}; | ||
|
||
// Fetch the vault object and it's associated issuance ID | ||
auto const vaultLedgerObject = | ||
sharedPtrBackend_->fetchLedgerObject(vaultKeylet.value().key, lgrInfo.seq, ctx.yield); | ||
|
||
if (not vaultLedgerObject) | ||
return Error{Status{ClioError::RpcEntryNotFound, "vault object not found."}}; | ||
|
||
ripple::STLedgerEntry const vaultSle{ | ||
ripple::SerialIter{vaultLedgerObject->data(), vaultLedgerObject->size()}, vaultKeylet.value().key | ||
}; | ||
|
||
auto const issuanceKeylet = ripple::keylet::mptIssuance(vaultSle[ripple::sfShareMPTID]).key; | ||
auto const issuanceObject = sharedPtrBackend_->fetchLedgerObject(issuanceKeylet, lgrInfo.seq, ctx.yield); | ||
|
||
if (not issuanceObject) | ||
return Error{Status{ClioError::RpcEntryNotFound, "issuance object not found."}}; | ||
|
||
ripple::STLedgerEntry const issuanceSle{ | ||
ripple::SerialIter{issuanceObject->data(), issuanceObject->size()}, issuanceKeylet | ||
}; | ||
|
||
// put issuance object into "shares" field of vault object | ||
// follows same logic as rippled: | ||
// https://github.com/XRPLF/rippled/pull/5224/files#diff-6cb544622c7942261f097d628f61f1c1fcf34a1bcfd954aedbada4238fc28f69R107 | ||
Output response; | ||
response.vault = toBoostJson(vaultSle.getJson(ripple::JsonOptions::none)); | ||
response.vault.as_object()[JS(shares)] = toBoostJson(issuanceSle.getJson(ripple::JsonOptions::none)); | ||
response.ledgerIndex = lgrInfo.seq; | ||
|
||
return response; | ||
} | ||
|
||
void | ||
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, VaultInfoHandler::Output const& output) | ||
{ | ||
jv = boost::json::object{ | ||
{JS(ledger_index), output.ledgerIndex}, {JS(validated), output.validated}, {JS(vault), output.vault} | ||
}; | ||
} | ||
|
||
VaultInfoHandler::Input | ||
tag_invoke(boost::json::value_to_tag<VaultInfoHandler::Input>, boost::json::value const& jv) | ||
{ | ||
auto input = VaultInfoHandler::Input{}; | ||
auto const& jsonObject = jv.as_object(); | ||
|
||
if (jsonObject.contains(JS(owner))) | ||
input.owner = jsonObject.at(JS(owner)).as_string(); | ||
|
||
if (jsonObject.contains(JS(seq))) | ||
input.tnxSequence = static_cast<uint32_t>(jsonObject.at(JS(seq)).as_int64()); | ||
|
||
if (jsonObject.contains(JS(vault_id))) | ||
input.vaultID = jsonObject.at(JS(vault_id)).as_string(); | ||
|
||
if (jsonObject.contains(JS(ledger_index))) { | ||
if (not jsonObject.at(JS(ledger_index)).is_string()) { | ||
input.ledgerIndex = jsonObject.at(JS(ledger_index)).as_int64(); | ||
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") { | ||
input.ledgerIndex = std::stoi(jsonObject.at(JS(ledger_index)).as_string().c_str()); | ||
} | ||
} | ||
|
||
return input; | ||
} | ||
|
||
} // namespace rpc |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.