mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Remove runtime inference of unrecognized SFields
This commit is contained in:
committed by
Nik Bougalis
parent
9279a3fee7
commit
57fe197d3e
@@ -235,12 +235,54 @@ public:
|
||||
testcase ("serialization");
|
||||
|
||||
unexpected (sfGeneric.isUseful (), "sfGeneric must not be useful");
|
||||
{
|
||||
// Try to put sfGeneric in an SOTemplate.
|
||||
SOTemplate elements;
|
||||
except<std::runtime_error>( [&]()
|
||||
{
|
||||
elements.push_back (SOElement (sfGeneric, SOE_REQUIRED));
|
||||
});
|
||||
}
|
||||
|
||||
unexpected (sfInvalid.isUseful (), "sfInvalid must not be useful");
|
||||
{
|
||||
// Test return of sfInvalid.
|
||||
auto testInvalid = [this] (SerializedTypeID tid, int fv)
|
||||
{
|
||||
SField const& shouldBeInvalid {SField::getField (tid, fv)};
|
||||
BEAST_EXPECT (shouldBeInvalid == sfInvalid);
|
||||
};
|
||||
testInvalid (STI_VL, 255);
|
||||
testInvalid (STI_HASH256, 255);
|
||||
testInvalid (STI_UINT32, 255);
|
||||
testInvalid (STI_VECTOR256, 255);
|
||||
testInvalid (STI_OBJECT, 255);
|
||||
}
|
||||
{
|
||||
// Try to put sfInvalid in an SOTemplate.
|
||||
SOTemplate elements;
|
||||
except<std::runtime_error>( [&]()
|
||||
{
|
||||
elements.push_back (SOElement (sfInvalid, SOE_REQUIRED));
|
||||
});
|
||||
}
|
||||
{
|
||||
// Try to put the same SField into an SOTemplate twice.
|
||||
SOTemplate elements;
|
||||
elements.push_back (SOElement (sfAccount, SOE_REQUIRED));
|
||||
except<std::runtime_error>( [&]()
|
||||
{
|
||||
elements.push_back (SOElement (sfAccount, SOE_REQUIRED));
|
||||
});
|
||||
}
|
||||
|
||||
// Put a variety of SFields of different types in an SOTemplate.
|
||||
SField const& sfTestVL = sfMasterSignature;
|
||||
SField const& sfTestH256 = sfCheckID;
|
||||
SField const& sfTestU32 = sfSettleDelay;
|
||||
SField const& sfTestV256 = sfAmendments;
|
||||
SField const& sfTestObject = sfMajority;
|
||||
|
||||
SField const& sfTestVL = SField::getField (STI_VL, 255);
|
||||
SField const& sfTestH256 = SField::getField (STI_HASH256, 255);
|
||||
SField const& sfTestU32 = SField::getField (STI_UINT32, 255);
|
||||
SField const& sfTestV256 = SField::getField(STI_VECTOR256, 255);
|
||||
SField const& sfTestObject = SField::getField (STI_OBJECT, 255);
|
||||
|
||||
SOTemplate elements;
|
||||
elements.push_back (SOElement (sfFlags, SOE_REQUIRED));
|
||||
@@ -639,15 +681,15 @@ public:
|
||||
|
||||
try
|
||||
{
|
||||
std::array<std::uint8_t, 5> const payload {{ 0xee, 0xee, 0xe1, 0xee, 0xee }};
|
||||
std::array<std::uint8_t, 7> const payload {
|
||||
{ 0xe9, 0x12, 0xab, 0xcd, 0x12, 0xfe, 0xdc }};
|
||||
SerialIter sit{makeSlice(payload)};
|
||||
auto obj = std::make_shared<STArray>(sit, sfMetadata);
|
||||
BEAST_EXPECT(!obj);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BEAST_EXPECT(strcmp(e.what(),
|
||||
"Duplicate field detected") == 0);
|
||||
BEAST_EXPECT(strcmp(e.what(), "Duplicate field detected") == 0);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -659,45 +701,7 @@ public:
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BEAST_EXPECT(strcmp(e.what(),
|
||||
"Duplicate field detected") == 0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::array<std::uint8_t, 250> const payload
|
||||
{{
|
||||
0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f,
|
||||
0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00,
|
||||
0x00, 0x00, 0x35, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68,
|
||||
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x73, 0x00, 0x81, 0x14,
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x24, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, 0xf3, 0xe7, 0xe5, 0x65,
|
||||
0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x6f, 0x00, 0x00, 0x20,
|
||||
0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35,
|
||||
0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00,
|
||||
0x00, 0x20, 0x1e, 0x00, 0x4f, 0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00,
|
||||
0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x35, 0x24, 0x59, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||
0x00, 0x54, 0x72, 0x61, 0x6e, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5,
|
||||
0xfe, 0xf3, 0xe7, 0xe5, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e,
|
||||
0x00, 0x6f, 0x00, 0x00, 0x20, 0xf6, 0x00, 0x00, 0x03, 0x1f, 0x20, 0x20,
|
||||
0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x73, 0x00,
|
||||
0x81, 0x14, 0x00, 0x10, 0x00, 0x73, 0x00, 0x81, 0x14, 0x00, 0x10, 0x00,
|
||||
0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe
|
||||
}};
|
||||
|
||||
SerialIter sit{makeSlice(payload)};
|
||||
auto obj = std::make_shared<STTx>(sit);
|
||||
BEAST_EXPECT(!obj);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BEAST_EXPECT(strcmp(e.what(),
|
||||
"Duplicate field detected") == 0);
|
||||
BEAST_EXPECT(strcmp(e.what(), "Duplicate field detected") == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <ripple/basics/Slice.h>
|
||||
#include <ripple/protocol/messages.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class STTx_test : public beast::unit_test::suite
|
||||
@@ -1218,7 +1220,7 @@ public:
|
||||
0x12, 0x12, 0x12, 0xff
|
||||
};
|
||||
|
||||
constexpr unsigned char dupField[] =
|
||||
constexpr unsigned char payload3[] =
|
||||
{
|
||||
0x12, 0x00, 0x65, 0x24, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1e, 0x00, 0x4f,
|
||||
0x00, 0x00, 0x20, 0x1f, 0x03, 0xf6, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00,
|
||||
@@ -1243,6 +1245,173 @@ public:
|
||||
0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe
|
||||
};
|
||||
|
||||
// Construct an STObject with 11 levels of object nesting so the
|
||||
// maximum nesting level exception is thrown.
|
||||
{
|
||||
// Construct an SOTemplate to get the ball rolling on building
|
||||
// an STObject that can contain another STObject.
|
||||
SOTemplate recurse;
|
||||
recurse.push_back (SOElement {sfTransactionMetaData, SOE_OPTIONAL});
|
||||
recurse.push_back (SOElement {sfTransactionHash, SOE_OPTIONAL});
|
||||
|
||||
// Make an STObject that nests objects ten levels deep. There's
|
||||
// a minimum transaction size we must meet, so include a hash256.
|
||||
uint256 const hash {42u};
|
||||
auto inner =
|
||||
std::make_unique<STObject> (recurse, sfTransactionMetaData);
|
||||
inner->setFieldH256 (sfTransactionHash, hash);
|
||||
|
||||
for (int i = 1; i < 10; ++i)
|
||||
{
|
||||
auto outer =
|
||||
std::make_unique<STObject> (recurse, sfTransactionMetaData);
|
||||
outer->set (std::move (inner));
|
||||
outer->setFieldH256 (sfTransactionHash, hash);
|
||||
inner = std::move (outer);
|
||||
}
|
||||
{
|
||||
// A ten-deep nested STObject should throw an exception that
|
||||
// there is no sfTransactionType field.
|
||||
Serializer const tenDeep {inner->getSerializer()};
|
||||
SerialIter tenSit {tenDeep.slice()};
|
||||
try
|
||||
{
|
||||
auto stx = std::make_shared<ripple::STTx const>(tenSit);
|
||||
fail ("STTx construction should have thrown.");
|
||||
}
|
||||
catch (std::runtime_error const& ex)
|
||||
{
|
||||
BEAST_EXPECT (
|
||||
std::strcmp (ex.what(), "Field not found") == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Add one more level of nesting and we should get an error
|
||||
// about excessive nesting.
|
||||
STObject outer {recurse, sfTransactionMetaData};
|
||||
outer.set (std::move (inner));
|
||||
outer.setFieldH256 (sfTransactionHash, hash);
|
||||
|
||||
Serializer const tooDeep {outer.getSerializer()};
|
||||
SerialIter tooDeepSit {tooDeep.slice()};
|
||||
try
|
||||
{
|
||||
auto stx = std::make_shared<ripple::STTx const>(tooDeepSit);
|
||||
fail ("STTx construction should have thrown.");
|
||||
}
|
||||
catch (std::runtime_error const& ex)
|
||||
{
|
||||
BEAST_EXPECT (std::strcmp (ex.what(),
|
||||
"Maximum nesting depth of STVar exceeded") == 0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Construct an STObject with 11 levels of nesting so the
|
||||
// maximum nesting level exception is thrown. This time
|
||||
// we're nesting arrays in addition to objects.
|
||||
|
||||
// Construct an SOTemplate to get the ball rolling on building
|
||||
// an STObject that can contain an STArray.
|
||||
SOTemplate recurse;
|
||||
recurse.push_back (SOElement {sfTransactionMetaData, SOE_OPTIONAL});
|
||||
recurse.push_back (SOElement {sfTransactionHash, SOE_OPTIONAL});
|
||||
recurse.push_back (SOElement {sfTemplate, SOE_OPTIONAL});
|
||||
|
||||
// Make an STObject that nests ten levels deep alternating objects
|
||||
// and arrays. Include a hash256 to meet the minimum transaction
|
||||
// size.
|
||||
uint256 const hash {42u};
|
||||
STObject inner = {recurse, sfTransactionMetaData};
|
||||
inner.setFieldH256 (sfTransactionHash, hash);
|
||||
|
||||
for (int i = 1; i < 5; ++i)
|
||||
{
|
||||
STObject outer {recurse, sfTransactionMetaData};
|
||||
outer.setFieldH256 (sfTransactionHash, hash);
|
||||
STArray& array {outer.peekFieldArray (sfTemplate)};
|
||||
array.push_back (std::move (inner));
|
||||
inner = std::move (outer);
|
||||
}
|
||||
{
|
||||
// A nine-deep nested STObject/STArray should throw an
|
||||
// exception that there is no sfTransactionType field.
|
||||
Serializer const nineDeep {inner.getSerializer()};
|
||||
SerialIter nineSit {nineDeep.slice()};
|
||||
try
|
||||
{
|
||||
auto stx = std::make_shared<ripple::STTx const>(nineSit);
|
||||
fail ("STTx construction should have thrown.");
|
||||
}
|
||||
catch (std::runtime_error const& ex)
|
||||
{
|
||||
BEAST_EXPECT (
|
||||
std::strcmp (ex.what(), "Field not found") == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Add one more level of nesting and we should get an error
|
||||
// about excessive nesting.
|
||||
STObject outer {recurse, sfTransactionMetaData};
|
||||
outer.setFieldH256 (sfTransactionHash, hash);
|
||||
STArray& array {outer.peekFieldArray (sfTemplate)};
|
||||
array.push_back (std::move (inner));
|
||||
|
||||
Serializer const tooDeep {outer.getSerializer()};
|
||||
SerialIter tooDeepSit {tooDeep.slice()};
|
||||
try
|
||||
{
|
||||
auto stx = std::make_shared<ripple::STTx const>(tooDeepSit);
|
||||
fail ("STTx construction should have thrown.");
|
||||
}
|
||||
catch (std::runtime_error const& ex)
|
||||
{
|
||||
BEAST_EXPECT (std::strcmp (ex.what(),
|
||||
"Maximum nesting depth of STVar exceeded") == 0);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Make an otherwise legit STTx with a duplicate field. Should
|
||||
// generate an exception when we deserialize.
|
||||
auto const keypair = randomKeyPair (KeyType::secp256k1);
|
||||
STTx acctSet (ttACCOUNT_SET,
|
||||
[&keypair](auto& obj)
|
||||
{
|
||||
obj.setAccountID (sfAccount, calcAccountID(keypair.first));
|
||||
obj.setFieldU32 (sfSequence, 7);
|
||||
obj.setFieldAmount (sfFee, STAmount (2557891634ull));
|
||||
obj.setFieldVL (sfSigningPubKey, keypair.first.slice());
|
||||
obj.setFieldU32 (sfSetFlag, 0x0DDBA11);
|
||||
obj.setFieldU32 (sfClearFlag, 0xB01DFACE);
|
||||
});
|
||||
|
||||
Serializer serialized {acctSet.getSerializer()};
|
||||
{
|
||||
// Verify we have a valid transaction.
|
||||
SerialIter sit {serialized.slice()};
|
||||
auto stx = std::make_shared<ripple::STTx const>(sit);
|
||||
}
|
||||
|
||||
// Tweak the serialized data to change the ClearFlag to
|
||||
// a SetFlag. This will leave us with two SetFlag fields
|
||||
// which we should trap as a duplicate field.
|
||||
BEAST_EXPECT (serialized.modData()[15] == sfClearFlag.fieldValue);
|
||||
serialized.modData()[15] = sfSetFlag.fieldValue;
|
||||
|
||||
SerialIter sit {serialized.slice()};
|
||||
try
|
||||
{
|
||||
auto stx = std::make_shared<ripple::STTx const>(sit);
|
||||
fail("An exception should have been thrown");
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
strcmp(ex.what(), "Duplicate field detected") == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Exercise the three raw transactions.
|
||||
try
|
||||
{
|
||||
protocol::TMTransaction tx2;
|
||||
@@ -1253,10 +1422,9 @@ public:
|
||||
auto stx = std::make_shared<ripple::STTx const>(sit);
|
||||
fail("An exception should have been thrown");
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
BEAST_EXPECT(strcmp(e.what(),
|
||||
"Maximum nesting depth of STVar exceeded") == 0);
|
||||
BEAST_EXPECT(strcmp(ex.what(), "Unknown field") == 0);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -1265,22 +1433,20 @@ public:
|
||||
auto stx = std::make_shared<ripple::STTx const>(sit);
|
||||
fail("An exception should have been thrown");
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
BEAST_EXPECT(strcmp(e.what(),
|
||||
"Maximum nesting depth of STVar exceeded") == 0);
|
||||
BEAST_EXPECT(strcmp(ex.what(), "Unknown field") == 0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ripple::SerialIter sit (Slice{dupField, sizeof(dupField)});
|
||||
ripple::SerialIter sit {payload3};
|
||||
auto stx = std::make_shared<ripple::STTx const>(sit);
|
||||
fail("An exception should have been thrown");
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
BEAST_EXPECT(strcmp(e.what(),
|
||||
"Duplicate field detected") == 0);
|
||||
BEAST_EXPECT(strcmp(ex.what(), "Unknown field") == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user