mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-18 17:45:48 +00:00
Add ripple::Expected which simulates std::expected:
Also integrates use of ripple::Expected into the code base.
This commit is contained in:
committed by
Nik Bougalis
parent
de43d43560
commit
c50d166c23
@@ -708,6 +708,7 @@ target_sources (rippled PRIVATE
|
||||
#]===============================]
|
||||
src/test/basics/Buffer_test.cpp
|
||||
src/test/basics/DetectCrash_test.cpp
|
||||
src/test/basics/Expected_test.cpp
|
||||
src/test/basics/FileUtilities_test.cpp
|
||||
src/test/basics/IOUAmount_test.cpp
|
||||
src/test/basics/KeyCache_test.cpp
|
||||
|
||||
@@ -58,13 +58,13 @@ SetSignerList::determineOperation(
|
||||
{
|
||||
auto signers = SignerEntries::deserialize(tx, j, "transaction");
|
||||
|
||||
if (signers.second != tesSUCCESS)
|
||||
return std::make_tuple(signers.second, quorum, sign, op);
|
||||
if (!signers)
|
||||
return std::make_tuple(signers.error(), quorum, sign, op);
|
||||
|
||||
std::sort(signers.first.begin(), signers.first.end());
|
||||
std::sort(signers->begin(), signers->end());
|
||||
|
||||
// Save deserialized list for later.
|
||||
sign = std::move(signers.first);
|
||||
sign = std::move(*signers);
|
||||
op = set;
|
||||
}
|
||||
else if ((quorum == 0) && !hasSignerEntries)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
std::pair<std::vector<SignerEntries::SignerEntry>, NotTEC>
|
||||
Expected<std::vector<SignerEntries::SignerEntry>, NotTEC>
|
||||
SignerEntries::deserialize(
|
||||
STObject const& obj,
|
||||
beast::Journal journal,
|
||||
@@ -37,11 +37,10 @@ SignerEntries::deserialize(
|
||||
{
|
||||
JLOG(journal.trace())
|
||||
<< "Malformed " << annotation << ": Need signer entry array.";
|
||||
s.second = temMALFORMED;
|
||||
return s;
|
||||
return Unexpected(temMALFORMED);
|
||||
}
|
||||
|
||||
auto& accountVec = s.first;
|
||||
std::vector<SignerEntry> accountVec;
|
||||
accountVec.reserve(STTx::maxMultiSigners);
|
||||
|
||||
STArray const& sEntries(obj.getFieldArray(sfSignerEntries));
|
||||
@@ -52,8 +51,7 @@ SignerEntries::deserialize(
|
||||
{
|
||||
JLOG(journal.trace())
|
||||
<< "Malformed " << annotation << ": Expected SignerEntry.";
|
||||
s.second = temMALFORMED;
|
||||
return s;
|
||||
return Unexpected(temMALFORMED);
|
||||
}
|
||||
|
||||
// Extract SignerEntry fields.
|
||||
@@ -61,9 +59,7 @@ SignerEntries::deserialize(
|
||||
std::uint16_t const weight = sEntry.getFieldU16(sfSignerWeight);
|
||||
accountVec.emplace_back(account, weight);
|
||||
}
|
||||
|
||||
s.second = tesSUCCESS;
|
||||
return s;
|
||||
return accountVec;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#define RIPPLE_TX_IMPL_SIGNER_ENTRIES_H_INCLUDED
|
||||
|
||||
#include <ripple/app/tx/impl/Transactor.h> // NotTEC
|
||||
#include <ripple/basics/Expected.h> //
|
||||
#include <ripple/beast/utility/Journal.h> // beast::Journal
|
||||
#include <ripple/protocol/STTx.h> // STTx::maxMultiSigners
|
||||
#include <ripple/protocol/TER.h> // temMALFORMED
|
||||
@@ -62,7 +63,7 @@ public:
|
||||
};
|
||||
|
||||
// Deserialize a SignerEntries array from the network or from the ledger.
|
||||
static std::pair<std::vector<SignerEntry>, NotTEC>
|
||||
static Expected<std::vector<SignerEntry>, NotTEC>
|
||||
deserialize(
|
||||
STObject const& obj,
|
||||
beast::Journal journal,
|
||||
|
||||
@@ -554,8 +554,8 @@ Transactor::checkMultiSign(PreclaimContext const& ctx)
|
||||
|
||||
auto accountSigners =
|
||||
SignerEntries::deserialize(*sleAccountSigners, ctx.j, "ledger");
|
||||
if (accountSigners.second != tesSUCCESS)
|
||||
return accountSigners.second;
|
||||
if (!accountSigners)
|
||||
return accountSigners.error();
|
||||
|
||||
// Get the array of transaction signers.
|
||||
STArray const& txSigners(ctx.tx.getFieldArray(sfSigners));
|
||||
@@ -567,7 +567,7 @@ Transactor::checkMultiSign(PreclaimContext const& ctx)
|
||||
// matching multi-signers to account signers should be a simple
|
||||
// linear walk. *All* signers must be valid or the transaction fails.
|
||||
std::uint32_t weightSum = 0;
|
||||
auto iter = accountSigners.first.begin();
|
||||
auto iter = accountSigners->begin();
|
||||
for (auto const& txSigner : txSigners)
|
||||
{
|
||||
AccountID const txSignerAcctID = txSigner.getAccountID(sfAccount);
|
||||
@@ -575,7 +575,7 @@ Transactor::checkMultiSign(PreclaimContext const& ctx)
|
||||
// Attempt to match the SignerEntry with a Signer;
|
||||
while (iter->account < txSignerAcctID)
|
||||
{
|
||||
if (++iter == accountSigners.first.end())
|
||||
if (++iter == accountSigners->end())
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "applyTransaction: Invalid SigningAccount.Account.";
|
||||
|
||||
@@ -55,10 +55,10 @@ checkValidity(
|
||||
: STTx::RequireFullyCanonicalSig::no;
|
||||
|
||||
auto const sigVerify = tx.checkSign(requireCanonicalSig);
|
||||
if (!sigVerify.first)
|
||||
if (!sigVerify)
|
||||
{
|
||||
router.setFlags(id, SF_SIGBAD);
|
||||
return {Validity::SigBad, sigVerify.second};
|
||||
return {Validity::SigBad, sigVerify.error()};
|
||||
}
|
||||
router.setFlags(id, SF_SIGGOOD);
|
||||
}
|
||||
|
||||
243
src/ripple/basics/Expected.h
Normal file
243
src/ripple/basics/Expected.h
Normal file
@@ -0,0 +1,243 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2021 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_BASICS_EXPECTED_H_INCLUDED
|
||||
#define RIPPLE_BASICS_EXPECTED_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <boost/outcome.hpp>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Expected is an approximation of std::expected (hoped for in C++23)
|
||||
|
||||
See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html
|
||||
|
||||
The implementation is entirely based on boost::outcome_v2::result.
|
||||
*/
|
||||
|
||||
// Exception thrown by an invalid access to Expected.
|
||||
struct bad_expected_access : public std::runtime_error
|
||||
{
|
||||
bad_expected_access() : runtime_error("bad expected access")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Custom policy for Expected. Always throw on an invalid access.
|
||||
struct throw_policy : public boost::outcome_v2::policy::base
|
||||
{
|
||||
template <class Impl>
|
||||
static constexpr void
|
||||
wide_value_check(Impl&& self)
|
||||
{
|
||||
if (!base::_has_value(std::forward<Impl>(self)))
|
||||
Throw<bad_expected_access>();
|
||||
}
|
||||
|
||||
template <class Impl>
|
||||
static constexpr void
|
||||
wide_error_check(Impl&& self)
|
||||
{
|
||||
if (!base::_has_error(std::forward<Impl>(self)))
|
||||
Throw<bad_expected_access>();
|
||||
}
|
||||
|
||||
template <class Impl>
|
||||
static constexpr void
|
||||
wide_exception_check(Impl&& self)
|
||||
{
|
||||
if (!base::_has_exception(std::forward<Impl>(self)))
|
||||
Throw<bad_expected_access>();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Definition of Unexpected, which is used to construct the unexpected
|
||||
// return type of an Expected.
|
||||
template <class E>
|
||||
class Unexpected
|
||||
{
|
||||
public:
|
||||
static_assert(!std::is_same<E, void>::value, "E must not be void");
|
||||
|
||||
Unexpected() = delete;
|
||||
|
||||
constexpr explicit Unexpected(E const& e) : val_(e)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr explicit Unexpected(E&& e) : val_(std::move(e))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr const E&
|
||||
value() const&
|
||||
{
|
||||
return val_;
|
||||
}
|
||||
|
||||
constexpr E&
|
||||
value() &
|
||||
{
|
||||
return val_;
|
||||
}
|
||||
|
||||
constexpr E&&
|
||||
value() &&
|
||||
{
|
||||
return std::move(val_);
|
||||
}
|
||||
|
||||
constexpr const E&&
|
||||
value() const&&
|
||||
{
|
||||
return std::move(val_);
|
||||
}
|
||||
|
||||
private:
|
||||
E val_;
|
||||
};
|
||||
|
||||
// Unexpected deduction guide that converts array to const*.
|
||||
template <typename E, std::size_t N>
|
||||
Unexpected(E (&)[N]) -> Unexpected<E const*>;
|
||||
|
||||
// Definition of Expected. All of the machinery comes from boost::result.
|
||||
template <class T, class E>
|
||||
class [[nodiscard]] Expected
|
||||
: private boost::outcome_v2::result<T, E, detail::throw_policy>
|
||||
{
|
||||
using Base = boost::outcome_v2::result<T, E, detail::throw_policy>;
|
||||
|
||||
public:
|
||||
template <
|
||||
typename U,
|
||||
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
constexpr Expected(U r) : Base(T{std::forward<U>(r)})
|
||||
{
|
||||
}
|
||||
|
||||
template <
|
||||
typename U,
|
||||
typename = std::enable_if_t<std::is_convertible_v<U, E>>>
|
||||
constexpr Expected(Unexpected<U> e) : Base(E{std::forward<U>(e.value())})
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool has_value() const
|
||||
{
|
||||
return Base::has_value();
|
||||
}
|
||||
|
||||
constexpr T const& value() const
|
||||
{
|
||||
return Base::value();
|
||||
}
|
||||
|
||||
constexpr T& value()
|
||||
{
|
||||
return Base::value();
|
||||
}
|
||||
|
||||
constexpr E const& error() const
|
||||
{
|
||||
return Base::error();
|
||||
}
|
||||
|
||||
constexpr E& error()
|
||||
{
|
||||
return Base::error();
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const
|
||||
{
|
||||
return has_value();
|
||||
}
|
||||
|
||||
// Add operator* and operator-> so the Expected API looks a bit more like
|
||||
// what std::expected is likely to look like. See:
|
||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html
|
||||
[[nodiscard]] constexpr T& operator*()
|
||||
{
|
||||
return this->value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T const& operator*() const
|
||||
{
|
||||
return this->value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T* operator->()
|
||||
{
|
||||
return &this->value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T const* operator->() const
|
||||
{
|
||||
return &this->value();
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization of Expected<void, E>. Allows returning either success
|
||||
// (without a value) or the reason for the failure.
|
||||
template <class E>
|
||||
class [[nodiscard]] Expected<void, E>
|
||||
: private boost::outcome_v2::result<void, E, detail::throw_policy>
|
||||
{
|
||||
using Base = boost::outcome_v2::result<void, E, detail::throw_policy>;
|
||||
|
||||
public:
|
||||
// The default constructor makes a successful Expected<void, E>.
|
||||
// This aligns with std::expected behavior proposed in P0323R10.
|
||||
constexpr Expected() : Base(boost::outcome_v2::success())
|
||||
{
|
||||
}
|
||||
|
||||
template <
|
||||
typename U,
|
||||
typename = std::enable_if_t<std::is_convertible_v<U, E>>>
|
||||
constexpr Expected(Unexpected<U> e) : Base(E{std::forward<U>(e.value())})
|
||||
{
|
||||
}
|
||||
|
||||
constexpr E const& error() const
|
||||
{
|
||||
return Base::error();
|
||||
}
|
||||
|
||||
constexpr E& error()
|
||||
{
|
||||
return Base::error();
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const
|
||||
{
|
||||
return Base::has_value();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif // RIPPLE_BASICS_EXPECTED_H_INCLUDED
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifndef RIPPLE_BASICS_BASE_UINT_H_INCLUDED
|
||||
#define RIPPLE_BASICS_BASE_UINT_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/Expected.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/basics/hardened_hash.h>
|
||||
#include <ripple/basics/strHex.h>
|
||||
@@ -185,7 +186,7 @@ private:
|
||||
badChar,
|
||||
};
|
||||
|
||||
constexpr std::pair<ParseResult, decltype(data_)>
|
||||
constexpr Expected<decltype(data_), ParseResult>
|
||||
parseFromStringView(std::string_view sv) noexcept
|
||||
{
|
||||
// Local lambda that converts a single hex char to four bits and
|
||||
@@ -212,7 +213,7 @@ private:
|
||||
return ParseResult::okay;
|
||||
};
|
||||
|
||||
std::pair<ParseResult, decltype(data_)> ret{ParseResult::okay, {}};
|
||||
decltype(data_) ret{};
|
||||
|
||||
if (sv == "0")
|
||||
{
|
||||
@@ -220,15 +221,10 @@ private:
|
||||
}
|
||||
|
||||
if (sv.size() != size() * 2)
|
||||
{
|
||||
ret.first = ParseResult::badLength;
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto out = ret.second.data();
|
||||
return Unexpected(ParseResult::badLength);
|
||||
|
||||
std::size_t i = 0u;
|
||||
auto in = sv.begin();
|
||||
|
||||
while (in != sv.end())
|
||||
{
|
||||
std::uint32_t accum = {};
|
||||
@@ -236,12 +232,9 @@ private:
|
||||
{
|
||||
if (auto const result = hexCharToUInt(*in++, shift, accum);
|
||||
result != ParseResult::okay)
|
||||
{
|
||||
ret.first = result;
|
||||
return ret;
|
||||
}
|
||||
return Unexpected(result);
|
||||
}
|
||||
*out++ = accum;
|
||||
ret[i++] = accum;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -250,13 +243,14 @@ private:
|
||||
parseFromStringViewThrows(std::string_view sv) noexcept(false)
|
||||
{
|
||||
auto const result = parseFromStringView(sv);
|
||||
if (result.first == ParseResult::badLength)
|
||||
Throw<std::invalid_argument>("invalid length for hex string");
|
||||
if (!result)
|
||||
{
|
||||
if (result.error() == ParseResult::badLength)
|
||||
Throw<std::invalid_argument>("invalid length for hex string");
|
||||
|
||||
if (result.first == ParseResult::badChar)
|
||||
Throw<std::range_error>("invalid hex character");
|
||||
|
||||
return result.second;
|
||||
}
|
||||
return *result;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -481,10 +475,10 @@ public:
|
||||
parseHex(std::string_view sv)
|
||||
{
|
||||
auto const result = parseFromStringView(sv);
|
||||
if (result.first != ParseResult::okay)
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
data_ = result.second;
|
||||
data_ = *result;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#ifndef RIPPLE_PROTOCOL_STTX_H_INCLUDED
|
||||
#define RIPPLE_PROTOCOL_STTX_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/Expected.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/STObject.h>
|
||||
#include <ripple/protocol/SecretKey.h>
|
||||
@@ -131,7 +132,7 @@ public:
|
||||
@return `true` if valid signature. If invalid, the error message string.
|
||||
*/
|
||||
enum class RequireFullyCanonicalSig : bool { no, yes };
|
||||
std::pair<bool, std::string>
|
||||
Expected<void, std::string>
|
||||
checkSign(RequireFullyCanonicalSig requireCanonicalSig) const;
|
||||
|
||||
// SQL Functions with metadata.
|
||||
@@ -150,10 +151,10 @@ public:
|
||||
std::string const& escapedMetaData) const;
|
||||
|
||||
private:
|
||||
std::pair<bool, std::string>
|
||||
Expected<void, std::string>
|
||||
checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const;
|
||||
|
||||
std::pair<bool, std::string>
|
||||
Expected<void, std::string>
|
||||
checkMultiSign(RequireFullyCanonicalSig requireCanonicalSig) const;
|
||||
|
||||
uint256 tid_;
|
||||
|
||||
@@ -349,7 +349,10 @@ public:
|
||||
}
|
||||
|
||||
// Trait tells enable_if which types are allowed for construction.
|
||||
template <typename T, typename = std::enable_if_t<Trait<T>::value>>
|
||||
template <
|
||||
typename T,
|
||||
typename = std::enable_if_t<
|
||||
Trait<std::remove_cv_t<std::remove_reference_t<T>>>::value>>
|
||||
constexpr TERSubset(T rhs) : code_(TERtoInt(rhs))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -186,24 +186,22 @@ STTx::sign(PublicKey const& publicKey, SecretKey const& secretKey)
|
||||
tid_ = getHash(HashPrefix::transactionID);
|
||||
}
|
||||
|
||||
std::pair<bool, std::string>
|
||||
Expected<void, std::string>
|
||||
STTx::checkSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
||||
{
|
||||
std::pair<bool, std::string> ret{false, ""};
|
||||
try
|
||||
{
|
||||
// Determine whether we're single- or multi-signing by looking
|
||||
// at the SigningPubKey. If it's empty we must be
|
||||
// multi-signing. Otherwise we're single-signing.
|
||||
Blob const& signingPubKey = getFieldVL(sfSigningPubKey);
|
||||
ret = signingPubKey.empty() ? checkMultiSign(requireCanonicalSig)
|
||||
: checkSingleSign(requireCanonicalSig);
|
||||
return signingPubKey.empty() ? checkMultiSign(requireCanonicalSig)
|
||||
: checkSingleSign(requireCanonicalSig);
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
ret = {false, "Internal signature check failure."};
|
||||
}
|
||||
return ret;
|
||||
return Unexpected("Internal signature check failure.");
|
||||
}
|
||||
|
||||
Json::Value STTx::getJson(JsonOptions) const
|
||||
@@ -269,14 +267,14 @@ STTx::getMetaSQL(
|
||||
getFieldU32(sfSequence) % inLedger % status % rTxn % escapedMetaData);
|
||||
}
|
||||
|
||||
std::pair<bool, std::string>
|
||||
Expected<void, std::string>
|
||||
STTx::checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
||||
{
|
||||
// We don't allow both a non-empty sfSigningPubKey and an sfSigners.
|
||||
// That would allow the transaction to be signed two ways. So if both
|
||||
// fields are present the signature is invalid.
|
||||
if (isFieldPresent(sfSigners))
|
||||
return {false, "Cannot both single- and multi-sign."};
|
||||
return Unexpected("Cannot both single- and multi-sign.");
|
||||
|
||||
bool validSig = false;
|
||||
try
|
||||
@@ -304,29 +302,29 @@ STTx::checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
||||
validSig = false;
|
||||
}
|
||||
if (validSig == false)
|
||||
return {false, "Invalid signature."};
|
||||
|
||||
return {true, ""};
|
||||
return Unexpected("Invalid signature.");
|
||||
// Signature was verified.
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<bool, std::string>
|
||||
Expected<void, std::string>
|
||||
STTx::checkMultiSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
||||
{
|
||||
// Make sure the MultiSigners are present. Otherwise they are not
|
||||
// attempting multi-signing and we just have a bad SigningPubKey.
|
||||
if (!isFieldPresent(sfSigners))
|
||||
return {false, "Empty SigningPubKey."};
|
||||
return Unexpected("Empty SigningPubKey.");
|
||||
|
||||
// We don't allow both an sfSigners and an sfTxnSignature. Both fields
|
||||
// being present would indicate that the transaction is signed both ways.
|
||||
if (isFieldPresent(sfTxnSignature))
|
||||
return {false, "Cannot both single- and multi-sign."};
|
||||
return Unexpected("Cannot both single- and multi-sign.");
|
||||
|
||||
STArray const& signers{getFieldArray(sfSigners)};
|
||||
|
||||
// There are well known bounds that the number of signers must be within.
|
||||
if (signers.size() < minMultiSigners || signers.size() > maxMultiSigners)
|
||||
return {false, "Invalid Signers array size."};
|
||||
return Unexpected("Invalid Signers array size.");
|
||||
|
||||
// We can ease the computational load inside the loop a bit by
|
||||
// pre-constructing part of the data that we hash. Fill a Serializer
|
||||
@@ -349,15 +347,15 @@ STTx::checkMultiSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
||||
|
||||
// The account owner may not multisign for themselves.
|
||||
if (accountID == txnAccountID)
|
||||
return {false, "Invalid multisigner."};
|
||||
return Unexpected("Invalid multisigner.");
|
||||
|
||||
// No duplicate signers allowed.
|
||||
if (lastAccountID == accountID)
|
||||
return {false, "Duplicate Signers not allowed."};
|
||||
return Unexpected("Duplicate Signers not allowed.");
|
||||
|
||||
// Accounts must be in order by account ID. No duplicates allowed.
|
||||
if (lastAccountID > accountID)
|
||||
return {false, "Unsorted Signers array."};
|
||||
return Unexpected("Unsorted Signers array.");
|
||||
|
||||
// The next signature must be greater than this one.
|
||||
lastAccountID = accountID;
|
||||
@@ -388,14 +386,12 @@ STTx::checkMultiSign(RequireFullyCanonicalSig requireCanonicalSig) const
|
||||
validSig = false;
|
||||
}
|
||||
if (!validSig)
|
||||
return {
|
||||
false,
|
||||
return Unexpected(
|
||||
std::string("Invalid signature on account ") +
|
||||
toBase58(accountID) + "."};
|
||||
toBase58(accountID) + ".");
|
||||
}
|
||||
|
||||
// All signatures verified.
|
||||
return {true, ""};
|
||||
return {};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
212
src/test/basics/Expected_test.cpp
Normal file
212
src/test/basics/Expected_test.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github0.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/Expected.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct Expected_test : beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
// Test non-error const construction.
|
||||
{
|
||||
auto const expected = []() -> Expected<std::string, TER> {
|
||||
return "Valid value";
|
||||
}();
|
||||
BEAST_EXPECT(expected);
|
||||
BEAST_EXPECT(expected.has_value());
|
||||
BEAST_EXPECT(expected.value() == "Valid value");
|
||||
BEAST_EXPECT(*expected == "Valid value");
|
||||
BEAST_EXPECT(expected->at(0) == 'V');
|
||||
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no error, so should throw.
|
||||
[[maybe_unused]] TER const t = expected.error();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
BEAST_EXPECT(throwOccurred);
|
||||
}
|
||||
// Test non-error non-const construction.
|
||||
{
|
||||
auto expected = []() -> Expected<std::string, TER> {
|
||||
return "Valid value";
|
||||
}();
|
||||
BEAST_EXPECT(expected);
|
||||
BEAST_EXPECT(expected.has_value());
|
||||
BEAST_EXPECT(expected.value() == "Valid value");
|
||||
BEAST_EXPECT(*expected == "Valid value");
|
||||
BEAST_EXPECT(expected->at(0) == 'V');
|
||||
std::string mv = std::move(*expected);
|
||||
BEAST_EXPECT(mv == "Valid value");
|
||||
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no error, so should throw.
|
||||
[[maybe_unused]] TER const t = expected.error();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
BEAST_EXPECT(throwOccurred);
|
||||
}
|
||||
// Test error construction from rvalue.
|
||||
{
|
||||
auto const expected = []() -> Expected<std::string, TER> {
|
||||
return Unexpected(telLOCAL_ERROR);
|
||||
}();
|
||||
BEAST_EXPECT(!expected);
|
||||
BEAST_EXPECT(!expected.has_value());
|
||||
BEAST_EXPECT(expected.error() == telLOCAL_ERROR);
|
||||
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no result, so should throw.
|
||||
[[maybe_unused]] std::string const s = *expected;
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
BEAST_EXPECT(throwOccurred);
|
||||
}
|
||||
// Test error construction from lvalue.
|
||||
{
|
||||
auto const err(telLOCAL_ERROR);
|
||||
auto expected = [&err]() -> Expected<std::string, TER> {
|
||||
return Unexpected(err);
|
||||
}();
|
||||
BEAST_EXPECT(!expected);
|
||||
BEAST_EXPECT(!expected.has_value());
|
||||
BEAST_EXPECT(expected.error() == telLOCAL_ERROR);
|
||||
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no result, so should throw.
|
||||
[[maybe_unused]] std::size_t const s = expected->size();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
BEAST_EXPECT(throwOccurred);
|
||||
}
|
||||
// Test error construction from const char*.
|
||||
{
|
||||
auto const expected = []() -> Expected<int, char const*> {
|
||||
return Unexpected("Not what is expected!");
|
||||
}();
|
||||
BEAST_EXPECT(!expected);
|
||||
BEAST_EXPECT(!expected.has_value());
|
||||
BEAST_EXPECT(
|
||||
expected.error() == std::string("Not what is expected!"));
|
||||
}
|
||||
// Test error construction of string from const char*.
|
||||
{
|
||||
auto expected = []() -> Expected<int, std::string> {
|
||||
return Unexpected("Not what is expected!");
|
||||
}();
|
||||
BEAST_EXPECT(!expected);
|
||||
BEAST_EXPECT(!expected.has_value());
|
||||
BEAST_EXPECT(expected.error() == "Not what is expected!");
|
||||
std::string const s(std::move(expected.error()));
|
||||
BEAST_EXPECT(s == "Not what is expected!");
|
||||
}
|
||||
// Test non-error const construction of Expected<void, T>.
|
||||
{
|
||||
auto const expected = []() -> Expected<void, std::string> {
|
||||
return {};
|
||||
}();
|
||||
BEAST_EXPECT(expected);
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no error, so should throw.
|
||||
[[maybe_unused]] std::size_t const s = expected.error().size();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
BEAST_EXPECT(throwOccurred);
|
||||
}
|
||||
// Test non-error non-const construction of Expected<void, T>.
|
||||
{
|
||||
auto expected = []() -> Expected<void, std::string> {
|
||||
return {};
|
||||
}();
|
||||
BEAST_EXPECT(expected);
|
||||
bool throwOccurred = false;
|
||||
try
|
||||
{
|
||||
// There's no error, so should throw.
|
||||
[[maybe_unused]] std::size_t const s = expected.error().size();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == std::string("bad expected access"));
|
||||
throwOccurred = true;
|
||||
}
|
||||
BEAST_EXPECT(throwOccurred);
|
||||
}
|
||||
// Test error const construction of Expected<void, T>.
|
||||
{
|
||||
auto const expected = []() -> Expected<void, std::string> {
|
||||
return Unexpected("Not what is expected!");
|
||||
}();
|
||||
BEAST_EXPECT(!expected);
|
||||
BEAST_EXPECT(expected.error() == "Not what is expected!");
|
||||
}
|
||||
// Test error non-const construction of Expected<void, T>.
|
||||
{
|
||||
auto expected = []() -> Expected<void, std::string> {
|
||||
return Unexpected("Not what is expected!");
|
||||
}();
|
||||
BEAST_EXPECT(!expected);
|
||||
BEAST_EXPECT(expected.error() == "Not what is expected!");
|
||||
std::string const s(std::move(expected.error()));
|
||||
BEAST_EXPECT(s == "Not what is expected!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Expected, ripple_basics, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -223,6 +223,47 @@ struct base_uint_test : beast::unit_test::suite
|
||||
static_assert(test96("00000000000000000000000~").signum() == 1);
|
||||
#endif // 0
|
||||
|
||||
// Using the constexpr constructor in a non-constexpr context
|
||||
// with an error in the parsing throws an exception.
|
||||
{
|
||||
// Invalid length for string.
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
// Try to prevent constant evaluation.
|
||||
std::vector<char> str(23, '7');
|
||||
std::string_view sView(str.data(), str.size());
|
||||
[[maybe_unused]] test96 t96(sView);
|
||||
}
|
||||
catch (std::invalid_argument const& e)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
e.what() ==
|
||||
std::string("invalid length for hex string"));
|
||||
caught = true;
|
||||
}
|
||||
BEAST_EXPECT(caught);
|
||||
}
|
||||
{
|
||||
// Invalid character in string.
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
// Try to prevent constant evaluation.
|
||||
std::vector<char> str(23, '7');
|
||||
str.push_back('G');
|
||||
std::string_view sView(str.data(), str.size());
|
||||
[[maybe_unused]] test96 t96(sView);
|
||||
}
|
||||
catch (std::range_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
e.what() == std::string("invalid hex character"));
|
||||
caught = true;
|
||||
}
|
||||
BEAST_EXPECT(caught);
|
||||
}
|
||||
|
||||
// Verify that constexpr base_uints interpret a string the same
|
||||
// way parseHex() does.
|
||||
struct StrBaseUint
|
||||
|
||||
@@ -1592,7 +1592,7 @@ public:
|
||||
j.sign(keypair.first, keypair.second);
|
||||
|
||||
unexpected(
|
||||
!j.checkSign(STTx::RequireFullyCanonicalSig::yes).first,
|
||||
!j.checkSign(STTx::RequireFullyCanonicalSig::yes),
|
||||
"Transaction fails signature test");
|
||||
|
||||
Serializer rawTxn;
|
||||
|
||||
Reference in New Issue
Block a user