mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
SignerListSet txn and InnerObjectFormats (RIPD-182):
Add support for the SignerListSet transaction as a step toward multi-sign support. As part of the SignerListSet implementation, add InnerObjectFormat templates (similar to TxFormats and LedgerFormats) and enforce them in STObject, STArray, and STParsedJSON.
This commit is contained in:
committed by
Vinnie Falco
parent
92799187ed
commit
64ebd64d2b
@@ -87,6 +87,8 @@ getRippleStateIndex (Account const& a, Account const& b, Currency const& currenc
|
||||
uint256
|
||||
getRippleStateIndex (Account const& a, Issue const& issue);
|
||||
|
||||
uint256
|
||||
getSignerListIndex (Account const& account);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
47
src/ripple/protocol/InnerObjectFormats.h
Executable file
47
src/ripple/protocol/InnerObjectFormats.h
Executable file
@@ -0,0 +1,47 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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_PROTOCOL_INNER_OBJECT_FORMATS_H_INCLUDED
|
||||
#define RIPPLE_PROTOCOL_INNER_OBJECT_FORMATS_H_INCLUDED
|
||||
|
||||
#include <ripple/protocol/KnownFormats.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Manages the list of known inner object formats.
|
||||
*/
|
||||
class InnerObjectFormats : public KnownFormats <int>
|
||||
{
|
||||
private:
|
||||
void addCommonFields (Item& item);
|
||||
|
||||
public:
|
||||
/** Create the object.
|
||||
This will load the object will all the known inner object formats.
|
||||
*/
|
||||
InnerObjectFormats ();
|
||||
|
||||
static InnerObjectFormats const& getInstance ();
|
||||
|
||||
SOTemplate const* findSOTemplateBySField (SField const& sField) const;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
@@ -56,6 +56,8 @@ enum LedgerEntryType
|
||||
|
||||
ltTICKET = 'T',
|
||||
|
||||
ltSIGNER_LIST = 'S',
|
||||
|
||||
/* Deprecated. */
|
||||
ltOFFER = 'o',
|
||||
|
||||
@@ -90,6 +92,7 @@ enum LedgerNameSpace
|
||||
spaceAmendment = 'f',
|
||||
spaceFee = 'e',
|
||||
spaceTicket = 'T',
|
||||
spaceSignerList = 'S',
|
||||
|
||||
// No longer used or supported. Left here to reserve the space and
|
||||
// avoid accidental reuse of the space.
|
||||
|
||||
@@ -295,6 +295,7 @@ extern SField const sfTransactionResult;
|
||||
// 16-bit integers
|
||||
extern SField const sfLedgerEntryType;
|
||||
extern SField const sfTransactionType;
|
||||
extern SField const sfSignerWeight;
|
||||
|
||||
// 32-bit integers (common)
|
||||
extern SField const sfFlags;
|
||||
@@ -331,6 +332,7 @@ extern SField const sfReserveBase;
|
||||
extern SField const sfReserveIncrement;
|
||||
extern SField const sfSetFlag;
|
||||
extern SField const sfClearFlag;
|
||||
extern SField const sfSignerQuorum;
|
||||
|
||||
// 64-bit integers
|
||||
extern SField const sfIndexNext;
|
||||
@@ -427,12 +429,13 @@ extern SField const sfFinalFields;
|
||||
extern SField const sfNewFields;
|
||||
extern SField const sfTemplateEntry;
|
||||
extern SField const sfMemo;
|
||||
extern SField const sfSignerEntry;
|
||||
|
||||
// array of objects
|
||||
// ARRAY/1 is reserved for end of array
|
||||
extern SField const sfSigningAccounts;
|
||||
extern SField const sfTxnSignatures;
|
||||
extern SField const sfSignatures;
|
||||
extern SField const sfSignerEntries;
|
||||
extern SField const sfTemplate;
|
||||
extern SField const sfNecessary;
|
||||
extern SField const sfSufficient;
|
||||
|
||||
@@ -40,7 +40,7 @@ private:
|
||||
list_type v_;
|
||||
|
||||
public:
|
||||
// Read-only iteration
|
||||
// Read-only iteration
|
||||
class Items;
|
||||
|
||||
static char const* getCountedObjectName () { return "STArray"; }
|
||||
@@ -135,6 +135,10 @@ public:
|
||||
{
|
||||
v_.clear ();
|
||||
}
|
||||
void reserve (std::size_t n)
|
||||
{
|
||||
v_.reserve (n);
|
||||
}
|
||||
void swap (STArray & a) noexcept
|
||||
{
|
||||
v_.swap (a.v_);
|
||||
|
||||
@@ -133,7 +133,18 @@ public:
|
||||
return v_.empty();
|
||||
}
|
||||
|
||||
void reserve (std::size_t n)
|
||||
{
|
||||
v_.reserve (n);
|
||||
}
|
||||
|
||||
bool setType (const SOTemplate & type);
|
||||
|
||||
enum ResultOfSetTypeFromSField : unsigned char
|
||||
{typeSetFail, typeIsSet, noTemplate};
|
||||
|
||||
ResultOfSetTypeFromSField setTypeFromSField (SField const&);
|
||||
|
||||
bool isValidForType ();
|
||||
bool isFieldAllowed (SField const&);
|
||||
bool isFree () const
|
||||
|
||||
@@ -44,6 +44,8 @@ public:
|
||||
typedef std::shared_ptr<STTx> pointer;
|
||||
typedef const std::shared_ptr<STTx>& ref;
|
||||
|
||||
static std::size_t const maxMultiSigners = 8;
|
||||
|
||||
public:
|
||||
STTx () = delete;
|
||||
STTx& operator= (STTx const& other) = delete;
|
||||
|
||||
@@ -81,6 +81,8 @@ enum TER // aka TransactionEngineResult
|
||||
temREDUNDANT,
|
||||
temRIPPLE_EMPTY,
|
||||
temDISABLED,
|
||||
temBAD_SIGNER,
|
||||
temBAD_QUORUM,
|
||||
|
||||
// An intermediate result used internally, should never be returned.
|
||||
temUNCERTAIN,
|
||||
|
||||
@@ -46,6 +46,7 @@ enum TxType
|
||||
no_longer_used = 9,
|
||||
ttTICKET_CREATE = 10,
|
||||
ttTICKET_CANCEL = 11,
|
||||
ttSIGNER_LIST_SET = 12,
|
||||
|
||||
ttTRUST_SET = 20,
|
||||
|
||||
|
||||
@@ -219,4 +219,15 @@ getRippleStateIndex (Account const& a, Issue const& issue)
|
||||
return getRippleStateIndex (a, issue.account, issue.currency);
|
||||
}
|
||||
|
||||
uint256
|
||||
getSignerListIndex (Account const& account)
|
||||
{
|
||||
Serializer s (22);
|
||||
|
||||
s.add16 (spaceSignerList); // 2
|
||||
s.add160 (account); // 20
|
||||
|
||||
return s.getSHA512Half ();
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
55
src/ripple/protocol/impl/InnerObjectFormats.cpp
Executable file
55
src/ripple/protocol/impl/InnerObjectFormats.cpp
Executable file
@@ -0,0 +1,55 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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 <BeastConfig.h>
|
||||
#include <ripple/protocol/InnerObjectFormats.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
InnerObjectFormats::InnerObjectFormats ()
|
||||
{
|
||||
add (sfSignerEntry.getJsonName ().c_str (), sfSignerEntry.getCode ())
|
||||
<< SOElement (sfAccount, SOE_REQUIRED)
|
||||
<< SOElement (sfSignerWeight, SOE_REQUIRED)
|
||||
;
|
||||
}
|
||||
|
||||
void InnerObjectFormats::addCommonFields (Item& item)
|
||||
{
|
||||
}
|
||||
|
||||
InnerObjectFormats const&
|
||||
InnerObjectFormats::getInstance ()
|
||||
{
|
||||
static InnerObjectFormats instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
SOTemplate const*
|
||||
InnerObjectFormats::findSOTemplateBySField (SField const& sField) const
|
||||
{
|
||||
SOTemplate const* ret = nullptr;
|
||||
auto itemPtr = findByType (sField.getCode ());
|
||||
if (itemPtr)
|
||||
ret = &(itemPtr->elements);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
@@ -105,6 +105,14 @@ LedgerFormats::LedgerFormats ()
|
||||
<< SOElement (sfTarget, SOE_OPTIONAL)
|
||||
<< SOElement (sfExpiration, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
// All three fields are SOE_REQUIRED because there is always a
|
||||
// SignerEntries. If there are no SignerEntries the node is deleted.
|
||||
add ("SignerList", ltSIGNER_LIST)
|
||||
<< SOElement (sfOwnerNode, SOE_REQUIRED)
|
||||
<< SOElement (sfSignerQuorum, SOE_REQUIRED)
|
||||
<< SOElement (sfSignerEntries, SOE_REQUIRED)
|
||||
;
|
||||
}
|
||||
|
||||
void LedgerFormats::addCommonFields (Item& item)
|
||||
|
||||
@@ -83,6 +83,7 @@ SField const sfTransactionResult = make::one(&sfTransactionResult, STI_UINT8, 3,
|
||||
// 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");
|
||||
|
||||
// 32-bit integers (common)
|
||||
SField const sfFlags = make::one(&sfFlags, STI_UINT32, 2, "Flags");
|
||||
@@ -119,6 +120,7 @@ SField const sfReserveBase = make::one(&sfReserveBase, STI_UINT3
|
||||
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");
|
||||
|
||||
// 64-bit integers
|
||||
SField const sfIndexNext = make::one(&sfIndexNext, STI_UINT64, 1, "IndexNext");
|
||||
@@ -215,12 +217,13 @@ SField const sfFinalFields = make::one(&sfFinalFields, STI_OBJEC
|
||||
SField const sfNewFields = make::one(&sfNewFields, STI_OBJECT, 8, "NewFields");
|
||||
SField const sfTemplateEntry = make::one(&sfTemplateEntry, STI_OBJECT, 9, "TemplateEntry");
|
||||
SField const sfMemo = make::one(&sfMemo, STI_OBJECT, 10, "Memo");
|
||||
SField const sfSignerEntry = make::one(&sfSignerEntry, STI_OBJECT, 11, "SignerEntry");
|
||||
|
||||
// array of objects
|
||||
// ARRAY/1 is reserved for end of array
|
||||
SField const sfSigningAccounts = make::one(&sfSigningAccounts, STI_ARRAY, 2, "SigningAccounts");
|
||||
SField const sfTxnSignatures = make::one(&sfTxnSignatures, STI_ARRAY, 3, "TxnSignatures", SField::sMD_Default, SField::notSigning);
|
||||
SField const sfSignatures = make::one(&sfSignatures, STI_ARRAY, 4, "Signatures");
|
||||
SField const sfSignerEntries = make::one(&sfSignerEntries, STI_ARRAY, 4, "SignerEntries");
|
||||
SField const sfTemplate = make::one(&sfTemplate, STI_ARRAY, 5, "Template");
|
||||
SField const sfNecessary = make::one(&sfNecessary, STI_ARRAY, 6, "Necessary");
|
||||
SField const sfSufficient = make::one(&sfSufficient, STI_ARRAY, 7, "Sufficient");
|
||||
|
||||
@@ -97,6 +97,11 @@ STArray::STArray (SerialIter& sit, SField const& f)
|
||||
|
||||
v_.emplace_back(fn);
|
||||
v_.back().set (sit, 1);
|
||||
|
||||
if (v_.back().setTypeFromSField (fn) == STObject::typeSetFail)
|
||||
{
|
||||
throw std::runtime_error ("Malformed object in array");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/json/json_reader.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <ripple/protocol/InnerObjectFormats.h>
|
||||
#include <ripple/protocol/STBase.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
#include <ripple/protocol/STArray.h>
|
||||
@@ -119,7 +120,7 @@ bool STObject::setType (const SOTemplate& type)
|
||||
{
|
||||
WriteLog (lsWARNING, STObject) <<
|
||||
"setType( " << getFName ().getName () <<
|
||||
") invalid default " << e->e_field.fieldName;
|
||||
" ) invalid default " << e->e_field.fieldName;
|
||||
valid = false;
|
||||
}
|
||||
v.emplace_back(std::move(*iter));
|
||||
@@ -131,7 +132,7 @@ bool STObject::setType (const SOTemplate& type)
|
||||
{
|
||||
WriteLog (lsWARNING, STObject) <<
|
||||
"setType( " << getFName ().getName () <<
|
||||
") invalid missing " << e->e_field.fieldName;
|
||||
" ) invalid missing " << e->e_field.fieldName;
|
||||
valid = false;
|
||||
}
|
||||
v.emplace_back(detail::nonPresentObject, e->e_field);
|
||||
@@ -144,7 +145,7 @@ bool STObject::setType (const SOTemplate& type)
|
||||
{
|
||||
WriteLog (lsWARNING, STObject) <<
|
||||
"setType( " << getFName ().getName () <<
|
||||
") invalid leftover " << e->getFName ().getName ();
|
||||
" ) invalid leftover " << e->getFName ().getName ();
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
@@ -154,6 +155,20 @@ bool STObject::setType (const SOTemplate& type)
|
||||
return valid;
|
||||
}
|
||||
|
||||
STObject::ResultOfSetTypeFromSField
|
||||
STObject::setTypeFromSField (SField const& sField)
|
||||
{
|
||||
ResultOfSetTypeFromSField ret = noTemplate;
|
||||
|
||||
SOTemplate const* elements =
|
||||
InnerObjectFormats::getInstance ().findSOTemplateBySField (sField);
|
||||
if (elements)
|
||||
{
|
||||
ret = setType (*elements) ? typeIsSet : typeSetFail;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool STObject::isValidForType ()
|
||||
{
|
||||
auto it = v_.begin();
|
||||
@@ -219,6 +234,13 @@ bool STObject::set (SerialIter& sit, int depth)
|
||||
|
||||
// Unflatten the field
|
||||
v_.emplace_back(sit, fn);
|
||||
|
||||
// If the object type has a known SOTemplate then set it.
|
||||
STObject* const obj = dynamic_cast <STObject*> (&(v_.back().get()));
|
||||
if (obj && (obj->setTypeFromSField (fn) == typeSetFail))
|
||||
{
|
||||
throw std::runtime_error ("field deserialization error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +138,26 @@ static Json::Value singleton_expected (std::string const& object,
|
||||
"]' must be an object with a single key/object value.");
|
||||
}
|
||||
|
||||
static Json::Value serialization_error (SField const& sField)
|
||||
{
|
||||
return RPC::make_error (rpcINVALID_PARAMS,
|
||||
"Object '" + sField.getName () + "' failed to serialize.");
|
||||
}
|
||||
|
||||
static Json::Value template_mismatch (SField const& sField)
|
||||
{
|
||||
return RPC::make_error (rpcINVALID_PARAMS,
|
||||
"Object '" + sField.getName () +
|
||||
"' contents did not meet requirements for that type.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
non_object_in_array (std::string const& item, Json::UInt index)
|
||||
{
|
||||
return RPC::make_error (rpcINVALID_PARAMS,
|
||||
"Item '" + item + "' at index " + std::to_string (index) +
|
||||
" is not an object. Arrays may only contain objects.");
|
||||
}
|
||||
|
||||
// This function is used by parseObject to parse any JSON type that doesn't
|
||||
// recurse. Everything represented here is a leaf-type.
|
||||
@@ -766,6 +786,13 @@ static boost::optional <STObject> parseObject (
|
||||
}
|
||||
}
|
||||
|
||||
// Some inner object types have templates. Attempt to apply that.
|
||||
if (data.setTypeFromSField (inName) == STObject::typeSetFail)
|
||||
{
|
||||
error = template_mismatch (inName);
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return std::move (data);
|
||||
}
|
||||
|
||||
@@ -823,10 +850,17 @@ static boost::optional <detail::STVar> parseArray (
|
||||
|
||||
auto ret = parseObject (ss.str (), objectFields,
|
||||
nameField, depth + 1, error);
|
||||
if (! ret)
|
||||
{
|
||||
std::string errMsg = error["error_message"].asString ();
|
||||
error["error_message"] = "Error at '" + ss.str () +
|
||||
"'. " + errMsg;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (! ret ||
|
||||
(ret->getFName().fieldType != STI_OBJECT))
|
||||
if (ret->getFName().fieldType != STI_OBJECT)
|
||||
{
|
||||
error = non_object_in_array (ss.str(), i);
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ bool transResultInfo (TER code, std::string& token, std::string& text)
|
||||
{ temBAD_OFFER, "temBAD_OFFER", "Malformed: Bad offer." },
|
||||
{ temBAD_PATH, "temBAD_PATH", "Malformed: Bad path." },
|
||||
{ temBAD_PATH_LOOP, "temBAD_PATH_LOOP", "Malformed: Loop in path." },
|
||||
{ temBAD_QUORUM, "temBAD_QUORUM", "Malformed: Quorum is unreachable." },
|
||||
{ temBAD_SEND_XRP_LIMIT, "temBAD_SEND_XRP_LIMIT", "Malformed: Limit quality is not allowed for XRP to XRP." },
|
||||
{ temBAD_SEND_XRP_MAX, "temBAD_SEND_XRP_MAX", "Malformed: Send max is not allowed for XRP to XRP." },
|
||||
{ temBAD_SEND_XRP_NO_DIRECT,"temBAD_SEND_XRP_NO_DIRECT","Malformed: No Ripple direct is not allowed for XRP to XRP." },
|
||||
@@ -109,6 +110,7 @@ bool transResultInfo (TER code, std::string& token, std::string& text)
|
||||
{ temBAD_SEND_XRP_PATHS, "temBAD_SEND_XRP_PATHS", "Malformed: Paths are not allowed for XRP to XRP." },
|
||||
{ temBAD_SEQUENCE, "temBAD_SEQUENCE", "Malformed: Sequence is not in the past." },
|
||||
{ temBAD_SIGNATURE, "temBAD_SIGNATURE", "Malformed: Bad signature." },
|
||||
{ temBAD_SIGNER, "temBAD_SIGNER", "Malformed: A signer may not duplicate account or other signers"},
|
||||
{ temBAD_SRC_ACCOUNT, "temBAD_SRC_ACCOUNT", "Malformed: Bad source account." },
|
||||
{ temBAD_TRANSFER_RATE, "temBAD_TRANSFER_RATE", "Malformed: Transfer rate must be >= 1.0" },
|
||||
{ temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." },
|
||||
|
||||
@@ -87,6 +87,13 @@ TxFormats::TxFormats ()
|
||||
add ("TicketCancel", ttTICKET_CANCEL)
|
||||
<< SOElement (sfTicketID, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
// The SignerEntries are optional because a SignerList is deleted by
|
||||
// setting the SignerQuorum to zero and omitting SignerEntries.
|
||||
add ("SignerListSet", ttSIGNER_LIST_SET)
|
||||
<< SOElement (sfSignerQuorum, SOE_REQUIRED)
|
||||
<< SOElement (sfSignerEntries, SOE_OPTIONAL)
|
||||
;
|
||||
}
|
||||
|
||||
void TxFormats::addCommonFields (Item& item)
|
||||
|
||||
204
src/ripple/protocol/tests/InnerObjectFormats.test.cpp
Executable file
204
src/ripple/protocol/tests/InnerObjectFormats.test.cpp
Executable file
@@ -0,0 +1,204 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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 <BeastConfig.h>
|
||||
#include <ripple/protocol/InnerObjectFormats.h>
|
||||
#include <ripple/protocol/ErrorCodes.h> // RPC::containsError
|
||||
#include <ripple/json/json_reader.h> // Json::Reader
|
||||
#include <ripple/protocol/STParsedJSON.h> // STParsedJSONObject
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace InnerObjectFormatsUnitTestDetail
|
||||
{
|
||||
|
||||
struct TestJSONTxt
|
||||
{
|
||||
std::string const txt;
|
||||
bool const expectFail;
|
||||
};
|
||||
|
||||
static TestJSONTxt const testArray[] =
|
||||
{
|
||||
|
||||
// Valid SignerEntry
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
"SignerEntries" :
|
||||
[
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
|
||||
"SignerWeight" : 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"Account" : "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
|
||||
"SignerWeight" : 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"SignerQuorum" : 7,
|
||||
"TransactionType" : "SignerListSet"
|
||||
})", false
|
||||
},
|
||||
|
||||
// SignerEntry missing Account
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
"SignerEntries" :
|
||||
[
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
|
||||
"SignerWeight" : 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"SignerWeight" : 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"SignerQuorum" : 7,
|
||||
"TransactionType" : "SignerListSet"
|
||||
})", true
|
||||
},
|
||||
|
||||
// SignerEntry missing SignerWeight
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
"SignerEntries" :
|
||||
[
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
|
||||
"SignerWeight" : 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"Account" : "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
|
||||
}
|
||||
}
|
||||
],
|
||||
"SignerQuorum" : 7,
|
||||
"TransactionType" : "SignerListSet"
|
||||
})", true
|
||||
},
|
||||
|
||||
// SignerEntry with unexpected Amount
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
"SignerEntries" :
|
||||
[
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
|
||||
"SignerWeight" : 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"Amount" : "1000000",
|
||||
"Account" : "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
|
||||
"SignerWeight" : 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"SignerQuorum" : 7,
|
||||
"TransactionType" : "SignerListSet"
|
||||
})", true
|
||||
},
|
||||
|
||||
// SignerEntry with no Account and unexpected Amount
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
"SignerEntries" :
|
||||
[
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
|
||||
"SignerWeight" : 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"SignerEntry" :
|
||||
{
|
||||
"Amount" : "10000000",
|
||||
"SignerWeight" : 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"SignerQuorum" : 7,
|
||||
"TransactionType" : "SignerListSet"
|
||||
})", true
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
} // namespace InnerObjectFormatsUnitTestDetail
|
||||
|
||||
|
||||
class InnerObjectFormatsParsedJSON_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void run()
|
||||
{
|
||||
using namespace InnerObjectFormatsUnitTestDetail;
|
||||
|
||||
for (auto const& test : testArray)
|
||||
{
|
||||
Json::Value req;
|
||||
Json::Reader ().parse (test.txt, req);
|
||||
if (RPC::contains_error (req))
|
||||
{
|
||||
throw std::runtime_error (
|
||||
"Internal InnerObjectFormatsParsedJSON error. Bad JSON.");
|
||||
}
|
||||
STParsedJSONObject parsed ("request", req);
|
||||
bool const noObj = parsed.object == boost::none;
|
||||
if ( noObj == test.expectFail )
|
||||
{
|
||||
pass ();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string errStr ("Unexpected STParsedJSON result on:\n");
|
||||
errStr += test.txt;
|
||||
fail (errStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(InnerObjectFormatsParsedJSON,ripple_app,ripple);
|
||||
|
||||
} // ripple
|
||||
Reference in New Issue
Block a user