From 2ec40cb6f1eeb76959b53666bbb9554f0893d805 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 24 Jul 2015 21:06:16 -0700 Subject: [PATCH] Add operator[] field accessors to STObject: New array index operators allow for concise reading and writing of fields in the STObject, with associated unit test. --- src/ripple/basics/Buffer.h | 15 + src/ripple/basics/Slice.h | 2 - src/ripple/protocol/PublicKey.h | 5 + src/ripple/protocol/SField.h | 269 ++++++---- src/ripple/protocol/SOTemplate.h | 12 + src/ripple/protocol/STAccount.h | 19 + src/ripple/protocol/STAmount.h | 8 + src/ripple/protocol/STBitString.h | 40 +- src/ripple/protocol/STBlob.h | 20 + src/ripple/protocol/STInteger.h | 11 + src/ripple/protocol/STObject.h | 517 ++++++++++++++++++++ src/ripple/protocol/STVector256.h | 27 +- src/ripple/protocol/impl/SField.cpp | 214 ++++---- src/ripple/protocol/tests/STObject.test.cpp | 320 +++++++++++- 14 files changed, 1230 insertions(+), 249 deletions(-) diff --git a/src/ripple/basics/Buffer.h b/src/ripple/basics/Buffer.h index e73eca5b73..7b8d4bcfb2 100644 --- a/src/ripple/basics/Buffer.h +++ b/src/ripple/basics/Buffer.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_BASICS_BUFFER_H_INCLUDED #define RIPPLE_BASICS_BUFFER_H_INCLUDED +#include #include #include #include @@ -80,6 +81,13 @@ public: std::memcpy(p_.get(), data, size); } + /** Create a buffer from a copy of existing memory. */ + explicit + Buffer (Slice const& slice) + : Buffer(slice.data(), slice.size()) + { + } + /** Returns the number of bytes in the buffer. */ std::size_t size() const noexcept @@ -93,6 +101,13 @@ public: return 0 == size_; } + operator Slice() const noexcept + { + if (! size_) + return Slice{}; + return Slice{ p_.get(), size_ }; + } + /** Return a pointer to beginning of the storage. @note The return type is guaranteed to be a pointer to a single byte, to facilitate pointer arithmetic. diff --git a/src/ripple/basics/Slice.h b/src/ripple/basics/Slice.h index 2276eaf77d..02db04e8db 100644 --- a/src/ripple/basics/Slice.h +++ b/src/ripple/basics/Slice.h @@ -58,8 +58,6 @@ public: std::uint8_t const*>(data)) , size_ (size) { - assert(data_ != nullptr); - assert(size_ > 0); } /** Return `true` if the byte range is empty. */ diff --git a/src/ripple/protocol/PublicKey.h b/src/ripple/protocol/PublicKey.h index 17881dc436..dda06c54e8 100644 --- a/src/ripple/protocol/PublicKey.h +++ b/src/ripple/protocol/PublicKey.h @@ -97,6 +97,11 @@ public: return { buf_, size_ }; } + operator Slice() const noexcept + { + return slice(); + } + bool verify (Slice const& message, Slice const& sig, bool mustBeFullyCanonical) const; diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index ec1877acb0..1545177785 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -27,10 +27,26 @@ namespace ripple { +/* + +Some fields have a different meaning for their + default value versus not present. + Example: + QualityIn on a TrustLine + +*/ + +//------------------------------------------------------------------------------ + // Forwards +class STAccount; +class STAmount; class STBlob; +template +class STBitString; template class STInteger; +class STVector256; enum SerializedTypeID { @@ -280,6 +296,45 @@ struct TypedField : SField } }; +/** Indicate boost::optional field semantics. */ +template +struct OptionaledField +{ + TypedField const* f; + + explicit + OptionaledField (TypedField const& f_) + : f (&f_) + { + } +}; + +template +inline +OptionaledField +operator~(TypedField const& f) +{ + return OptionaledField(f); +} + +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ + +using SF_U8 = TypedField>; +using SF_U16 = TypedField>; +using SF_U32 = TypedField>; +using SF_U64 = TypedField>; +using SF_U128 = TypedField>; +using SF_U160 = TypedField>; +using SF_U256 = TypedField>; +using SF_Account = TypedField; +using SF_Amount = TypedField; +using SF_Blob = TypedField; +using SF_Vec256 = TypedField; + +//------------------------------------------------------------------------------ + extern SField const sfInvalid; extern SField const sfGeneric; extern SField const sfLedgerEntry; @@ -288,139 +343,141 @@ extern SField const sfValidation; extern SField const sfMetadata; // 8-bit integers -extern SField const sfCloseResolution; -extern SField const sfTemplateEntryType; -extern SField const sfTransactionResult; +extern SF_U8 const sfCloseResolution; +extern SF_U8 const sfTemplateEntryType; +extern SF_U8 const sfTransactionResult; // 16-bit integers -extern SField const sfLedgerEntryType; -extern SField const sfTransactionType; -extern SField const sfSignerWeight; +extern SF_U16 const sfLedgerEntryType; +extern SF_U16 const sfTransactionType; +extern SF_U16 const sfSignerWeight; // 32-bit integers (common) -extern SField const sfFlags; -extern SField const sfSourceTag; -extern TypedField> const sfSequence; -extern SField const sfPreviousTxnLgrSeq; -extern SField const sfLedgerSequence; -extern SField const sfCloseTime; -extern SField const sfParentCloseTime; -extern SField const sfSigningTime; -extern SField const sfExpiration; -extern SField const sfTransferRate; -extern SField const sfWalletSize; -extern SField const sfOwnerCount; -extern SField const sfDestinationTag; +extern SF_U32 const sfFlags; +extern SF_U32 const sfSourceTag; +extern SF_U32 const sfSequence; +extern SF_U32 const sfPreviousTxnLgrSeq; +extern SF_U32 const sfLedgerSequence; +extern SF_U32 const sfCloseTime; +extern SF_U32 const sfParentCloseTime; +extern SF_U32 const sfSigningTime; +extern SF_U32 const sfExpiration; +extern SF_U32 const sfTransferRate; +extern SF_U32 const sfWalletSize; +extern SF_U32 const sfOwnerCount; +extern SF_U32 const sfDestinationTag; // 32-bit integers (uncommon) -extern SField const sfHighQualityIn; -extern SField const sfHighQualityOut; -extern SField const sfLowQualityIn; -extern SField const sfLowQualityOut; -extern SField const sfQualityIn; -extern SField const sfQualityOut; -extern SField const sfStampEscrow; -extern SField const sfBondAmount; -extern SField const sfLoadFee; -extern SField const sfOfferSequence; -extern SField const sfFirstLedgerSequence; // Deprecated: do not use -extern SField const sfLastLedgerSequence; -extern SField const sfTransactionIndex; -extern SField const sfOperationLimit; -extern SField const sfReferenceFeeUnits; -extern SField const sfReserveBase; -extern SField const sfReserveIncrement; -extern SField const sfSetFlag; -extern SField const sfClearFlag; -extern SField const sfSignerQuorum; +extern SF_U32 const sfHighQualityIn; +extern SF_U32 const sfHighQualityOut; +extern SF_U32 const sfLowQualityIn; +extern SF_U32 const sfLowQualityOut; +extern SF_U32 const sfQualityIn; +extern SF_U32 const sfQualityOut; +extern SF_U32 const sfStampEscrow; +extern SF_U32 const sfBondAmount; +extern SF_U32 const sfLoadFee; +extern SF_U32 const sfOfferSequence; +extern SF_U32 const sfFirstLedgerSequence; // Deprecated: do not use +extern SF_U32 const sfLastLedgerSequence; +extern SF_U32 const sfTransactionIndex; +extern SF_U32 const sfOperationLimit; +extern SF_U32 const sfReferenceFeeUnits; +extern SF_U32 const sfReserveBase; +extern SF_U32 const sfReserveIncrement; +extern SF_U32 const sfSetFlag; +extern SF_U32 const sfClearFlag; +extern SF_U32 const sfSignerQuorum; // 64-bit integers -extern SField const sfIndexNext; -extern SField const sfIndexPrevious; -extern SField const sfBookNode; -extern SField const sfOwnerNode; -extern SField const sfBaseFee; -extern SField const sfExchangeRate; -extern SField const sfLowNode; -extern SField const sfHighNode; +extern SF_U64 const sfIndexNext; +extern SF_U64 const sfIndexPrevious; +extern SF_U64 const sfBookNode; +extern SF_U64 const sfOwnerNode; +extern SF_U64 const sfBaseFee; +extern SF_U64 const sfExchangeRate; +extern SF_U64 const sfLowNode; +extern SF_U64 const sfHighNode; // 128-bit -extern SField const sfEmailHash; - -// 256-bit (common) -extern SField const sfLedgerHash; -extern SField const sfParentHash; -extern SField const sfTransactionHash; -extern SField const sfAccountHash; -extern SField const sfPreviousTxnID; -extern SField const sfLedgerIndex; -extern SField const sfWalletLocator; -extern SField const sfRootIndex; -extern SField const sfAccountTxnID; - -// 256-bit (uncommon) -extern SField const sfBookDirectory; -extern SField const sfInvoiceID; -extern SField const sfNickname; -extern SField const sfAmendment; -extern SField const sfTicketID; +extern SF_U128 const sfEmailHash; // 160-bit (common) -extern SField const sfTakerPaysCurrency; -extern SField const sfTakerPaysIssuer; -extern SField const sfTakerGetsCurrency; -extern SField const sfTakerGetsIssuer; +extern SF_U160 const sfTakerPaysCurrency; +extern SF_U160 const sfTakerPaysIssuer; +extern SF_U160 const sfTakerGetsCurrency; +extern SF_U160 const sfTakerGetsIssuer; + +// 256-bit (common) +extern SF_U256 const sfLedgerHash; +extern SF_U256 const sfParentHash; +extern SF_U256 const sfTransactionHash; +extern SF_U256 const sfAccountHash; +extern SF_U256 const sfPreviousTxnID; +extern SF_U256 const sfLedgerIndex; +extern SF_U256 const sfWalletLocator; +extern SF_U256 const sfRootIndex; +extern SF_U256 const sfAccountTxnID; + +// 256-bit (uncommon) +extern SF_U256 const sfBookDirectory; +extern SF_U256 const sfInvoiceID; +extern SF_U256 const sfNickname; +extern SF_U256 const sfAmendment; +extern SF_U256 const sfTicketID; +extern SF_U256 const sfDigest; // currency amount (common) -extern SField const sfAmount; -extern SField const sfBalance; -extern SField const sfLimitAmount; -extern SField const sfTakerPays; -extern SField const sfTakerGets; -extern SField const sfLowLimit; -extern SField const sfHighLimit; -extern SField const sfFee; -extern SField const sfSendMax; -extern SField const sfDeliverMin; +extern SF_Amount const sfAmount; +extern SF_Amount const sfBalance; +extern SF_Amount const sfLimitAmount; +extern SF_Amount const sfTakerPays; +extern SF_Amount const sfTakerGets; +extern SF_Amount const sfLowLimit; +extern SF_Amount const sfHighLimit; +extern SF_Amount const sfFee; +extern SF_Amount const sfSendMax; +extern SF_Amount const sfDeliverMin; // currency amount (uncommon) -extern SField const sfMinimumOffer; -extern SField const sfRippleEscrow; -extern SField const sfDeliveredAmount; +extern SF_Amount const sfMinimumOffer; +extern SF_Amount const sfRippleEscrow; +extern SF_Amount const sfDeliveredAmount; // variable length (common) -extern TypedField const sfPublicKey; -extern SField const sfMessageKey; -extern TypedField const sfSigningPubKey; -extern SField const sfTxnSignature; -extern TypedField const sfSignature; -extern SField const sfDomain; -extern SField const sfFundCode; -extern SField const sfRemoveCode; -extern SField const sfExpireCode; -extern SField const sfCreateCode; -extern SField const sfMemoType; -extern SField const sfMemoData; -extern SField const sfMemoFormat; +extern SF_Blob const sfPublicKey; +extern SF_Blob const sfMessageKey; +extern SF_Blob const sfSigningPubKey; +extern SF_Blob const sfTxnSignature; +extern SF_Blob const sfSignature; +extern SF_Blob const sfDomain; +extern SF_Blob const sfFundCode; +extern SF_Blob const sfRemoveCode; +extern SF_Blob const sfExpireCode; +extern SF_Blob const sfCreateCode; +extern SF_Blob const sfMemoType; +extern SF_Blob const sfMemoData; +extern SF_Blob const sfMemoFormat; // variable length (uncommon) -extern SField const sfMultiSignature; +extern SF_Blob const sfMultiSignature; +extern SF_Blob const sfInnerSig; // account -extern SField const sfAccount; -extern SField const sfOwner; -extern SField const sfDestination; -extern SField const sfIssuer; -extern SField const sfTarget; -extern SField const sfRegularKey; +extern SF_Account const sfAccount; +extern SF_Account const sfOwner; +extern SF_Account const sfDestination; +extern SF_Account const sfIssuer; +extern SF_Account const sfTarget; +extern SF_Account const sfRegularKey; // path set extern SField const sfPaths; // vector of 256-bit -extern SField const sfIndexes; -extern SField const sfHashes; -extern SField const sfAmendments; +extern SF_Vec256 const sfIndexes; +extern SF_Vec256 const sfHashes; +extern SF_Vec256 const sfAmendments; // inner object // OBJECT/1 is reserved for end of object diff --git a/src/ripple/protocol/SOTemplate.h b/src/ripple/protocol/SOTemplate.h index ca35bedd98..78084ffff3 100644 --- a/src/ripple/protocol/SOTemplate.h +++ b/src/ripple/protocol/SOTemplate.h @@ -71,6 +71,12 @@ public: */ SOTemplate () = default; + SOTemplate(SOTemplate&& other) + : mTypes(std::move(other.mTypes)) + , mIndex(std::move(other.mIndex)) + { + } + /* Provide for the enumeration of fields */ iterator_range all () const { @@ -89,6 +95,12 @@ public: /** Retrieve the position of a named field. */ int getIndex (SField const&) const; + SOE_Flags + style(SField const& sf) const + { + return mTypes[mIndex[sf.getNum()]]->flags; + } + private: list_type mTypes; diff --git a/src/ripple/protocol/STAccount.h b/src/ripple/protocol/STAccount.h index 1b2530169e..3a6747d5f3 100644 --- a/src/ripple/protocol/STAccount.h +++ b/src/ripple/protocol/STAccount.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_PROTOCOL_STACCOUNT_H_INCLUDED #define RIPPLE_PROTOCOL_STACCOUNT_H_INCLUDED +#include #include #include #include @@ -30,6 +31,8 @@ class STAccount final : public STBlob { public: + using value_type = AccountID; + STAccount (SField const& n, Buffer&& v) : STBlob (n, std::move(v)) { @@ -63,8 +66,24 @@ public: { return STI_ACCOUNT; } + std::string getText () const override; + STAccount& + operator= (value_type const& value) + { + setValueH160(value); + return *this; + } + + value_type + value() const noexcept + { + AccountID result; + getValueH160(result); + return result; + } + template void setValueH160 (base_uint<160, Tag> const& v) { diff --git a/src/ripple/protocol/STAmount.h b/src/ripple/protocol/STAmount.h index 9d68d3804f..601b8a0c24 100644 --- a/src/ripple/protocol/STAmount.h +++ b/src/ripple/protocol/STAmount.h @@ -54,6 +54,8 @@ private: bool mIsNegative; public: + using value_type = STAmount; + static const int cMinOffset = -96; static const int cMaxOffset = 80; @@ -166,6 +168,12 @@ public: void setJson (Json::Value&) const; + STAmount const& + value() const noexcept + { + return *this; + } + //-------------------------------------------------------------------------- // // Operators diff --git a/src/ripple/protocol/STBitString.h b/src/ripple/protocol/STBitString.h index ba6fc718d3..c7acf6a19d 100644 --- a/src/ripple/protocol/STBitString.h +++ b/src/ripple/protocol/STBitString.h @@ -29,7 +29,7 @@ class STBitString final : public STBase { public: - using BitString = base_uint; + using value_type = base_uint; STBitString () = default; @@ -37,24 +37,24 @@ public: : STBase (n) { } - STBitString (const BitString& v) - : bitString_ (v) + STBitString (const value_type& v) + : value_ (v) { } - STBitString (SField const& n, const BitString& v) - : STBase (n), bitString_ (v) + STBitString (SField const& n, const value_type& v) + : STBase (n), value_ (v) { } STBitString (SField const& n, const char* v) : STBase (n) { - bitString_.SetHex (v); + value_.SetHex (v); } STBitString (SField const& n, std::string const& v) : STBase (n) { - bitString_.SetHex (v); + value_.SetHex (v); } STBitString (SerialIter& sit, SField const& name) @@ -80,14 +80,14 @@ public: std::string getText () const override { - return to_string (bitString_); + return to_string (value_); } bool isEquivalent (const STBase& t) const override { const STBitString* v = dynamic_cast (&t); - return v && (bitString_ == v->bitString_); + return v && (value_ == v->value_); } void @@ -95,34 +95,40 @@ public: { assert (fName->isBinary ()); assert (fName->fieldType == getSType()); - s.addBitString (bitString_); + s.addBitString (value_); } - const BitString& + const value_type& getValue () const { - return bitString_; + return value_; } template void setValue (base_uint const& v) { - bitString_.copyFrom(v); + value_.copyFrom(v); } - operator BitString () const + value_type const& + value() const { - return bitString_; + return value_; + } + + operator value_type () const + { + return value_; } bool isDefault () const override { - return bitString_ == zero; + return value_ == zero; } private: - BitString bitString_; + value_type value_; }; using STHash128 = STBitString<128>; diff --git a/src/ripple/protocol/STBlob.h b/src/ripple/protocol/STBlob.h index 2caab5e792..3e4dbcc4bc 100644 --- a/src/ripple/protocol/STBlob.h +++ b/src/ripple/protocol/STBlob.h @@ -121,6 +121,26 @@ public: return value_; } + STBlob& + operator= (Slice const& slice) + { + value_ = Buffer(slice.data(), slice.size()); + return *this; + } + + value_type + value() const noexcept + { + return value_; + } + + STBlob& + operator= (Buffer&& buffer) + { + value_ = std::move(buffer); + return *this; + } + Buffer& peekValue () { diff --git a/src/ripple/protocol/STInteger.h b/src/ripple/protocol/STInteger.h index 15645a68bf..5ea610e932 100644 --- a/src/ripple/protocol/STInteger.h +++ b/src/ripple/protocol/STInteger.h @@ -77,6 +77,17 @@ public: return value_; } + STInteger& operator= (value_type const& v) + { + value_ = v; + return *this; + } + + value_type value() const noexcept + { + return value_; + } + void setValue (Integer v) { diff --git a/src/ripple/protocol/STObject.h b/src/ripple/protocol/STObject.h index 6dd3a0d9b0..bbf9d5b51f 100644 --- a/src/ripple/protocol/STObject.h +++ b/src/ripple/protocol/STObject.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_PROTOCOL_STOBJECT_H_INCLUDED #define RIPPLE_PROTOCOL_STOBJECT_H_INCLUDED +#include #include #include #include @@ -27,6 +28,10 @@ #include #include #include +#include +#include +#include +#include // #include #include @@ -38,11 +43,213 @@ namespace ripple { class STArray; +/** Thrown on illegal access to non-present SField. */ +struct missing_field_error : std::logic_error +{ + explicit + missing_field_error (SField const& f) + : logic_error( + "missing field '" + f.getName() + "'") + { + } +}; + +/** Thrown on a field template violation. */ +struct template_field_error : std::logic_error +{ + explicit + template_field_error (SField const& f) + : logic_error( + "template field error '" + f.getName() + "'") + { + } +}; + +//------------------------------------------------------------------------------ + class STObject : public STBase , public CountedObject { private: + // Proxy value for a STBase derived class + template + class Proxy + { + protected: + using value_type = + typename T::value_type; + + STObject* st_; + SOE_Flags style_; + TypedField const* f_; + + Proxy (Proxy const&) = default; + Proxy (STObject* st, TypedField const* f); + value_type value() const; + T const* find() const; + + template + void assign (U&& u); + }; + + template + class ValueProxy : private Proxy + { + private: + using value_type = + typename T::value_type; + + public: + ValueProxy& operator= (ValueProxy const&) = delete; + + template + std::enable_if_t< + std::is_assignable::value, + ValueProxy&> + operator= (U&& u); + + operator value_type() const; + + private: + friend class STObject; + + ValueProxy (STObject* st, TypedField const* f); + }; + + template + class OptionalProxy : private Proxy + { + private: + using value_type = + typename T::value_type; + + using optional_type = boost::optional< + typename std::decay::type>; + + public: + OptionalProxy& operator= (OptionalProxy const&) = delete; + + /** Returns `true` if the field is set. + + Fields with SOE_DEFAULT and set to the + default value will return `true` + */ + explicit operator bool() const noexcept; + + /** Return the contained value + + Throws: + + missing_field_error if !engaged() + */ + value_type operator*() const; + + operator optional_type() const; + + /** Explicit conversion to boost::optional */ + optional_type + operator~() const; + + friend bool operator==( + OptionalProxy const& lhs, + boost::none_t) noexcept + { + return ! lhs.engaged(); + } + + friend bool operator==( + boost::none_t, + OptionalProxy const& rhs) noexcept + { + return rhs == boost::none; + } + + friend bool operator==( + OptionalProxy const& lhs, + optional_type const& rhs) noexcept + { + if (! lhs.engaged()) + return ! rhs; + if (! rhs) + return false; + return *lhs == *rhs; + } + + friend bool operator==( + optional_type const& lhs, + OptionalProxy const& rhs) noexcept + { + return rhs == lhs; + } + + friend bool operator==( + OptionalProxy const& lhs, + OptionalProxy const& rhs) noexcept + { + if (lhs.engaged() != lhs.engaged()) + return false; + return ! lhs.engaged() || *lhs == *rhs; + } + + friend bool operator!=( + OptionalProxy const& lhs, + boost::none_t) noexcept + { + return ! (lhs == boost::none); + } + + friend bool operator!=(boost::none_t, + OptionalProxy const& rhs) noexcept + { + return ! (rhs == boost::none); + } + + friend bool operator!=( + OptionalProxy const& lhs, + optional_type const& rhs) noexcept + { + return ! (lhs == rhs); + } + + friend bool operator!=( + optional_type const& lhs, + OptionalProxy const& rhs) noexcept + { + return ! (lhs == rhs); + } + + friend bool operator!=( + OptionalProxy const& lhs, + OptionalProxy const& rhs) noexcept + { + return ! (lhs == rhs); + } + + OptionalProxy& operator= (boost::none_t const&); + OptionalProxy& operator= (optional_type&& v); + OptionalProxy& operator= (optional_type const& v); + + template + std::enable_if_t< + std::is_assignable::value, + OptionalProxy&> + operator= (U&& u); + + private: + friend class STObject; + + OptionalProxy (STObject* st, + TypedField const* f); + + bool engaged() const noexcept; + + void disengage(); + + optional_type + optional_value() const; + }; + struct Transform { using argument_type = detail::STVar; @@ -275,6 +482,45 @@ public: const STArray& getFieldArray (SField const& field) const; const STObject& getFieldObject (SField const& field) const; + /** Return the value of a field. + + Throws: + + missing_field_error if the field is + not present. + */ + template + typename T::value_type + operator[](TypedField const& f) const; + + /** Return the value of a field as boost::optional + + @return boost::none if the field is not present. + */ + template + boost::optional> + operator[](OptionaledField const& of) const; + + /** Return a modifiable field value. + + Throws: + + missing_field_error if the field is + not present. + */ + template + ValueProxy + operator[](TypedField const& f); + + /** Return a modifiable field value as boost::optional + + The return value equals boost::none if the + field is not present. + */ + template + OptionalProxy + operator[](OptionaledField const& of); + /** Set a field. if the field already exists, it is replaced. */ @@ -468,6 +714,277 @@ private: } }; +//------------------------------------------------------------------------------ + +template +STObject::Proxy::Proxy (STObject* st, TypedField const* f) + : st_ (st) + , f_ (f) +{ + if (st_->mType) + { + // STObject has associated template + if (! st_->peekAtPField(*f_)) + THROW(template_field_error, *f); + style_ = st_->mType->style(*f_); + } + else + { + style_ = SOE_INVALID; + } +} + +template +auto +STObject::Proxy::value() const -> + value_type +{ + auto const t = find(); + if (t) + return t->value(); + if (style_ != SOE_DEFAULT) + THROW(missing_field_error, *f_); + return value_type{}; +} + +template +inline +T const* +STObject::Proxy::find() const +{ + return dynamic_cast( + st_->peekAtPField(*f_)); +} + +template +template +void +STObject::Proxy::assign(U&& u) +{ + if (style_ == SOE_DEFAULT && + u == value_type{}) + { + st_->makeFieldAbsent(*f_); + return; + } + T* t; + if (style_ == SOE_INVALID) + t = dynamic_cast( + st_->getPField(*f_, true)); + else + t = dynamic_cast( + st_->makeFieldPresent(*f_)); + assert(t); + *t = std::forward(u); +} + +//------------------------------------------------------------------------------ + +template +template +std::enable_if_t< + std::is_assignable::value, + STObject::ValueProxy&> +STObject::ValueProxy::operator= (U&& u) +{ + this->assign(std::forward(u)); + return *this; +} + +template +STObject::ValueProxy::operator value_type() const +{ + return this->value(); +} + +template +STObject::ValueProxy::ValueProxy( + STObject* st, TypedField const* f) + : Proxy(st, f) +{ +} + +//------------------------------------------------------------------------------ + +template +STObject::OptionalProxy::operator bool() const noexcept +{ + return engaged(); +} + +template +auto +STObject::OptionalProxy::operator*() const -> + value_type +{ + return this->value(); +} + +template +STObject::OptionalProxy::operator + typename STObject::OptionalProxy::optional_type() const +{ + return optional_value(); +} + +template +typename STObject::OptionalProxy::optional_type +STObject::OptionalProxy::operator~() const +{ + return optional_value(); +} + +template +auto +STObject::OptionalProxy::operator=(boost::none_t const&) -> + OptionalProxy& +{ + disengage(); + return *this; +} + +template +auto +STObject::OptionalProxy::operator=(optional_type&& v) -> + OptionalProxy& +{ + if (v) + this->assign(std::move(*v)); + else + disengage(); + return *this; +} + +template +auto +STObject::OptionalProxy::operator=(optional_type const& v) -> + OptionalProxy& +{ + if (v) + this->assign(*v); + else + disengage(); + return *this; +} + +template +template +std::enable_if_t< + std::is_assignable::value, + STObject::OptionalProxy&> +STObject::OptionalProxy::operator=(U&& u) +{ + this->assign(std::forward(u)); + return *this; +} + +template +STObject::OptionalProxy::OptionalProxy( + STObject* st, TypedField const* f) + : Proxy(st, f) +{ +} + +template +bool +STObject::OptionalProxy::engaged() const noexcept +{ + return this->style_ == SOE_DEFAULT + || this->find() != nullptr; +} + +template +void +STObject::OptionalProxy::disengage() +{ + if (this->style_ == SOE_REQUIRED || + this->style_ == SOE_DEFAULT) + THROW(template_field_error, *this->f_); + if (this->style_ == SOE_INVALID) + this->st_->delField(*this->f_); + else + this->st_->makeFieldAbsent(*this->f_); +} + +template +auto +STObject::OptionalProxy::optional_value() const -> + optional_type +{ + if (! engaged()) + return boost::none; + return this->value(); +} + +//------------------------------------------------------------------------------ + +template +typename T::value_type +STObject::operator[](TypedField const& f) const +{ + auto const b = peekAtPField(f); + if (! b) + // This is a free object (no constraints) + // with no template + THROW(missing_field_error, f); + auto const u = + dynamic_cast(b); + if (! u) + { + assert(mType); + assert(b->getSType() == STI_NOTPRESENT); + if(mType->style(f) == SOE_OPTIONAL) + THROW(missing_field_error, f); + assert(mType->style(f) == SOE_DEFAULT); + // Handle the case where value_type is a + // const reference, otherwise we return + // the address of a temporary. + static std::decay_t< + typename T::value_type> const dv{}; + return dv; + } + return u->value(); +} + +template +boost::optional> +STObject::operator[](OptionaledField const& of) const +{ + auto const b = peekAtPField(*of.f); + if (! b) + return boost::none; + auto const u = + dynamic_cast(b); + if (! u) + { + assert(mType); + assert(b->getSType() == STI_NOTPRESENT); + if(mType->style(*of.f) == SOE_OPTIONAL) + return boost::none; + assert(mType->style(*of.f) == SOE_DEFAULT); + return typename T::value_type{}; + } + return u->value(); +} + +template +inline +auto +STObject::operator[](TypedField const& f) -> + ValueProxy +{ + return ValueProxy(this, &f); +} + +template +inline +auto +STObject::operator[](OptionaledField const& of) -> + OptionalProxy +{ + return OptionalProxy(this, of.f); +} + } // ripple #endif diff --git a/src/ripple/protocol/STVector256.h b/src/ripple/protocol/STVector256.h index 351b05a157..64508d0a97 100644 --- a/src/ripple/protocol/STVector256.h +++ b/src/ripple/protocol/STVector256.h @@ -31,6 +31,8 @@ class STVector256 : public STBase { public: + using value_type = std::vector const&; + STVector256 () = default; explicit STVector256 (SField const& n) @@ -80,6 +82,20 @@ public: return mValue.empty (); } + STVector256& + operator= (std::vector const& v) + { + mValue = v; + return *this; + } + + STVector256& + operator= (std::vector&& v) + { + mValue = std::move(v); + return *this; + } + void setValue (const STVector256& v) { @@ -93,15 +109,14 @@ public: return mValue; } - // std::vector interface: - std::vector::size_type + std::size_t size () const { return mValue.size (); } void - resize (std::vector::size_type n) + resize (std::size_t n) { return mValue.resize (n); } @@ -124,6 +139,12 @@ public: return mValue[n]; } + std::vector const& + value() const + { + return mValue; + } + void push_back (uint256 const& v) { diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index 46e04f76e8..27332a324d 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -76,139 +76,141 @@ SField const sfHash = make::one(&sfHash, STI_HASH256, 257, "ha SField const sfIndex = make::one(&sfIndex, STI_HASH256, 258, "index"); // 8-bit integers -SField const sfCloseResolution = make::one(&sfCloseResolution, STI_UINT8, 1, "CloseResolution"); -SField const sfTemplateEntryType = make::one(&sfTemplateEntryType, STI_UINT8, 2, "TemplateEntryType"); -SField const sfTransactionResult = make::one(&sfTransactionResult, STI_UINT8, 3, "TransactionResult"); +SF_U8 const sfCloseResolution = make::one(&sfCloseResolution, STI_UINT8, 1, "CloseResolution"); +SF_U8 const sfTemplateEntryType = make::one(&sfTemplateEntryType, STI_UINT8, 2, "TemplateEntryType"); +SF_U8 const sfTransactionResult = make::one(&sfTransactionResult, STI_UINT8, 3, "TransactionResult"); // 16-bit integers -SField const sfLedgerEntryType = make::one(&sfLedgerEntryType, STI_UINT16, 1, "LedgerEntryType", SField::sMD_Never); -SField const sfTransactionType = make::one(&sfTransactionType, STI_UINT16, 2, "TransactionType"); -SField const sfSignerWeight = make::one(&sfSignerWeight, STI_UINT16, 3, "SignerWeight"); +SF_U16 const sfLedgerEntryType = make::one(&sfLedgerEntryType, STI_UINT16, 1, "LedgerEntryType", SField::sMD_Never); +SF_U16 const sfTransactionType = make::one(&sfTransactionType, STI_UINT16, 2, "TransactionType"); +SF_U16 const sfSignerWeight = make::one(&sfSignerWeight, STI_UINT16, 3, "SignerWeight"); // 32-bit integers (common) -SField const sfFlags = make::one(&sfFlags, STI_UINT32, 2, "Flags"); -SField const sfSourceTag = make::one(&sfSourceTag, STI_UINT32, 3, "SourceTag"); -TypedField> const sfSequence = make::one>(&sfSequence, STI_UINT32, 4, "Sequence"); -SField const sfPreviousTxnLgrSeq = make::one(&sfPreviousTxnLgrSeq, STI_UINT32, 5, "PreviousTxnLgrSeq", SField::sMD_DeleteFinal); -SField const sfLedgerSequence = make::one(&sfLedgerSequence, STI_UINT32, 6, "LedgerSequence"); -SField const sfCloseTime = make::one(&sfCloseTime, STI_UINT32, 7, "CloseTime"); -SField const sfParentCloseTime = make::one(&sfParentCloseTime, STI_UINT32, 8, "ParentCloseTime"); -SField const sfSigningTime = make::one(&sfSigningTime, STI_UINT32, 9, "SigningTime"); -SField const sfExpiration = make::one(&sfExpiration, STI_UINT32, 10, "Expiration"); -SField const sfTransferRate = make::one(&sfTransferRate, STI_UINT32, 11, "TransferRate"); -SField const sfWalletSize = make::one(&sfWalletSize, STI_UINT32, 12, "WalletSize"); -SField const sfOwnerCount = make::one(&sfOwnerCount, STI_UINT32, 13, "OwnerCount"); -SField const sfDestinationTag = make::one(&sfDestinationTag, STI_UINT32, 14, "DestinationTag"); +SF_U32 const sfFlags = make::one(&sfFlags, STI_UINT32, 2, "Flags"); +SF_U32 const sfSourceTag = make::one(&sfSourceTag, STI_UINT32, 3, "SourceTag"); +SF_U32 const sfSequence = make::one(&sfSequence, STI_UINT32, 4, "Sequence"); +SF_U32 const sfPreviousTxnLgrSeq = make::one(&sfPreviousTxnLgrSeq, STI_UINT32, 5, "PreviousTxnLgrSeq", SField::sMD_DeleteFinal); +SF_U32 const sfLedgerSequence = make::one(&sfLedgerSequence, STI_UINT32, 6, "LedgerSequence"); +SF_U32 const sfCloseTime = make::one(&sfCloseTime, STI_UINT32, 7, "CloseTime"); +SF_U32 const sfParentCloseTime = make::one(&sfParentCloseTime, STI_UINT32, 8, "ParentCloseTime"); +SF_U32 const sfSigningTime = make::one(&sfSigningTime, STI_UINT32, 9, "SigningTime"); +SF_U32 const sfExpiration = make::one(&sfExpiration, STI_UINT32, 10, "Expiration"); +SF_U32 const sfTransferRate = make::one(&sfTransferRate, STI_UINT32, 11, "TransferRate"); +SF_U32 const sfWalletSize = make::one(&sfWalletSize, STI_UINT32, 12, "WalletSize"); +SF_U32 const sfOwnerCount = make::one(&sfOwnerCount, STI_UINT32, 13, "OwnerCount"); +SF_U32 const sfDestinationTag = make::one(&sfDestinationTag, STI_UINT32, 14, "DestinationTag"); // 32-bit integers (uncommon) -SField const sfHighQualityIn = make::one(&sfHighQualityIn, STI_UINT32, 16, "HighQualityIn"); -SField const sfHighQualityOut = make::one(&sfHighQualityOut, STI_UINT32, 17, "HighQualityOut"); -SField const sfLowQualityIn = make::one(&sfLowQualityIn, STI_UINT32, 18, "LowQualityIn"); -SField const sfLowQualityOut = make::one(&sfLowQualityOut, STI_UINT32, 19, "LowQualityOut"); -SField const sfQualityIn = make::one(&sfQualityIn, STI_UINT32, 20, "QualityIn"); -SField const sfQualityOut = make::one(&sfQualityOut, STI_UINT32, 21, "QualityOut"); -SField const sfStampEscrow = make::one(&sfStampEscrow, STI_UINT32, 22, "StampEscrow"); -SField const sfBondAmount = make::one(&sfBondAmount, STI_UINT32, 23, "BondAmount"); -SField const sfLoadFee = make::one(&sfLoadFee, STI_UINT32, 24, "LoadFee"); -SField const sfOfferSequence = make::one(&sfOfferSequence, STI_UINT32, 25, "OfferSequence"); -SField const sfFirstLedgerSequence = make::one(&sfFirstLedgerSequence, STI_UINT32, 26, "FirstLedgerSequence"); // Deprecated: do not use -SField const sfLastLedgerSequence = make::one(&sfLastLedgerSequence, STI_UINT32, 27, "LastLedgerSequence"); -SField const sfTransactionIndex = make::one(&sfTransactionIndex, STI_UINT32, 28, "TransactionIndex"); -SField const sfOperationLimit = make::one(&sfOperationLimit, STI_UINT32, 29, "OperationLimit"); -SField const sfReferenceFeeUnits = make::one(&sfReferenceFeeUnits, STI_UINT32, 30, "ReferenceFeeUnits"); -SField const sfReserveBase = make::one(&sfReserveBase, STI_UINT32, 31, "ReserveBase"); -SField const sfReserveIncrement = make::one(&sfReserveIncrement, STI_UINT32, 32, "ReserveIncrement"); -SField const sfSetFlag = make::one(&sfSetFlag, STI_UINT32, 33, "SetFlag"); -SField const sfClearFlag = make::one(&sfClearFlag, STI_UINT32, 34, "ClearFlag"); -SField const sfSignerQuorum = make::one(&sfSignerQuorum, STI_UINT32, 35, "SignerQuorum"); +SF_U32 const sfHighQualityIn = make::one(&sfHighQualityIn, STI_UINT32, 16, "HighQualityIn"); +SF_U32 const sfHighQualityOut = make::one(&sfHighQualityOut, STI_UINT32, 17, "HighQualityOut"); +SF_U32 const sfLowQualityIn = make::one(&sfLowQualityIn, STI_UINT32, 18, "LowQualityIn"); +SF_U32 const sfLowQualityOut = make::one(&sfLowQualityOut, STI_UINT32, 19, "LowQualityOut"); +SF_U32 const sfQualityIn = make::one(&sfQualityIn, STI_UINT32, 20, "QualityIn"); +SF_U32 const sfQualityOut = make::one(&sfQualityOut, STI_UINT32, 21, "QualityOut"); +SF_U32 const sfStampEscrow = make::one(&sfStampEscrow, STI_UINT32, 22, "StampEscrow"); +SF_U32 const sfBondAmount = make::one(&sfBondAmount, STI_UINT32, 23, "BondAmount"); +SF_U32 const sfLoadFee = make::one(&sfLoadFee, STI_UINT32, 24, "LoadFee"); +SF_U32 const sfOfferSequence = make::one(&sfOfferSequence, STI_UINT32, 25, "OfferSequence"); +SF_U32 const sfFirstLedgerSequence = make::one(&sfFirstLedgerSequence, STI_UINT32, 26, "FirstLedgerSequence"); // Deprecated: do not use +SF_U32 const sfLastLedgerSequence = make::one(&sfLastLedgerSequence, STI_UINT32, 27, "LastLedgerSequence"); +SF_U32 const sfTransactionIndex = make::one(&sfTransactionIndex, STI_UINT32, 28, "TransactionIndex"); +SF_U32 const sfOperationLimit = make::one(&sfOperationLimit, STI_UINT32, 29, "OperationLimit"); +SF_U32 const sfReferenceFeeUnits = make::one(&sfReferenceFeeUnits, STI_UINT32, 30, "ReferenceFeeUnits"); +SF_U32 const sfReserveBase = make::one(&sfReserveBase, STI_UINT32, 31, "ReserveBase"); +SF_U32 const sfReserveIncrement = make::one(&sfReserveIncrement, STI_UINT32, 32, "ReserveIncrement"); +SF_U32 const sfSetFlag = make::one(&sfSetFlag, STI_UINT32, 33, "SetFlag"); +SF_U32 const sfClearFlag = make::one(&sfClearFlag, STI_UINT32, 34, "ClearFlag"); +SF_U32 const sfSignerQuorum = make::one(&sfSignerQuorum, STI_UINT32, 35, "SignerQuorum"); // 64-bit integers -SField const sfIndexNext = make::one(&sfIndexNext, STI_UINT64, 1, "IndexNext"); -SField const sfIndexPrevious = make::one(&sfIndexPrevious, STI_UINT64, 2, "IndexPrevious"); -SField const sfBookNode = make::one(&sfBookNode, STI_UINT64, 3, "BookNode"); -SField const sfOwnerNode = make::one(&sfOwnerNode, STI_UINT64, 4, "OwnerNode"); -SField const sfBaseFee = make::one(&sfBaseFee, STI_UINT64, 5, "BaseFee"); -SField const sfExchangeRate = make::one(&sfExchangeRate, STI_UINT64, 6, "ExchangeRate"); -SField const sfLowNode = make::one(&sfLowNode, STI_UINT64, 7, "LowNode"); -SField const sfHighNode = make::one(&sfHighNode, STI_UINT64, 8, "HighNode"); +SF_U64 const sfIndexNext = make::one(&sfIndexNext, STI_UINT64, 1, "IndexNext"); +SF_U64 const sfIndexPrevious = make::one(&sfIndexPrevious, STI_UINT64, 2, "IndexPrevious"); +SF_U64 const sfBookNode = make::one(&sfBookNode, STI_UINT64, 3, "BookNode"); +SF_U64 const sfOwnerNode = make::one(&sfOwnerNode, STI_UINT64, 4, "OwnerNode"); +SF_U64 const sfBaseFee = make::one(&sfBaseFee, STI_UINT64, 5, "BaseFee"); +SF_U64 const sfExchangeRate = make::one(&sfExchangeRate, STI_UINT64, 6, "ExchangeRate"); +SF_U64 const sfLowNode = make::one(&sfLowNode, STI_UINT64, 7, "LowNode"); +SF_U64 const sfHighNode = make::one(&sfHighNode, STI_UINT64, 8, "HighNode"); // 128-bit -SField const sfEmailHash = make::one(&sfEmailHash, STI_HASH128, 1, "EmailHash"); - -// 256-bit (common) -SField const sfLedgerHash = make::one(&sfLedgerHash, STI_HASH256, 1, "LedgerHash"); -SField const sfParentHash = make::one(&sfParentHash, STI_HASH256, 2, "ParentHash"); -SField const sfTransactionHash = make::one(&sfTransactionHash, STI_HASH256, 3, "TransactionHash"); -SField const sfAccountHash = make::one(&sfAccountHash, STI_HASH256, 4, "AccountHash"); -SField const sfPreviousTxnID = make::one(&sfPreviousTxnID, STI_HASH256, 5, "PreviousTxnID", SField::sMD_DeleteFinal); -SField const sfLedgerIndex = make::one(&sfLedgerIndex, STI_HASH256, 6, "LedgerIndex"); -SField const sfWalletLocator = make::one(&sfWalletLocator, STI_HASH256, 7, "WalletLocator"); -SField const sfRootIndex = make::one(&sfRootIndex, STI_HASH256, 8, "RootIndex", SField::sMD_Always); -SField const sfAccountTxnID = make::one(&sfAccountTxnID, STI_HASH256, 9, "AccountTxnID"); - -// 256-bit (uncommon) -SField const sfBookDirectory = make::one(&sfBookDirectory, STI_HASH256, 16, "BookDirectory"); -SField const sfInvoiceID = make::one(&sfInvoiceID, STI_HASH256, 17, "InvoiceID"); -SField const sfNickname = make::one(&sfNickname, STI_HASH256, 18, "Nickname"); -SField const sfAmendment = make::one(&sfAmendment, STI_HASH256, 19, "Amendment"); -SField const sfTicketID = make::one(&sfTicketID, STI_HASH256, 20, "TicketID"); +SF_U128 const sfEmailHash = make::one(&sfEmailHash, STI_HASH128, 1, "EmailHash"); // 160-bit (common) -SField const sfTakerPaysCurrency = make::one(&sfTakerPaysCurrency, STI_HASH160, 1, "TakerPaysCurrency"); -SField const sfTakerPaysIssuer = make::one(&sfTakerPaysIssuer, STI_HASH160, 2, "TakerPaysIssuer"); -SField const sfTakerGetsCurrency = make::one(&sfTakerGetsCurrency, STI_HASH160, 3, "TakerGetsCurrency"); -SField const sfTakerGetsIssuer = make::one(&sfTakerGetsIssuer, STI_HASH160, 4, "TakerGetsIssuer"); +SF_U160 const sfTakerPaysCurrency = make::one(&sfTakerPaysCurrency, STI_HASH160, 1, "TakerPaysCurrency"); +SF_U160 const sfTakerPaysIssuer = make::one(&sfTakerPaysIssuer, STI_HASH160, 2, "TakerPaysIssuer"); +SF_U160 const sfTakerGetsCurrency = make::one(&sfTakerGetsCurrency, STI_HASH160, 3, "TakerGetsCurrency"); +SF_U160 const sfTakerGetsIssuer = make::one(&sfTakerGetsIssuer, STI_HASH160, 4, "TakerGetsIssuer"); + +// 256-bit (common) +SF_U256 const sfLedgerHash = make::one(&sfLedgerHash, STI_HASH256, 1, "LedgerHash"); +SF_U256 const sfParentHash = make::one(&sfParentHash, STI_HASH256, 2, "ParentHash"); +SF_U256 const sfTransactionHash = make::one(&sfTransactionHash, STI_HASH256, 3, "TransactionHash"); +SF_U256 const sfAccountHash = make::one(&sfAccountHash, STI_HASH256, 4, "AccountHash"); +SF_U256 const sfPreviousTxnID = make::one(&sfPreviousTxnID, STI_HASH256, 5, "PreviousTxnID", SField::sMD_DeleteFinal); +SF_U256 const sfLedgerIndex = make::one(&sfLedgerIndex, STI_HASH256, 6, "LedgerIndex"); +SF_U256 const sfWalletLocator = make::one(&sfWalletLocator, STI_HASH256, 7, "WalletLocator"); +SF_U256 const sfRootIndex = make::one(&sfRootIndex, STI_HASH256, 8, "RootIndex", SField::sMD_Always); +SF_U256 const sfAccountTxnID = make::one(&sfAccountTxnID, STI_HASH256, 9, "AccountTxnID"); + +// 256-bit (uncommon) +SF_U256 const sfBookDirectory = make::one(&sfBookDirectory, STI_HASH256, 16, "BookDirectory"); +SF_U256 const sfInvoiceID = make::one(&sfInvoiceID, STI_HASH256, 17, "InvoiceID"); +SF_U256 const sfNickname = make::one(&sfNickname, STI_HASH256, 18, "Nickname"); +SF_U256 const sfAmendment = make::one(&sfAmendment, STI_HASH256, 19, "Amendment"); +SF_U256 const sfTicketID = make::one(&sfTicketID, STI_HASH256, 20, "TicketID"); +SF_U256 const sfDigest = make::one(&sfDigest, STI_HASH256, 21, "Digest"); // currency amount (common) -SField const sfAmount = make::one(&sfAmount, STI_AMOUNT, 1, "Amount"); -SField const sfBalance = make::one(&sfBalance, STI_AMOUNT, 2, "Balance"); -SField const sfLimitAmount = make::one(&sfLimitAmount, STI_AMOUNT, 3, "LimitAmount"); -SField const sfTakerPays = make::one(&sfTakerPays, STI_AMOUNT, 4, "TakerPays"); -SField const sfTakerGets = make::one(&sfTakerGets, STI_AMOUNT, 5, "TakerGets"); -SField const sfLowLimit = make::one(&sfLowLimit, STI_AMOUNT, 6, "LowLimit"); -SField const sfHighLimit = make::one(&sfHighLimit, STI_AMOUNT, 7, "HighLimit"); -SField const sfFee = make::one(&sfFee, STI_AMOUNT, 8, "Fee"); -SField const sfSendMax = make::one(&sfSendMax, STI_AMOUNT, 9, "SendMax"); -SField const sfDeliverMin = make::one(&sfDeliverMin, STI_AMOUNT, 10, "DeliverMin"); +SF_Amount const sfAmount = make::one(&sfAmount, STI_AMOUNT, 1, "Amount"); +SF_Amount const sfBalance = make::one(&sfBalance, STI_AMOUNT, 2, "Balance"); +SF_Amount const sfLimitAmount = make::one(&sfLimitAmount, STI_AMOUNT, 3, "LimitAmount"); +SF_Amount const sfTakerPays = make::one(&sfTakerPays, STI_AMOUNT, 4, "TakerPays"); +SF_Amount const sfTakerGets = make::one(&sfTakerGets, STI_AMOUNT, 5, "TakerGets"); +SF_Amount const sfLowLimit = make::one(&sfLowLimit, STI_AMOUNT, 6, "LowLimit"); +SF_Amount const sfHighLimit = make::one(&sfHighLimit, STI_AMOUNT, 7, "HighLimit"); +SF_Amount const sfFee = make::one(&sfFee, STI_AMOUNT, 8, "Fee"); +SF_Amount const sfSendMax = make::one(&sfSendMax, STI_AMOUNT, 9, "SendMax"); +SF_Amount const sfDeliverMin = make::one(&sfDeliverMin, STI_AMOUNT, 10, "DeliverMin"); // currency amount (uncommon) -SField const sfMinimumOffer = make::one(&sfMinimumOffer, STI_AMOUNT, 16, "MinimumOffer"); -SField const sfRippleEscrow = make::one(&sfRippleEscrow, STI_AMOUNT, 17, "RippleEscrow"); -SField const sfDeliveredAmount = make::one(&sfDeliveredAmount, STI_AMOUNT, 18, "DeliveredAmount"); +SF_Amount const sfMinimumOffer = make::one(&sfMinimumOffer, STI_AMOUNT, 16, "MinimumOffer"); +SF_Amount const sfRippleEscrow = make::one(&sfRippleEscrow, STI_AMOUNT, 17, "RippleEscrow"); +SF_Amount const sfDeliveredAmount = make::one(&sfDeliveredAmount, STI_AMOUNT, 18, "DeliveredAmount"); // variable length (common) -TypedField const sfPublicKey = make::one(&sfPublicKey, STI_VL, 1, "PublicKey"); -TypedField const sfSigningPubKey = make::one(&sfSigningPubKey, STI_VL, 3, "SigningPubKey"); -TypedField const sfSignature = make::one(&sfSignature, STI_VL, 6, "Signature", SField::sMD_Default, SField::notSigning); -SField const sfMessageKey = make::one(&sfMessageKey, STI_VL, 2, "MessageKey"); -SField const sfTxnSignature = make::one(&sfTxnSignature, STI_VL, 4, "TxnSignature", SField::sMD_Default, SField::notSigning); -SField const sfDomain = make::one(&sfDomain, STI_VL, 7, "Domain"); -SField const sfFundCode = make::one(&sfFundCode, STI_VL, 8, "FundCode"); -SField const sfRemoveCode = make::one(&sfRemoveCode, STI_VL, 9, "RemoveCode"); -SField const sfExpireCode = make::one(&sfExpireCode, STI_VL, 10, "ExpireCode"); -SField const sfCreateCode = make::one(&sfCreateCode, STI_VL, 11, "CreateCode"); -SField const sfMemoType = make::one(&sfMemoType, STI_VL, 12, "MemoType"); -SField const sfMemoData = make::one(&sfMemoData, STI_VL, 13, "MemoData"); -SField const sfMemoFormat = make::one(&sfMemoFormat, STI_VL, 14, "MemoFormat"); +SF_Blob const sfPublicKey = make::one(&sfPublicKey, STI_VL, 1, "PublicKey"); +SF_Blob const sfSigningPubKey = make::one(&sfSigningPubKey, STI_VL, 3, "SigningPubKey"); +SF_Blob const sfSignature = make::one(&sfSignature, STI_VL, 6, "Signature", SField::sMD_Default, SField::notSigning); +SF_Blob const sfMessageKey = make::one(&sfMessageKey, STI_VL, 2, "MessageKey"); +SF_Blob const sfTxnSignature = make::one(&sfTxnSignature, STI_VL, 4, "TxnSignature", SField::sMD_Default, SField::notSigning); +SF_Blob const sfDomain = make::one(&sfDomain, STI_VL, 7, "Domain"); +SF_Blob const sfFundCode = make::one(&sfFundCode, STI_VL, 8, "FundCode"); +SF_Blob const sfRemoveCode = make::one(&sfRemoveCode, STI_VL, 9, "RemoveCode"); +SF_Blob const sfExpireCode = make::one(&sfExpireCode, STI_VL, 10, "ExpireCode"); +SF_Blob const sfCreateCode = make::one(&sfCreateCode, STI_VL, 11, "CreateCode"); +SF_Blob const sfMemoType = make::one(&sfMemoType, STI_VL, 12, "MemoType"); +SF_Blob const sfMemoData = make::one(&sfMemoData, STI_VL, 13, "MemoData"); +SF_Blob const sfMemoFormat = make::one(&sfMemoFormat, STI_VL, 14, "MemoFormat"); // variable length (uncommon) -SField const sfMultiSignature = make::one(&sfMultiSignature, STI_VL, 16, "MultiSignature"); +SF_Blob const sfMultiSignature = make::one(&sfMultiSignature, STI_VL, 16, "MultiSignature"); +SF_Blob const sfInnerSig = make::one(&sfInnerSig, STI_VL, 17, "InnerSig"); // account -SField const sfAccount = make::one(&sfAccount, STI_ACCOUNT, 1, "Account"); -SField const sfOwner = make::one(&sfOwner, STI_ACCOUNT, 2, "Owner"); -SField const sfDestination = make::one(&sfDestination, STI_ACCOUNT, 3, "Destination"); -SField const sfIssuer = make::one(&sfIssuer, STI_ACCOUNT, 4, "Issuer"); -SField const sfTarget = make::one(&sfTarget, STI_ACCOUNT, 7, "Target"); -SField const sfRegularKey = make::one(&sfRegularKey, STI_ACCOUNT, 8, "RegularKey"); +SF_Account const sfAccount = make::one(&sfAccount, STI_ACCOUNT, 1, "Account"); +SF_Account const sfOwner = make::one(&sfOwner, STI_ACCOUNT, 2, "Owner"); +SF_Account const sfDestination = make::one(&sfDestination, STI_ACCOUNT, 3, "Destination"); +SF_Account const sfIssuer = make::one(&sfIssuer, STI_ACCOUNT, 4, "Issuer"); +SF_Account const sfTarget = make::one(&sfTarget, STI_ACCOUNT, 7, "Target"); +SF_Account const sfRegularKey = make::one(&sfRegularKey, STI_ACCOUNT, 8, "RegularKey"); // path set SField const sfPaths = make::one(&sfPaths, STI_PATHSET, 1, "Paths"); // vector of 256-bit -SField const sfIndexes = make::one(&sfIndexes, STI_VECTOR256, 1, "Indexes", SField::sMD_Never); -SField const sfHashes = make::one(&sfHashes, STI_VECTOR256, 2, "Hashes"); -SField const sfAmendments = make::one(&sfAmendments, STI_VECTOR256, 3, "Amendments"); +SF_Vec256 const sfIndexes = make::one(&sfIndexes, STI_VECTOR256, 1, "Indexes", SField::sMD_Never); +SF_Vec256 const sfHashes = make::one(&sfHashes, STI_VECTOR256, 2, "Hashes"); +SF_Vec256 const sfAmendments = make::one(&sfAmendments, STI_VECTOR256, 3, "Amendments"); // inner object // OBJECT/1 is reserved for end of object diff --git a/src/ripple/protocol/tests/STObject.test.cpp b/src/ripple/protocol/tests/STObject.test.cpp index bc53d02ae2..3115d17059 100644 --- a/src/ripple/protocol/tests/STObject.test.cpp +++ b/src/ripple/protocol/tests/STObject.test.cpp @@ -19,28 +19,19 @@ #include #include -#include -#include -#include -#include -#include +#include +#include #include #include #include -#include // +#include +#include namespace ripple { -class SerializedObject_test : public beast::unit_test::suite +class STObject_test : public beast::unit_test::suite { public: - void run() - { - testSerialization(); - testParseJSONArray(); - testParseJSONArrayWithInvalidChildrenObjects(); - } - bool parseJSONString (std::string const& json, Json::Value& to) { Json::Reader reader; @@ -192,8 +183,307 @@ public: unexpected (object3.getFieldVL (sfTestVL) != j, "STObject error"); } } + + // Exercise field accessors + void + testFields() + { + testcase ("fields"); + + auto const& sf1 = sfSequence; + auto const& sf2 = sfExpiration; + auto const& sf3 = sfQualityIn; + auto const& sf4 = sfSignature; + auto const& sf5 = sfPublicKey; + + // read free object + + { + auto const st = [&]() + { + STObject st(sfGeneric); + st.setFieldU32(sf1, 1); + st.setFieldU32(sf2, 2); + return st; + }(); + + expect(st[sf1] == 1); + expect(st[sf2] == 2); + except([&]() + { st[sf3]; }); + expect(*st[~sf1] == 1); + expect(*st[~sf2] == 2); + expect(st[~sf3] == boost::none); + expect(!! st[~sf1]); + expect(!! st[~sf2]); + expect(! st[~sf3]); + expect(st[sf1] != st[sf2]); + expect(st[~sf1] != st[~sf2]); + } + + // read templated object + + auto const sot = [&]() + { + SOTemplate sot; + sot.push_back(SOElement(sf1, SOE_REQUIRED)); + sot.push_back(SOElement(sf2, SOE_OPTIONAL)); + sot.push_back(SOElement(sf3, SOE_DEFAULT)); + sot.push_back(SOElement(sf4, SOE_OPTIONAL)); + sot.push_back(SOElement(sf5, SOE_DEFAULT)); + return sot; + }(); + + { + auto const st = [&]() + { + STObject st(sot, sfGeneric); + st.setFieldU32(sf1, 1); + st.setFieldU32(sf2, 2); + return st; + }(); + + expect(st[sf1] == 1); + expect(st[sf2] == 2); + expect(st[sf3] == 0); + expect(*st[~sf1] == 1); + expect(*st[~sf2] == 2); + expect(*st[~sf3] == 0); + expect(!! st[~sf1]); + expect(!! st[~sf2]); + expect(!! st[~sf3]); + } + + // write free object + + { + STObject st(sfGeneric); + unexcept([&]() { st[sf1]; }); + except([&](){ return st[sf1] == 0; }); + expect(st[~sf1] == boost::none); + expect(st[~sf1] == boost::optional{}); + expect(st[~sf1] != boost::optional(1)); + expect(! st[~sf1]); + st[sf1] = 2; + expect(st[sf1] == 2); + expect(st[~sf1] != boost::none); + expect(st[~sf1] == boost::optional(2)); + expect(!! st[~sf1]); + st[sf1] = 1; + expect(st[sf1] == 1); + expect(!! st[sf1]); + expect(!! st[~sf1]); + st[sf1] = 0; + expect(! st[sf1]); + expect(!! st[~sf1]); + st[~sf1] = boost::none; + expect(! st[~sf1]); + expect(st[~sf1] == boost::none); + expect(st[~sf1] == boost::optional{}); + st[~sf1] = boost::none; + expect(! st[~sf1]); + except([&]() { return st[sf1] == 0; }); + except([&]() { return *st[~sf1]; }); + st[sf1] = 1; + expect(st[sf1] == 1); + expect(!! st[sf1]); + expect(!! st[~sf1]); + st[sf1] = 3; + st[sf2] = st[sf1]; + expect(st[sf1] == 3); + expect(st[sf2] == 3); + expect(st[sf2] == st[sf1]); + st[sf1] = 4; + st[sf2] = st[sf1]; + expect(st[sf1] == 4); + expect(st[sf2] == 4); + expect(st[sf2] == st[sf1]); + } + + // Write templated object + + { + STObject st(sot, sfGeneric); + expect(!! st[~sf1]); + expect(st[~sf1] != boost::none); + expect(st[sf1] == 0); + expect(*st[~sf1] == 0); + expect(! st[~sf2]); + expect(st[~sf2] == boost::none); + except([&]() { return st[sf2] == 0; }); + expect(!! st[~sf3]); + expect(st[~sf3] != boost::none); + expect(st[sf3] == 0); + except([&]() { st[~sf1] = boost::none; }); + st[sf1] = 1; + expect(st[sf1] == 1); + expect(*st[~sf1] == 1); + expect(!! st[~sf1]); + st[sf1] = 0; + expect(st[sf1] == 0); + expect(*st[~sf1] == 0); + expect(!! st[~sf1]); + st[sf2] = 2; + expect(st[sf2] == 2); + expect(*st[~sf2] == 2); + expect(!! st[~sf2]); + st[~sf2] = boost::none; + except([&]() { return *st[~sf2]; }); + expect(! st[~sf2]); + st[sf3] = 3; + expect(st[sf3] == 3); + expect(*st[~sf3] == 3); + expect(!! st[~sf3]); + st[sf3] = 2; + expect(st[sf3] == 2); + expect(*st[~sf3] == 2); + expect(!! st[~sf3]); + st[sf3] = 0; + expect(st[sf3] == 0); + expect(*st[~sf3] == 0); + expect(!! st[~sf3]); + except([&]() { st[~sf3] = boost::none; }); + expect(st[sf3] == 0); + expect(*st[~sf3] == 0); + expect(!! st[~sf3]); + } + + // coercion operator to boost::optional + + { + STObject st(sfGeneric); + auto const v = ~st[~sf1]; + static_assert(std::is_same< + std::decay_t, + boost::optional>::value, ""); + } + + // UDT scalar fields + + { + STObject st(sfGeneric); + st[sfAmount] = STAmount{}; + st[sfAccount] = AccountID{}; + st[sfDigest] = uint256{}; + [&](STAmount){}(st[sfAmount]); + [&](AccountID){}(st[sfAccount]); + [&](uint256){}(st[sfDigest]); + } + + // STBlob and slice + + { + { + STObject st(sfGeneric); + Buffer b(1); + expect(! b.empty()); + st[sf4] = std::move(b); + expect(b.empty()); + expect(Slice(st[sf4]).size() == 1); + st[~sf4] = boost::none; + expect(! ~st[~sf4]); + b = Buffer{2}; + st[sf4] = Slice(b); + expect(b.size() == 2); + expect(Slice(st[sf4]).size() == 2); + st[sf5] = st[sf4]; + expect(Slice(st[sf4]).size() == 2); + expect(Slice(st[sf5]).size() == 2); + } + { + STObject st(sot, sfGeneric); + expect(st[sf5] == Slice{}); + expect(!! st[~sf5]); + expect(!! ~st[~sf5]); + Buffer b(1); + st[sf5] = std::move(b); + expect(b.empty()); + expect(Slice(st[sf5]).size() == 1); + st[~sf4] = boost::none; + expect(! ~st[~sf4]); + } + } + + // UDT blobs + + { + STObject st(sfGeneric); + expect(! st[~sf5]); + auto const kp = generateKeyPair( + KeyType::secp256k1, + generateSeed("masterpassphrase")); + st[sf5] = kp.first; + expect(st[sf5] != PublicKey{}); + st[~sf5] = boost::none; +#if 0 + pk = st[sf5]; + expect(pk.size() == 0); +#endif + } + + // By reference fields + + { + auto const& sf = sfIndexes; + STObject st(sfGeneric); + std::vector v; + v.emplace_back(1); + st[sf] = v; + st[sf] = std::move(v); + auto const& cst = st; + expect(cst[sf].size() == 1); + expect(cst[~sf]->size() == 1); + static_assert(std::is_same const&>::value, ""); + } + + // Default by reference field + + { + auto const& sf1 = sfIndexes; + auto const& sf2 = sfHashes; + auto const& sf3 = sfAmendments; + auto const sot = [&]() + { + SOTemplate sot; + sot.push_back(SOElement(sf1, SOE_REQUIRED)); + sot.push_back(SOElement(sf2, SOE_OPTIONAL)); + sot.push_back(SOElement(sf3, SOE_DEFAULT)); + return sot; + }(); + STObject st(sot, sfGeneric); + auto const& cst(st); + expect(cst[sf1].size() == 0); + expect(! cst[~sf2]); + expect(cst[sf3].size() == 0); + std::vector v; + v.emplace_back(1); + st[sf1] = v; + expect(cst[sf1].size() == 1); + expect(cst[sf1][0] == uint256{1}); + st[sf2] = v; + expect(cst[sf2].size() == 1); + expect(cst[sf2][0] == uint256{1}); + st[~sf2] = boost::none; + expect(! st[~sf2]); + st[sf3] = v; + expect(cst[sf3].size() == 1); + expect(cst[sf3][0] == uint256{1}); + st[sf3] = std::vector{}; + expect(cst[sf3].size() == 0); + } + } + + void + run() + { + testFields(); + testSerialization(); + testParseJSONArray(); + testParseJSONArrayWithInvalidChildrenObjects(); + } }; -BEAST_DEFINE_TESTSUITE(SerializedObject,ripple_data,ripple); +BEAST_DEFINE_TESTSUITE(STObject,protocol,ripple); } // ripple