mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +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
@@ -1976,12 +1976,26 @@
|
|||||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\SetSignerList.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\tx\impl\SetTrust.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\SetTrust.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\SignerEntries.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\tx\impl\SignerEntries.h">
|
||||||
|
</ClInclude>
|
||||||
<ClCompile Include="..\..\src\ripple\app\tx\impl\Taker.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\Taker.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
@@ -2794,6 +2808,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\protocol\impl\InnerObjectFormats.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\protocol\impl\LedgerFormats.cpp">
|
<ClCompile Include="..\..\src\ripple\protocol\impl\LedgerFormats.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
@@ -2894,6 +2912,8 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\protocol\Indexes.h">
|
<ClInclude Include="..\..\src\ripple\protocol\Indexes.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\protocol\InnerObjectFormats.h">
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\protocol\Issue.h">
|
<ClInclude Include="..\..\src\ripple\protocol\Issue.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\protocol\JsonFields.h">
|
<ClInclude Include="..\..\src\ripple\protocol\JsonFields.h">
|
||||||
@@ -2960,6 +2980,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\protocol\tests\InnerObjectFormats.test.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\protocol\tests\Issue.test.cpp">
|
<ClCompile Include="..\..\src\ripple\protocol\tests\Issue.test.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
@@ -3499,10 +3523,6 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\unity\app9.cpp">
|
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\..\src\ripple\unity\basics.cpp">
|
<ClCompile Include="..\..\src\ripple\unity\basics.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -2529,9 +2529,18 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\app\tx\impl\SetRegularKey.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\SetRegularKey.cpp">
|
||||||
<Filter>ripple\app\tx\impl</Filter>
|
<Filter>ripple\app\tx\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\SetSignerList.cpp">
|
||||||
|
<Filter>ripple\app\tx\impl</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\tx\impl\SetTrust.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\SetTrust.cpp">
|
||||||
<Filter>ripple\app\tx\impl</Filter>
|
<Filter>ripple\app\tx\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\SignerEntries.cpp">
|
||||||
|
<Filter>ripple\app\tx\impl</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\tx\impl\SignerEntries.h">
|
||||||
|
<Filter>ripple\app\tx\impl</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClCompile Include="..\..\src\ripple\app\tx\impl\Taker.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\Taker.cpp">
|
||||||
<Filter>ripple\app\tx\impl</Filter>
|
<Filter>ripple\app\tx\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -3327,6 +3336,9 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\protocol\impl\Indexes.cpp">
|
<ClCompile Include="..\..\src\ripple\protocol\impl\Indexes.cpp">
|
||||||
<Filter>ripple\protocol\impl</Filter>
|
<Filter>ripple\protocol\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\protocol\impl\InnerObjectFormats.cpp">
|
||||||
|
<Filter>ripple\protocol\impl</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\protocol\impl\LedgerFormats.cpp">
|
<ClCompile Include="..\..\src\ripple\protocol\impl\LedgerFormats.cpp">
|
||||||
<Filter>ripple\protocol\impl</Filter>
|
<Filter>ripple\protocol\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -3405,6 +3417,9 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\protocol\Indexes.h">
|
<ClInclude Include="..\..\src\ripple\protocol\Indexes.h">
|
||||||
<Filter>ripple\protocol</Filter>
|
<Filter>ripple\protocol</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\protocol\InnerObjectFormats.h">
|
||||||
|
<Filter>ripple\protocol</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\protocol\Issue.h">
|
<ClInclude Include="..\..\src\ripple\protocol\Issue.h">
|
||||||
<Filter>ripple\protocol</Filter>
|
<Filter>ripple\protocol</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -3501,6 +3516,9 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\protocol\tests\BuildInfo.test.cpp">
|
<ClCompile Include="..\..\src\ripple\protocol\tests\BuildInfo.test.cpp">
|
||||||
<Filter>ripple\protocol\tests</Filter>
|
<Filter>ripple\protocol\tests</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\protocol\tests\InnerObjectFormats.test.cpp">
|
||||||
|
<Filter>ripple\protocol\tests</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\protocol\tests\Issue.test.cpp">
|
<ClCompile Include="..\..\src\ripple\protocol\tests\Issue.test.cpp">
|
||||||
<Filter>ripple\protocol\tests</Filter>
|
<Filter>ripple\protocol\tests</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -4068,9 +4086,6 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\unity\app8.cpp">
|
<ClCompile Include="..\..\src\ripple\unity\app8.cpp">
|
||||||
<Filter>ripple\unity</Filter>
|
<Filter>ripple\unity</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\unity\app9.cpp">
|
|
||||||
<Filter>ripple\unity</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\..\src\ripple\unity\basics.cpp">
|
<ClCompile Include="..\..\src\ripple\unity\basics.cpp">
|
||||||
<Filter>ripple\unity</Filter>
|
<Filter>ripple\unity</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -678,7 +678,6 @@ def get_unity_sources():
|
|||||||
'src/ripple/unity/app6.cpp',
|
'src/ripple/unity/app6.cpp',
|
||||||
'src/ripple/unity/app7.cpp',
|
'src/ripple/unity/app7.cpp',
|
||||||
'src/ripple/unity/app8.cpp',
|
'src/ripple/unity/app8.cpp',
|
||||||
'src/ripple/unity/app9.cpp',
|
|
||||||
'src/ripple/unity/core.cpp',
|
'src/ripple/unity/core.cpp',
|
||||||
'src/ripple/unity/basics.cpp',
|
'src/ripple/unity/basics.cpp',
|
||||||
'src/ripple/unity/crypto.cpp',
|
'src/ripple/unity/crypto.cpp',
|
||||||
|
|||||||
@@ -202,4 +202,13 @@
|
|||||||
#define RIPPLE_ENABLE_TICKETS 0
|
#define RIPPLE_ENABLE_TICKETS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** Config: RIPPLE_ENABLE_MULTI_SIGN
|
||||||
|
When set, activates the current state of the multi-sign feature which is
|
||||||
|
under development. When the feature is complete and released this
|
||||||
|
#define should be removed.
|
||||||
|
*/
|
||||||
|
#ifndef RIPPLE_ENABLE_MULTI_SIGN
|
||||||
|
#define RIPPLE_ENABLE_MULTI_SIGN 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1018,50 +1018,65 @@ uint256 LedgerEntrySet::getNextLedgerIndex (
|
|||||||
|
|
||||||
void LedgerEntrySet::incrementOwnerCount (SLE::ref sleAccount)
|
void LedgerEntrySet::incrementOwnerCount (SLE::ref sleAccount)
|
||||||
{
|
{
|
||||||
assert (sleAccount);
|
increaseOwnerCount (sleAccount, 1);
|
||||||
|
|
||||||
std::uint32_t const current_count = sleAccount->getFieldU32 (sfOwnerCount);
|
|
||||||
|
|
||||||
if (current_count == std::numeric_limits<std::uint32_t>::max ())
|
|
||||||
{
|
|
||||||
WriteLog (lsFATAL, LedgerEntrySet) <<
|
|
||||||
"Account " << sleAccount->getFieldAccount160 (sfAccount) <<
|
|
||||||
" owner count exceeds max!";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sleAccount->setFieldU32 (sfOwnerCount, current_count + 1);
|
|
||||||
entryModify (sleAccount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LedgerEntrySet::incrementOwnerCount (Account const& owner)
|
void LedgerEntrySet::incrementOwnerCount (Account const& owner)
|
||||||
{
|
{
|
||||||
incrementOwnerCount(entryCache (ltACCOUNT_ROOT,
|
increaseOwnerCount(
|
||||||
getAccountRootIndex (owner)));
|
entryCache (ltACCOUNT_ROOT, getAccountRootIndex (owner)), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LedgerEntrySet::decrementOwnerCount (SLE::ref sleAccount)
|
void
|
||||||
|
LedgerEntrySet::increaseOwnerCount (SLE::ref sleAccount, std::size_t howMuch)
|
||||||
{
|
{
|
||||||
assert (sleAccount);
|
assert (sleAccount);
|
||||||
|
|
||||||
std::uint32_t const current_count = sleAccount->getFieldU32 (sfOwnerCount);
|
std::uint32_t const current_count = sleAccount->getFieldU32 (sfOwnerCount);
|
||||||
|
std::uint32_t new_count = current_count + howMuch;
|
||||||
|
|
||||||
if (current_count == 0)
|
// Check for integer overflow -- well defined behavior on unsigned.
|
||||||
|
if (new_count < current_count)
|
||||||
{
|
{
|
||||||
WriteLog (lsFATAL, LedgerEntrySet) <<
|
WriteLog (lsFATAL, LedgerEntrySet) <<
|
||||||
"Account " << sleAccount->getFieldAccount160 (sfAccount) <<
|
"Account " << sleAccount->getFieldAccount160 (sfAccount) <<
|
||||||
" owner count is already 0!";
|
" owner count exceeds max!";
|
||||||
return;
|
new_count = std::numeric_limits<std::uint32_t>::max ();
|
||||||
}
|
}
|
||||||
|
sleAccount->setFieldU32 (sfOwnerCount, new_count);
|
||||||
sleAccount->setFieldU32 (sfOwnerCount, current_count - 1);
|
|
||||||
entryModify (sleAccount);
|
entryModify (sleAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LedgerEntrySet::decrementOwnerCount (SLE::ref sleAccount)
|
||||||
|
{
|
||||||
|
decreaseOwnerCount (sleAccount, 1);
|
||||||
|
}
|
||||||
|
|
||||||
void LedgerEntrySet::decrementOwnerCount (Account const& owner)
|
void LedgerEntrySet::decrementOwnerCount (Account const& owner)
|
||||||
{
|
{
|
||||||
decrementOwnerCount(entryCache (ltACCOUNT_ROOT,
|
decreaseOwnerCount(
|
||||||
getAccountRootIndex (owner)));
|
entryCache (ltACCOUNT_ROOT, getAccountRootIndex (owner)), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LedgerEntrySet::decreaseOwnerCount (SLE::ref sleAccount, std::size_t howMuch)
|
||||||
|
{
|
||||||
|
assert (sleAccount);
|
||||||
|
|
||||||
|
std::uint32_t const current_count = sleAccount->getFieldU32 (sfOwnerCount);
|
||||||
|
std::uint32_t new_count = current_count - howMuch;
|
||||||
|
|
||||||
|
// Check for integer underflow -- well defined behavior on unsigned.
|
||||||
|
if (new_count > current_count)
|
||||||
|
{
|
||||||
|
WriteLog (lsFATAL, LedgerEntrySet) <<
|
||||||
|
"Account " << sleAccount->getFieldAccount160 (sfAccount) <<
|
||||||
|
" owner count set below 0!";
|
||||||
|
new_count = 0;
|
||||||
|
assert (false); // "This is a dangerous place." Stop in a debug build.
|
||||||
|
}
|
||||||
|
sleAccount->setFieldU32 (sfOwnerCount, new_count);
|
||||||
|
entryModify (sleAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
TER LedgerEntrySet::offerDelete (SLE::pointer sleOffer)
|
TER LedgerEntrySet::offerDelete (SLE::pointer sleOffer)
|
||||||
|
|||||||
@@ -185,11 +185,13 @@ public:
|
|||||||
/** @{ */
|
/** @{ */
|
||||||
void incrementOwnerCount (SLE::ref sleAccount);
|
void incrementOwnerCount (SLE::ref sleAccount);
|
||||||
void incrementOwnerCount (Account const& owner);
|
void incrementOwnerCount (Account const& owner);
|
||||||
|
void increaseOwnerCount (SLE::ref sleAccount, std::size_t howMuch);
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** @{ */
|
/** @{ */
|
||||||
void decrementOwnerCount (SLE::ref sleAccount);
|
void decrementOwnerCount (SLE::ref sleAccount);
|
||||||
void decrementOwnerCount (Account const& owner);
|
void decrementOwnerCount (Account const& owner);
|
||||||
|
void decreaseOwnerCount (SLE::ref sleAccount, std::size_t howMuch);
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
// Offer functions.
|
// Offer functions.
|
||||||
|
|||||||
@@ -137,7 +137,9 @@ void printHelp (const po::options_description& desc)
|
|||||||
" ripple_path_find <json> [<ledger>]\n"
|
" ripple_path_find <json> [<ledger>]\n"
|
||||||
" version\n"
|
" version\n"
|
||||||
" server_info\n"
|
" server_info\n"
|
||||||
|
" sign\n"
|
||||||
" stop\n"
|
" stop\n"
|
||||||
|
" submit\n"
|
||||||
" tx <id>\n"
|
" tx <id>\n"
|
||||||
" unl_add <domain>|<public> [<comment>]\n"
|
" unl_add <domain>|<public> [<comment>]\n"
|
||||||
" unl_delete <domain>|<public_key>\n"
|
" unl_delete <domain>|<public_key>\n"
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
|
|
||||||
# Transactors #
|
|
||||||
|
|
||||||
## Introduction ##
|
|
||||||
|
|
||||||
Each separate kind of transaction is handled by it's own Transactor.
|
|
||||||
The Transactor base class provides functionality that is common to
|
|
||||||
all derived Transactors. The Transactor base class also gives derived
|
|
||||||
classes the ability to replace portions of the default implementation.
|
|
||||||
|
|
||||||
|
|
||||||
# Details on Specific Transactors #
|
|
||||||
|
|
||||||
## AddWallet ##
|
|
||||||
|
|
||||||
## Change ##
|
|
||||||
|
|
||||||
## Offers ##
|
|
||||||
|
|
||||||
### CreateOffer ###
|
|
||||||
|
|
||||||
### CancelOffer ###
|
|
||||||
|
|
||||||
## Payment ##
|
|
||||||
|
|
||||||
## SetAccount ##
|
|
||||||
|
|
||||||
## SetRegularKey ##
|
|
||||||
|
|
||||||
## SetTrust ##
|
|
||||||
|
|
||||||
## Tickets ##
|
|
||||||
|
|
||||||
**Associated JIRA task is [RIPD-368](https://ripplelabs.atlassian.net/browse/RIPD-368)**
|
|
||||||
|
|
||||||
Currently transactions on the Ripple network require the use of sequence
|
|
||||||
numbers and sequence numbers must monotonically increase. Since the sequence
|
|
||||||
number is part of the transaction, it is "covered" by the signature that
|
|
||||||
authorizes the transaction, which means that the sequence number would have
|
|
||||||
to be decided at the time of transaction issuance. This would mean that
|
|
||||||
multi-signature transactions could only be processed in strict "first-in,
|
|
||||||
first-out" order which is not practical.
|
|
||||||
|
|
||||||
Tickets can be used in lieu of sequence number. A ticket is a special token
|
|
||||||
which, through a transaction, can be issued by any account and can be
|
|
||||||
configured with an optional expiry date and an optional associated account.
|
|
||||||
|
|
||||||
### Specifics ###
|
|
||||||
|
|
||||||
The expiry date can be used to constrain the validity of the ticket. If
|
|
||||||
specified, the ticket will be considered invalid and unusable if the closing
|
|
||||||
time of the last *validated* ledger is greater than or equal to the expiration
|
|
||||||
time of the ticket.
|
|
||||||
|
|
||||||
The associated account can be used to specify an account, other than the
|
|
||||||
issuing account, that is allowed to "consume" the ticket. Consuming a ticket
|
|
||||||
means to use the ticket in a transaction that is accepted by the network and
|
|
||||||
makes its way into a validated ledger. If not present, the ticket can only be
|
|
||||||
consumed by the issuing account.
|
|
||||||
|
|
||||||
*Corner Case:* It is possible that two or more transactions reference the same
|
|
||||||
ticket and that both go into the same consensus set. During final application
|
|
||||||
of transactions from the consensus set at most one of these transactions may
|
|
||||||
succeed; others must fail with the indication that the ticket has been consumed.
|
|
||||||
|
|
||||||
*Reserve:* While a ticket is outstanding, it should count against the reserve
|
|
||||||
of the *issuer*.
|
|
||||||
|
|
||||||
##### Issuance
|
|
||||||
We should decide whether, in the case of multi-signature accounts, any single
|
|
||||||
authorized signer can issue a ticket on the multi-signature accounts' behalf.
|
|
||||||
This approach has both advantages and disadvantages.
|
|
||||||
|
|
||||||
Advantages include:
|
|
||||||
|
|
||||||
+ Simpler logic for tickets and reduced data - no need to store or consider an associated account.
|
|
||||||
+ Owner reserves for issued tickets count against the multi-signature account instead of the account of the signer proposing a transaction.
|
|
||||||
+ Cleaner meta-data: easier to follow who issued a ticket and how many tickets are outstanding and associated with a particular account.
|
|
||||||
|
|
||||||
Disadvantages are:
|
|
||||||
|
|
||||||
+ Any single authorized signer can issue multiple tickets, each counting against the account's reserve.
|
|
||||||
+ Special-case logic for authorizing tickets on multi-sign accounts.
|
|
||||||
|
|
||||||
I believe that the disadvantages outweigh the advantages, but debate is welcome.
|
|
||||||
|
|
||||||
|
|
||||||
### CreateTicket ###
|
|
||||||
|
|
||||||
### CancelTicket ###
|
|
||||||
217
src/ripple/app/tx/README.md
Normal file
217
src/ripple/app/tx/README.md
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
|
||||||
|
# Transactors #
|
||||||
|
|
||||||
|
## Introduction ##
|
||||||
|
|
||||||
|
Each separate kind of transaction is handled by it's own Transactor.
|
||||||
|
The Transactor base class provides functionality that is common to
|
||||||
|
all derived Transactors. The Transactor base class also gives derived
|
||||||
|
classes the ability to replace portions of the default implementation.
|
||||||
|
|
||||||
|
|
||||||
|
# Details on Specific Transactors #
|
||||||
|
|
||||||
|
## AddWallet ##
|
||||||
|
|
||||||
|
## Change ##
|
||||||
|
|
||||||
|
## Offers ##
|
||||||
|
|
||||||
|
### CreateOffer ###
|
||||||
|
|
||||||
|
### CancelOffer ###
|
||||||
|
|
||||||
|
## Payment ##
|
||||||
|
|
||||||
|
## SetAccount ##
|
||||||
|
|
||||||
|
## SetRegularKey ##
|
||||||
|
|
||||||
|
## SetSignerList ##
|
||||||
|
|
||||||
|
**Associated JIRA task is [RIPD-182](https://ripplelabs.atlassian.net/browse/RIPD-182)**
|
||||||
|
|
||||||
|
In order to enhance the flexibility of Ripple and provide support for enhanced
|
||||||
|
security of accounts, native support for "multi-signature" or "multi-sign"
|
||||||
|
accounts is required.
|
||||||
|
|
||||||
|
Transactions on an account which is designated as multi-sign can be authorized
|
||||||
|
either by using the master or regular keys (unless those are disabled) or by
|
||||||
|
being signed by a certain number (a quorum) of pre-authorized accounts.
|
||||||
|
|
||||||
|
Some technical details, including tables indicating some of the Ripple
|
||||||
|
commands and ledger entries to be used for implementing multi-signature, are
|
||||||
|
currently listed on the [wiki](https://ripple.com/wiki/Multisign) but will
|
||||||
|
eventually be migrated into this document as well.
|
||||||
|
|
||||||
|
For accounts which are designated as multi-sign, there must be a list which
|
||||||
|
specifies which accounts are authorized to sign and the quorum threshold.
|
||||||
|
|
||||||
|
- Each authorized account has a weight. The sum of the weights of the signers is used to determine whether a given set of signatures is sufficient for a quorum.
|
||||||
|
|
||||||
|
- The quorum threshold indicates the minimum required weight that the sum of the weights of all signatures must have before a transaction can be authorized.
|
||||||
|
|
||||||
|
### Verification of Multiple Signatures During TX Processing
|
||||||
|
The current approach to adding multi-signature support is to require that a
|
||||||
|
transaction is to be signed outside the Ripple network and only submitted
|
||||||
|
after the quorum has been reached.
|
||||||
|
|
||||||
|
This reduces the implementation footprint and the load imposed on the network,
|
||||||
|
and mirrors the way transaction signing is currently handled. It will require
|
||||||
|
some messaging mechanism outside the Ripple network to disseminate the proposed
|
||||||
|
transaction to the authorized signers and to allow them to apply signatures.
|
||||||
|
|
||||||
|
Supporting in-ledger voting should be considered, but it has both advantages
|
||||||
|
and disadvantages.
|
||||||
|
|
||||||
|
One of the advantages is increased transparency - transactions are visible as
|
||||||
|
are the "votes" (the authorized accounts signing the transaction) on the
|
||||||
|
ledger. However, transactions may also languish for a long time in the ledger,
|
||||||
|
never reaching a quorum and consuming network resources.
|
||||||
|
|
||||||
|
### Signature Format
|
||||||
|
We should not develop a new format for multi-sign signatures. Instead every
|
||||||
|
signer should extract and sign the transaction as they normally would if this
|
||||||
|
were a regular transaction. The resulting signature will be stored as a triple
|
||||||
|
of { signing-account, signer-public-key, signature } in an array of signatures
|
||||||
|
associated with this transaction.
|
||||||
|
|
||||||
|
The advantage of this is that we can reuse the existing signing and
|
||||||
|
verification code, and leverage the work that will go towards implementing
|
||||||
|
support for the Ed25519 elliptic curve.
|
||||||
|
|
||||||
|
### Fees ###
|
||||||
|
Multi-signature transactions impose a heavier load on the network and should
|
||||||
|
claim higher fees.
|
||||||
|
|
||||||
|
The fee structure is not yet decided, but an escalating fee structure is laid
|
||||||
|
out and discussed on the [wiki](https://ripple.com/wiki/Multisign). This
|
||||||
|
might need to be revisited and designed in light of discussions about changing
|
||||||
|
how fees are handled.
|
||||||
|
|
||||||
|
### Proposed Transaction Cancellation ###
|
||||||
|
A transaction that has been proposed against a multi-sign account using a
|
||||||
|
ticket can be positively canceled if a quorum of authorized signers sign and
|
||||||
|
issue a transaction that consumes that ticket.
|
||||||
|
|
||||||
|
### Implementation ###
|
||||||
|
|
||||||
|
Any account can have one SignerList attached to it. A SignerList contains the
|
||||||
|
following elements:
|
||||||
|
|
||||||
|
- A list of from 2 to a protocol-defined maximum of 8 signers. Each signer in the array consists of:
|
||||||
|
- The signer's 160-bit account ID and
|
||||||
|
- The signer's 16-bit weight (used to calculate whether a quorum is met).
|
||||||
|
- And, for the entire list, a single 32-bit quorum value.
|
||||||
|
|
||||||
|
Giving the signers different weights allows an account to organize signers so
|
||||||
|
some are more important than others. A signer with a larger weight has more
|
||||||
|
significance in achieving the quorum.
|
||||||
|
|
||||||
|
A multi-signed transaction is validated like this:
|
||||||
|
|
||||||
|
- Each signer of the transaction has their signature validated.
|
||||||
|
- The weights of all valid signers are summed.
|
||||||
|
- If the sum of the weights equals or exceeds the quorum value then the entire transaction is considered signed. If the sum is below the quorum, then the signature fails with a tefBAD_QUORUM.
|
||||||
|
|
||||||
|
|
||||||
|
By making the signer weights 16 bits and the quorum value 32 bits we avoid
|
||||||
|
concerns about overflows and still have plenty of resolution.
|
||||||
|
|
||||||
|
This transactor allows two operations:
|
||||||
|
|
||||||
|
- Create (or replace) a signer list for the target account.
|
||||||
|
- Remove any signer list from the target account.
|
||||||
|
|
||||||
|
The data for a transaction creating or replacing a signer list has this
|
||||||
|
general form:
|
||||||
|
|
||||||
|
{
|
||||||
|
"TransactionType": "SignerListSet",
|
||||||
|
"Account": "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||||
|
"SignerQuorum": 7,
|
||||||
|
"SignerEntries": [
|
||||||
|
{
|
||||||
|
"SignerEntry": {
|
||||||
|
"Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
|
||||||
|
"SignerWeight": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SignerEntry": {
|
||||||
|
"Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
|
||||||
|
"SignerWeight": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
The data for a transaction that removes any signer list has this form:
|
||||||
|
|
||||||
|
{
|
||||||
|
"TransactionType": "SignerListSet",
|
||||||
|
"Account": "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||||
|
"SignerQuorum": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
## SetTrust ##
|
||||||
|
|
||||||
|
## Tickets ##
|
||||||
|
|
||||||
|
**Associated JIRA task is [RIPD-368](https://ripplelabs.atlassian.net/browse/RIPD-368)**
|
||||||
|
|
||||||
|
Currently transactions on the Ripple network require the use of sequence
|
||||||
|
numbers and sequence numbers must monotonically increase. Since the sequence
|
||||||
|
number is part of the transaction, it is "covered" by the signature that
|
||||||
|
authorizes the transaction, which means that the sequence number would have
|
||||||
|
to be decided at the time of transaction issuance. This would mean that
|
||||||
|
multi-signature transactions could only be processed in strict "first-in,
|
||||||
|
first-out" order which is not practical.
|
||||||
|
|
||||||
|
Tickets can be used in lieu of sequence number. A ticket is a special token
|
||||||
|
which, through a transaction, can be issued by any account and can be
|
||||||
|
configured with an optional expiry date and an optional associated account.
|
||||||
|
|
||||||
|
### Specifics ###
|
||||||
|
|
||||||
|
The expiry date can be used to constrain the validity of the ticket. If
|
||||||
|
specified, the ticket will be considered invalid and unusable if the closing
|
||||||
|
time of the last *validated* ledger is greater than or equal to the expiration
|
||||||
|
time of the ticket.
|
||||||
|
|
||||||
|
The associated account can be used to specify an account, other than the
|
||||||
|
issuing account, that is allowed to "consume" the ticket. Consuming a ticket
|
||||||
|
means to use the ticket in a transaction that is accepted by the network and
|
||||||
|
makes its way into a validated ledger. If not present, the ticket can only be
|
||||||
|
consumed by the issuing account.
|
||||||
|
|
||||||
|
*Corner Case:* It is possible that two or more transactions reference the same
|
||||||
|
ticket and that both go into the same consensus set. During final application
|
||||||
|
of transactions from the consensus set at most one of these transactions may
|
||||||
|
succeed; others must fail with the indication that the ticket has been consumed.
|
||||||
|
|
||||||
|
*Reserve:* While a ticket is outstanding, it should count against the reserve
|
||||||
|
of the *issuer*.
|
||||||
|
|
||||||
|
##### Issuance
|
||||||
|
We should decide whether, in the case of multi-signature accounts, any single
|
||||||
|
authorized signer can issue a ticket on the multi-signature accounts' behalf.
|
||||||
|
This approach has both advantages and disadvantages.
|
||||||
|
|
||||||
|
Advantages include:
|
||||||
|
|
||||||
|
+ Simpler logic for tickets and reduced data - no need to store or consider an associated account.
|
||||||
|
+ Owner reserves for issued tickets count against the multi-signature account instead of the account of the signer proposing a transaction.
|
||||||
|
+ Cleaner meta-data: easier to follow who issued a ticket and how many tickets are outstanding and associated with a particular account.
|
||||||
|
|
||||||
|
Disadvantages are:
|
||||||
|
|
||||||
|
+ Any single authorized signer can issue multiple tickets, each counting against the account's reserve.
|
||||||
|
+ Special-case logic for authorizing tickets on multi-sign accounts.
|
||||||
|
|
||||||
|
I believe that the disadvantages outweigh the advantages, but debate is welcome.
|
||||||
|
|
||||||
|
|
||||||
|
### CreateTicket ###
|
||||||
|
|
||||||
|
### CancelTicket ###
|
||||||
363
src/ripple/app/tx/impl/SetSignerList.cpp
Normal file
363
src/ripple/app/tx/impl/SetSignerList.cpp
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2014 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/app/tx/impl/Transactor.h>
|
||||||
|
#include <ripple/app/tx/impl/SignerEntries.h>
|
||||||
|
#include <ripple/protocol/STObject.h>
|
||||||
|
#include <ripple/protocol/STArray.h>
|
||||||
|
#include <ripple/protocol/STTx.h>
|
||||||
|
#include <ripple/protocol/STAccount.h>
|
||||||
|
#include <ripple/protocol/Indexes.h>
|
||||||
|
#include <ripple/basics/Log.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
/**
|
||||||
|
See the README.md for an overview of the SetSignerList transaction that
|
||||||
|
this class implements.
|
||||||
|
*/
|
||||||
|
class SetSignerList final
|
||||||
|
: public Transactor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Values determined during preCheck for use later.
|
||||||
|
enum Operation {unknown, set, destroy};
|
||||||
|
Operation do_ {unknown};
|
||||||
|
std::uint32_t quorum_ {0};
|
||||||
|
std::vector<SignerEntries::SignerEntry> signers_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SetSignerList (
|
||||||
|
STTx const& txn,
|
||||||
|
TransactionEngineParams params,
|
||||||
|
TransactionEngine* engine)
|
||||||
|
: Transactor (
|
||||||
|
txn,
|
||||||
|
params,
|
||||||
|
engine,
|
||||||
|
deprecatedLogs().journal("SetSignerList"))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Applies the transaction if it is well formed and the ledger state permits.
|
||||||
|
*/
|
||||||
|
TER doApply () override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
Check anything that can be checked without the ledger.
|
||||||
|
*/
|
||||||
|
TER preCheck () override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// signers are not const because method (intentionally) sorts vector.
|
||||||
|
TER validateQuorumAndSignerEntries (
|
||||||
|
std::uint32_t quorum,
|
||||||
|
std::vector<SignerEntries::SignerEntry>& signers) const;
|
||||||
|
|
||||||
|
// Methods called by doApply()
|
||||||
|
TER replaceSignerList (uint256 const& index);
|
||||||
|
TER destroySignerList (uint256 const& index);
|
||||||
|
|
||||||
|
void writeSignersToLedger (SLE::pointer ledgerEntry);
|
||||||
|
|
||||||
|
static std::size_t ownerCountDelta (std::size_t entryCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TER
|
||||||
|
SetSignerList::doApply ()
|
||||||
|
{
|
||||||
|
assert (mTxnAccount);
|
||||||
|
|
||||||
|
// All operations require our ledger index. Compute that once and pass it
|
||||||
|
// to our handlers.
|
||||||
|
uint256 const index = getSignerListIndex (mTxnAccountID);
|
||||||
|
|
||||||
|
// Perform the operation preCheck() decided on.
|
||||||
|
switch (do_)
|
||||||
|
{
|
||||||
|
case set:
|
||||||
|
return replaceSignerList (index);
|
||||||
|
|
||||||
|
case destroy:
|
||||||
|
return destroySignerList (index);
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Fall through intentionally
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert (false); // Should not be possible to get here.
|
||||||
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
SetSignerList::preCheck()
|
||||||
|
{
|
||||||
|
// We need the account ID later, so do this check first.
|
||||||
|
preCheckAccount ();
|
||||||
|
|
||||||
|
// Check the quorum. A non-zero quorum means we're creating or replacing
|
||||||
|
// the list. A zero quorum means we're destroying the list.
|
||||||
|
quorum_ = (mTxn.getFieldU32 (sfSignerQuorum));
|
||||||
|
|
||||||
|
bool const hasSignerEntries (mTxn.isFieldPresent (sfSignerEntries));
|
||||||
|
if (quorum_ && hasSignerEntries)
|
||||||
|
{
|
||||||
|
SignerEntries::Decoded signers (
|
||||||
|
SignerEntries::deserialize (mTxn, m_journal, "transaction"));
|
||||||
|
|
||||||
|
if (signers.ter != tesSUCCESS)
|
||||||
|
return signers.ter;
|
||||||
|
|
||||||
|
// Validate our settings.
|
||||||
|
if (TER const ter =
|
||||||
|
validateQuorumAndSignerEntries (quorum_, signers.vec))
|
||||||
|
{
|
||||||
|
return ter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save deserialized and validated list for later.
|
||||||
|
signers_ = std::move (signers.vec);
|
||||||
|
do_ = set;
|
||||||
|
}
|
||||||
|
else if ((quorum_ == 0) && !hasSignerEntries)
|
||||||
|
{
|
||||||
|
do_ = destroy;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Neither a set nor a destroy. Malformed.
|
||||||
|
if (m_journal.trace) m_journal.trace <<
|
||||||
|
"Malformed transaction: Invalid signer set list format.";
|
||||||
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return preCheckSigningKey ();
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
SetSignerList::validateQuorumAndSignerEntries (
|
||||||
|
std::uint32_t quorum,
|
||||||
|
std::vector<SignerEntries::SignerEntry>& signers) const
|
||||||
|
{
|
||||||
|
// Reject if there are too many or too few entries in the list.
|
||||||
|
{
|
||||||
|
std::size_t const signerCount = signers.size ();
|
||||||
|
if ((signerCount < SignerEntries::minEntries)
|
||||||
|
|| (signerCount > SignerEntries::maxEntries))
|
||||||
|
{
|
||||||
|
if (m_journal.trace) m_journal.trace <<
|
||||||
|
"Too many or too few signers in signer list.";
|
||||||
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure there are no duplicate signers.
|
||||||
|
std::sort (signers.begin (), signers.end ());
|
||||||
|
if (std::adjacent_find (
|
||||||
|
signers.begin (), signers.end ()) != signers.end ())
|
||||||
|
{
|
||||||
|
if (m_journal.trace) m_journal.trace <<
|
||||||
|
"Duplicate signers in signer list";
|
||||||
|
return temBAD_SIGNER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure no signers reference this account. Also make sure the
|
||||||
|
// quorum can be reached.
|
||||||
|
std::uint64_t allSignersWeight (0);
|
||||||
|
for (auto const& signer : signers)
|
||||||
|
{
|
||||||
|
allSignersWeight += signer.weight;
|
||||||
|
|
||||||
|
if (signer.account == mTxnAccountID)
|
||||||
|
{
|
||||||
|
if (m_journal.trace) m_journal.trace <<
|
||||||
|
"A signer may not self reference account.";
|
||||||
|
return temBAD_SIGNER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't verify that the signer accounts exist. Non-existent accounts
|
||||||
|
// may be phantom accounts (which are permitted).
|
||||||
|
}
|
||||||
|
if ((quorum <= 0) || (allSignersWeight < quorum))
|
||||||
|
{
|
||||||
|
if (m_journal.trace) m_journal.trace <<
|
||||||
|
"Quorum is unreachable";
|
||||||
|
return temBAD_QUORUM;
|
||||||
|
}
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
SetSignerList::replaceSignerList (uint256 const& index)
|
||||||
|
{
|
||||||
|
// This may be either a create or a replace. Preemptively destroy any
|
||||||
|
// old signer list. May reduce the reserve, so this is done before
|
||||||
|
// checking the reserve.
|
||||||
|
if (TER const ter = destroySignerList (index))
|
||||||
|
return ter;
|
||||||
|
|
||||||
|
// Compute new reserve. Verify the account has funds to meet the reserve.
|
||||||
|
std::size_t const oldOwnerCount = mTxnAccount->getFieldU32 (sfOwnerCount);
|
||||||
|
std::size_t const addedOwnerCount = ownerCountDelta (signers_.size ());
|
||||||
|
|
||||||
|
std::uint64_t const newReserve =
|
||||||
|
mEngine->getLedger ()->getReserve (oldOwnerCount + addedOwnerCount);
|
||||||
|
|
||||||
|
// We check the reserve against the starting balance because we want to
|
||||||
|
// allow dipping into the reserve to pay fees. This behavior is consistent
|
||||||
|
// with CreateTicket.
|
||||||
|
if (mPriorBalance < newReserve)
|
||||||
|
return tecINSUFFICIENT_RESERVE;
|
||||||
|
|
||||||
|
// Everything's ducky. Add the ltSIGNER_LIST to the ledger.
|
||||||
|
SLE::pointer signerList (
|
||||||
|
mEngine->view().entryCreate (ltSIGNER_LIST, index));
|
||||||
|
writeSignersToLedger (signerList);
|
||||||
|
|
||||||
|
// Lambda for call to dirAdd.
|
||||||
|
auto describer = [&] (SLE::ref sle, bool dummy)
|
||||||
|
{
|
||||||
|
Ledger::ownerDirDescriber (sle, dummy, mTxnAccountID);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the signer list to the account's directory.
|
||||||
|
std::uint64_t hint;
|
||||||
|
TER result = mEngine->view ().dirAdd (
|
||||||
|
hint, getOwnerDirIndex (mTxnAccountID), index, describer);
|
||||||
|
|
||||||
|
if (m_journal.trace) m_journal.trace <<
|
||||||
|
"Create signer list for account " <<
|
||||||
|
mTxnAccountID << ": " << transHuman (result);
|
||||||
|
|
||||||
|
if (result != tesSUCCESS)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
signerList->setFieldU64 (sfOwnerNode, hint);
|
||||||
|
|
||||||
|
// If we succeeded, the new entry counts against the creator's reserve.
|
||||||
|
mEngine->view ().increaseOwnerCount (mTxnAccount, addedOwnerCount);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
SetSignerList::destroySignerList (uint256 const& index)
|
||||||
|
{
|
||||||
|
// See if there's an ltSIGNER_LIST for this account.
|
||||||
|
SLE::pointer signerList =
|
||||||
|
mEngine->view ().entryCache (ltSIGNER_LIST, index);
|
||||||
|
|
||||||
|
// If the signer list doesn't exist we've already succeeded in deleting it.
|
||||||
|
if (!signerList)
|
||||||
|
return tesSUCCESS;
|
||||||
|
|
||||||
|
// We have to examine the current SignerList so we know how much to
|
||||||
|
// reduce the OwnerCount.
|
||||||
|
std::size_t removeFromOwnerCount = 0;
|
||||||
|
uint256 const signerListIndex = getSignerListIndex (mTxnAccountID);
|
||||||
|
SLE::pointer accountSignersList =
|
||||||
|
mEngine->view ().entryCache (ltSIGNER_LIST, signerListIndex);
|
||||||
|
if (accountSignersList)
|
||||||
|
{
|
||||||
|
STArray const& actualList =
|
||||||
|
accountSignersList->getFieldArray (sfSignerEntries);
|
||||||
|
removeFromOwnerCount = ownerCountDelta (actualList.size ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the node from the account directory.
|
||||||
|
std::uint64_t const hint (signerList->getFieldU64 (sfOwnerNode));
|
||||||
|
|
||||||
|
TER const result = mEngine->view ().dirDelete (false, hint,
|
||||||
|
getOwnerDirIndex (mTxnAccountID), index, false, (hint == 0));
|
||||||
|
|
||||||
|
if (result == tesSUCCESS)
|
||||||
|
mEngine->view ().decreaseOwnerCount (mTxnAccount, removeFromOwnerCount);
|
||||||
|
|
||||||
|
mEngine->view ().entryDelete (signerList);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetSignerList::writeSignersToLedger (SLE::pointer ledgerEntry)
|
||||||
|
{
|
||||||
|
// Assign the quorum.
|
||||||
|
ledgerEntry->setFieldU32 (sfSignerQuorum, quorum_);
|
||||||
|
|
||||||
|
// Create the SignerListArray one SignerEntry at a time.
|
||||||
|
STArray toLedger (signers_.size ());
|
||||||
|
for (auto const& entry : signers_)
|
||||||
|
{
|
||||||
|
toLedger.emplace_back(sfSignerEntry);
|
||||||
|
STObject& obj = toLedger.back();
|
||||||
|
obj.reserve (2);
|
||||||
|
obj.setFieldAccount (sfAccount, entry.account);
|
||||||
|
obj.setFieldU16 (sfSignerWeight, entry.weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the SignerEntries.
|
||||||
|
ledgerEntry->setFieldArray (sfSignerEntries, toLedger);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
SetSignerList::ownerCountDelta (std::size_t entryCount)
|
||||||
|
{
|
||||||
|
// We always compute the full change in OwnerCount, taking into account:
|
||||||
|
// o The fact that we're adding/removing a SignerList and
|
||||||
|
// o Accounting for the number of entries in the list.
|
||||||
|
// We can get away with that because lists are not adjusted incrementally;
|
||||||
|
// we add or remove an entire list.
|
||||||
|
|
||||||
|
// The wiki (https://wiki.ripple.com/Multisign#Fees_2) currently says
|
||||||
|
// (December 2014) the reserve should be
|
||||||
|
// Reserve * (N + 1) / 2
|
||||||
|
// That's not making sense to me right now, since I'm working in
|
||||||
|
// integral OwnerCount units. If, say, N is 4 I don't know how to return
|
||||||
|
// 4.5 units as an integer.
|
||||||
|
//
|
||||||
|
// So, just to get started, I'm saying that:
|
||||||
|
// o Simply having a SignerList costs 2 OwnerCount units.
|
||||||
|
// o And each signer in the list costs 1 more OwnerCount unit.
|
||||||
|
// So, at a minimum, adding a SignerList with 2 entries costs 4 OwnerCount
|
||||||
|
// units. A SignerList with 8 entries would cost 10 OwnerCount units.
|
||||||
|
//
|
||||||
|
// It's worth noting that once this reserve policy has gotten into the
|
||||||
|
// wild it will be very difficult to change. So think hard about what
|
||||||
|
// we want for the long term.
|
||||||
|
return 2 + entryCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
transact_SetSignerList (
|
||||||
|
STTx const& txn,
|
||||||
|
TransactionEngineParams params,
|
||||||
|
TransactionEngine* engine)
|
||||||
|
{
|
||||||
|
return SetSignerList (txn, params, engine).apply ();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
67
src/ripple/app/tx/impl/SignerEntries.cpp
Normal file
67
src/ripple/app/tx/impl/SignerEntries.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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/app/tx/impl/SignerEntries.h>
|
||||||
|
#include <ripple/protocol/STObject.h>
|
||||||
|
#include <ripple/protocol/STArray.h>
|
||||||
|
#include <ripple/protocol/STAccount.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
SignerEntries::Decoded
|
||||||
|
SignerEntries::deserialize (
|
||||||
|
STObject const& obj, beast::Journal journal, std::string const& annotation)
|
||||||
|
{
|
||||||
|
Decoded s;
|
||||||
|
auto& accountVec (s.vec);
|
||||||
|
accountVec.reserve (maxEntries);
|
||||||
|
|
||||||
|
if (!obj.isFieldPresent (sfSignerEntries))
|
||||||
|
{
|
||||||
|
if (journal.trace) journal.trace <<
|
||||||
|
"Malformed " << annotation << ": Need signer entry array.";
|
||||||
|
s.ter = temMALFORMED;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
STArray const& sEntries (obj.getFieldArray (sfSignerEntries));
|
||||||
|
for (STObject const& sEntry : sEntries)
|
||||||
|
{
|
||||||
|
// Validate the SignerEntry.
|
||||||
|
if (sEntry.getFName () != sfSignerEntry)
|
||||||
|
{
|
||||||
|
journal.trace <<
|
||||||
|
"Malformed " << annotation << ": Expected SignerEntry.";
|
||||||
|
s.ter = temMALFORMED;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract SignerEntry fields.
|
||||||
|
Account const account = sEntry.getFieldAccount160 (sfAccount);
|
||||||
|
std::uint16_t const weight = sEntry.getFieldU16 (sfSignerWeight);
|
||||||
|
accountVec.emplace_back (account, weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ter = tesSUCCESS;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // ripple
|
||||||
77
src/ripple/app/tx/impl/SignerEntries.h
Normal file
77
src/ripple/app/tx/impl/SignerEntries.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_TX_IMPL_SIGNER_ENTRIES_H_INCLUDED
|
||||||
|
#define RIPPLE_TX_IMPL_SIGNER_ENTRIES_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/protocol/STTx.h> // STTx::maxMultiSigners
|
||||||
|
#include <ripple/protocol/UintTypes.h> // Account
|
||||||
|
#include <ripple/protocol/TER.h> // temMALFORMED
|
||||||
|
#include <beast/utility/Journal.h> // beast::Journal
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
class STObject;
|
||||||
|
|
||||||
|
// Support for SignerEntries that is needed by a few Transactors
|
||||||
|
class SignerEntries
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct SignerEntry
|
||||||
|
{
|
||||||
|
Account account;
|
||||||
|
std::uint16_t weight;
|
||||||
|
|
||||||
|
SignerEntry (Account const& inAccount, std::uint16_t inWeight)
|
||||||
|
: account (inAccount)
|
||||||
|
, weight (inWeight)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// For sorting to look for duplicate accounts
|
||||||
|
friend bool operator< (SignerEntry const& lhs, SignerEntry const& rhs)
|
||||||
|
{
|
||||||
|
return lhs.account < rhs.account;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator== (SignerEntry const& lhs, SignerEntry const& rhs)
|
||||||
|
{
|
||||||
|
return lhs.account == rhs.account;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Decoded
|
||||||
|
{
|
||||||
|
std::vector<SignerEntry> vec;
|
||||||
|
TER ter = temMALFORMED;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deserialize a SignerEntries array from the network or from the ledger.
|
||||||
|
static Decoded deserialize (
|
||||||
|
STObject const& obj,
|
||||||
|
beast::Journal journal,
|
||||||
|
std::string const& annotation);
|
||||||
|
|
||||||
|
static std::size_t const minEntries = 2;
|
||||||
|
static std::size_t const maxEntries = STTx::maxMultiSigners;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // ripple
|
||||||
|
|
||||||
|
#endif // RIPPLE_TX_IMPL_SIGNER_ENTRIES_H_INCLUDED
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
#include <ripple/core/Config.h>
|
|
||||||
#include <ripple/app/tx/impl/Transactor.h>
|
#include <ripple/app/tx/impl/Transactor.h>
|
||||||
|
#include <ripple/core/Config.h>
|
||||||
#include <ripple/protocol/Indexes.h>
|
#include <ripple/protocol/Indexes.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
@@ -33,6 +33,7 @@ TER transact_CancelOffer (STTx const& txn, TransactionEngineParams params, Trans
|
|||||||
TER transact_Change (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
TER transact_Change (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||||
TER transact_CreateTicket (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
TER transact_CreateTicket (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||||
TER transact_CancelTicket (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
TER transact_CancelTicket (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||||
|
TER transact_SetSignerList (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||||
|
|
||||||
TER
|
TER
|
||||||
Transactor::transact (
|
Transactor::transact (
|
||||||
@@ -70,6 +71,13 @@ Transactor::transact (
|
|||||||
case ttTICKET_CANCEL:
|
case ttTICKET_CANCEL:
|
||||||
return transact_CancelTicket (txn, params, engine);
|
return transact_CancelTicket (txn, params, engine);
|
||||||
|
|
||||||
|
#if RIPPLE_ENABLE_MULTI_SIGN
|
||||||
|
|
||||||
|
case ttSIGNER_LIST_SET:
|
||||||
|
return transact_SetSignerList (txn, params, engine);
|
||||||
|
|
||||||
|
#endif // RIPPLE_ENABLE_MULTI_SIGN
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return temUNKNOWN;
|
return temUNKNOWN;
|
||||||
}
|
}
|
||||||
@@ -219,6 +227,15 @@ TER Transactor::checkSeq ()
|
|||||||
|
|
||||||
// check stuff before you bother to lock the ledger
|
// check stuff before you bother to lock the ledger
|
||||||
TER Transactor::preCheck ()
|
TER Transactor::preCheck ()
|
||||||
|
{
|
||||||
|
TER result = preCheckAccount ();
|
||||||
|
if (result != tesSUCCESS)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
return preCheckSigningKey ();
|
||||||
|
}
|
||||||
|
|
||||||
|
TER Transactor::preCheckAccount ()
|
||||||
{
|
{
|
||||||
mTxnAccountID = mTxn.getSourceAccount ().getAccountID ();
|
mTxnAccountID = mTxn.getSourceAccount ().getAccountID ();
|
||||||
|
|
||||||
@@ -227,14 +244,19 @@ TER Transactor::preCheck ()
|
|||||||
m_journal.warning << "applyTransaction: bad transaction source id";
|
m_journal.warning << "applyTransaction: bad transaction source id";
|
||||||
return temBAD_SRC_ACCOUNT;
|
return temBAD_SRC_ACCOUNT;
|
||||||
}
|
}
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
TER Transactor::preCheckSigningKey ()
|
||||||
|
{
|
||||||
// Extract signing key
|
// Extract signing key
|
||||||
// Transactions contain a signing key. This allows us to trivially verify a
|
// Transactions contain a signing key. This allows us to trivially verify a
|
||||||
// transaction has at least been properly signed without going to disk.
|
// transaction has at least been properly signed without going to disk.
|
||||||
// Each transaction also notes a source account id. This is used to verify
|
// Each transaction also notes a source account id. This is used to verify
|
||||||
// that the signing key is associated with the account.
|
// that the signing key is associated with the account.
|
||||||
// XXX This could be a lot cleaner to prevent unnecessary copying.
|
// XXX This could be a lot cleaner to prevent unnecessary copying.
|
||||||
mSigningPubKey = RippleAddress::createAccountPublic (mTxn.getSigningPubKey ());
|
mSigningPubKey =
|
||||||
|
RippleAddress::createAccountPublic (mTxn.getSigningPubKey ());
|
||||||
|
|
||||||
// Consistency: really signed.
|
// Consistency: really signed.
|
||||||
if (!mTxn.isKnownGood ())
|
if (!mTxn.isKnownGood ())
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ protected:
|
|||||||
beast::Journal m_journal;
|
beast::Journal m_journal;
|
||||||
|
|
||||||
virtual TER preCheck ();
|
virtual TER preCheck ();
|
||||||
|
|
||||||
|
// Non-virtual components of preCheck()
|
||||||
|
TER preCheckAccount ();
|
||||||
|
TER preCheckSigningKey ();
|
||||||
|
|
||||||
virtual TER checkSeq ();
|
virtual TER checkSeq ();
|
||||||
virtual TER payFee ();
|
virtual TER payFee ();
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ getRippleStateIndex (Account const& a, Account const& b, Currency const& currenc
|
|||||||
uint256
|
uint256
|
||||||
getRippleStateIndex (Account const& a, Issue const& issue);
|
getRippleStateIndex (Account const& a, Issue const& issue);
|
||||||
|
|
||||||
|
uint256
|
||||||
|
getSignerListIndex (Account const& account);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
29
src/ripple/unity/app9.cpp → src/ripple/protocol/InnerObjectFormats.h
Normal file → Executable file
29
src/ripple/unity/app9.cpp → src/ripple/protocol/InnerObjectFormats.h
Normal file → Executable file
@@ -17,4 +17,31 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <BeastConfig.h>
|
#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',
|
ltTICKET = 'T',
|
||||||
|
|
||||||
|
ltSIGNER_LIST = 'S',
|
||||||
|
|
||||||
/* Deprecated. */
|
/* Deprecated. */
|
||||||
ltOFFER = 'o',
|
ltOFFER = 'o',
|
||||||
|
|
||||||
@@ -90,6 +92,7 @@ enum LedgerNameSpace
|
|||||||
spaceAmendment = 'f',
|
spaceAmendment = 'f',
|
||||||
spaceFee = 'e',
|
spaceFee = 'e',
|
||||||
spaceTicket = 'T',
|
spaceTicket = 'T',
|
||||||
|
spaceSignerList = 'S',
|
||||||
|
|
||||||
// No longer used or supported. Left here to reserve the space and
|
// No longer used or supported. Left here to reserve the space and
|
||||||
// avoid accidental reuse of the space.
|
// avoid accidental reuse of the space.
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ extern SField const sfTransactionResult;
|
|||||||
// 16-bit integers
|
// 16-bit integers
|
||||||
extern SField const sfLedgerEntryType;
|
extern SField const sfLedgerEntryType;
|
||||||
extern SField const sfTransactionType;
|
extern SField const sfTransactionType;
|
||||||
|
extern SField const sfSignerWeight;
|
||||||
|
|
||||||
// 32-bit integers (common)
|
// 32-bit integers (common)
|
||||||
extern SField const sfFlags;
|
extern SField const sfFlags;
|
||||||
@@ -331,6 +332,7 @@ extern SField const sfReserveBase;
|
|||||||
extern SField const sfReserveIncrement;
|
extern SField const sfReserveIncrement;
|
||||||
extern SField const sfSetFlag;
|
extern SField const sfSetFlag;
|
||||||
extern SField const sfClearFlag;
|
extern SField const sfClearFlag;
|
||||||
|
extern SField const sfSignerQuorum;
|
||||||
|
|
||||||
// 64-bit integers
|
// 64-bit integers
|
||||||
extern SField const sfIndexNext;
|
extern SField const sfIndexNext;
|
||||||
@@ -427,12 +429,13 @@ extern SField const sfFinalFields;
|
|||||||
extern SField const sfNewFields;
|
extern SField const sfNewFields;
|
||||||
extern SField const sfTemplateEntry;
|
extern SField const sfTemplateEntry;
|
||||||
extern SField const sfMemo;
|
extern SField const sfMemo;
|
||||||
|
extern SField const sfSignerEntry;
|
||||||
|
|
||||||
// array of objects
|
// array of objects
|
||||||
// ARRAY/1 is reserved for end of array
|
// ARRAY/1 is reserved for end of array
|
||||||
extern SField const sfSigningAccounts;
|
extern SField const sfSigningAccounts;
|
||||||
extern SField const sfTxnSignatures;
|
extern SField const sfTxnSignatures;
|
||||||
extern SField const sfSignatures;
|
extern SField const sfSignerEntries;
|
||||||
extern SField const sfTemplate;
|
extern SField const sfTemplate;
|
||||||
extern SField const sfNecessary;
|
extern SField const sfNecessary;
|
||||||
extern SField const sfSufficient;
|
extern SField const sfSufficient;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ private:
|
|||||||
list_type v_;
|
list_type v_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Read-only iteration
|
// Read-only iteration
|
||||||
class Items;
|
class Items;
|
||||||
|
|
||||||
static char const* getCountedObjectName () { return "STArray"; }
|
static char const* getCountedObjectName () { return "STArray"; }
|
||||||
@@ -135,6 +135,10 @@ public:
|
|||||||
{
|
{
|
||||||
v_.clear ();
|
v_.clear ();
|
||||||
}
|
}
|
||||||
|
void reserve (std::size_t n)
|
||||||
|
{
|
||||||
|
v_.reserve (n);
|
||||||
|
}
|
||||||
void swap (STArray & a) noexcept
|
void swap (STArray & a) noexcept
|
||||||
{
|
{
|
||||||
v_.swap (a.v_);
|
v_.swap (a.v_);
|
||||||
|
|||||||
@@ -133,7 +133,18 @@ public:
|
|||||||
return v_.empty();
|
return v_.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reserve (std::size_t n)
|
||||||
|
{
|
||||||
|
v_.reserve (n);
|
||||||
|
}
|
||||||
|
|
||||||
bool setType (const SOTemplate & type);
|
bool setType (const SOTemplate & type);
|
||||||
|
|
||||||
|
enum ResultOfSetTypeFromSField : unsigned char
|
||||||
|
{typeSetFail, typeIsSet, noTemplate};
|
||||||
|
|
||||||
|
ResultOfSetTypeFromSField setTypeFromSField (SField const&);
|
||||||
|
|
||||||
bool isValidForType ();
|
bool isValidForType ();
|
||||||
bool isFieldAllowed (SField const&);
|
bool isFieldAllowed (SField const&);
|
||||||
bool isFree () const
|
bool isFree () const
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ public:
|
|||||||
typedef std::shared_ptr<STTx> pointer;
|
typedef std::shared_ptr<STTx> pointer;
|
||||||
typedef const std::shared_ptr<STTx>& ref;
|
typedef const std::shared_ptr<STTx>& ref;
|
||||||
|
|
||||||
|
static std::size_t const maxMultiSigners = 8;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
STTx () = delete;
|
STTx () = delete;
|
||||||
STTx& operator= (STTx const& other) = delete;
|
STTx& operator= (STTx const& other) = delete;
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ enum TER // aka TransactionEngineResult
|
|||||||
temREDUNDANT,
|
temREDUNDANT,
|
||||||
temRIPPLE_EMPTY,
|
temRIPPLE_EMPTY,
|
||||||
temDISABLED,
|
temDISABLED,
|
||||||
|
temBAD_SIGNER,
|
||||||
|
temBAD_QUORUM,
|
||||||
|
|
||||||
// An intermediate result used internally, should never be returned.
|
// An intermediate result used internally, should never be returned.
|
||||||
temUNCERTAIN,
|
temUNCERTAIN,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ enum TxType
|
|||||||
no_longer_used = 9,
|
no_longer_used = 9,
|
||||||
ttTICKET_CREATE = 10,
|
ttTICKET_CREATE = 10,
|
||||||
ttTICKET_CANCEL = 11,
|
ttTICKET_CANCEL = 11,
|
||||||
|
ttSIGNER_LIST_SET = 12,
|
||||||
|
|
||||||
ttTRUST_SET = 20,
|
ttTRUST_SET = 20,
|
||||||
|
|
||||||
|
|||||||
@@ -219,4 +219,15 @@ getRippleStateIndex (Account const& a, Issue const& issue)
|
|||||||
return getRippleStateIndex (a, issue.account, issue.currency);
|
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 (sfTarget, SOE_OPTIONAL)
|
||||||
<< SOElement (sfExpiration, 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)
|
void LedgerFormats::addCommonFields (Item& item)
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ SField const sfTransactionResult = make::one(&sfTransactionResult, STI_UINT8, 3,
|
|||||||
// 16-bit integers
|
// 16-bit integers
|
||||||
SField const sfLedgerEntryType = make::one(&sfLedgerEntryType, STI_UINT16, 1, "LedgerEntryType", SField::sMD_Never);
|
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 sfTransactionType = make::one(&sfTransactionType, STI_UINT16, 2, "TransactionType");
|
||||||
|
SField const sfSignerWeight = make::one(&sfSignerWeight, STI_UINT16, 3, "SignerWeight");
|
||||||
|
|
||||||
// 32-bit integers (common)
|
// 32-bit integers (common)
|
||||||
SField const sfFlags = make::one(&sfFlags, STI_UINT32, 2, "Flags");
|
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 sfReserveIncrement = make::one(&sfReserveIncrement, STI_UINT32, 32, "ReserveIncrement");
|
||||||
SField const sfSetFlag = make::one(&sfSetFlag, STI_UINT32, 33, "SetFlag");
|
SField const sfSetFlag = make::one(&sfSetFlag, STI_UINT32, 33, "SetFlag");
|
||||||
SField const sfClearFlag = make::one(&sfClearFlag, STI_UINT32, 34, "ClearFlag");
|
SField const sfClearFlag = make::one(&sfClearFlag, STI_UINT32, 34, "ClearFlag");
|
||||||
|
SField const sfSignerQuorum = make::one(&sfSignerQuorum, STI_UINT32, 35, "SignerQuorum");
|
||||||
|
|
||||||
// 64-bit integers
|
// 64-bit integers
|
||||||
SField const sfIndexNext = make::one(&sfIndexNext, STI_UINT64, 1, "IndexNext");
|
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 sfNewFields = make::one(&sfNewFields, STI_OBJECT, 8, "NewFields");
|
||||||
SField const sfTemplateEntry = make::one(&sfTemplateEntry, STI_OBJECT, 9, "TemplateEntry");
|
SField const sfTemplateEntry = make::one(&sfTemplateEntry, STI_OBJECT, 9, "TemplateEntry");
|
||||||
SField const sfMemo = make::one(&sfMemo, STI_OBJECT, 10, "Memo");
|
SField const sfMemo = make::one(&sfMemo, STI_OBJECT, 10, "Memo");
|
||||||
|
SField const sfSignerEntry = make::one(&sfSignerEntry, STI_OBJECT, 11, "SignerEntry");
|
||||||
|
|
||||||
// array of objects
|
// array of objects
|
||||||
// ARRAY/1 is reserved for end of array
|
// ARRAY/1 is reserved for end of array
|
||||||
SField const sfSigningAccounts = make::one(&sfSigningAccounts, STI_ARRAY, 2, "SigningAccounts");
|
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 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 sfTemplate = make::one(&sfTemplate, STI_ARRAY, 5, "Template");
|
||||||
SField const sfNecessary = make::one(&sfNecessary, STI_ARRAY, 6, "Necessary");
|
SField const sfNecessary = make::one(&sfNecessary, STI_ARRAY, 6, "Necessary");
|
||||||
SField const sfSufficient = make::one(&sfSufficient, STI_ARRAY, 7, "Sufficient");
|
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_.emplace_back(fn);
|
||||||
v_.back().set (sit, 1);
|
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/basics/Log.h>
|
||||||
#include <ripple/json/json_reader.h>
|
#include <ripple/json/json_reader.h>
|
||||||
#include <ripple/json/to_string.h>
|
#include <ripple/json/to_string.h>
|
||||||
|
#include <ripple/protocol/InnerObjectFormats.h>
|
||||||
#include <ripple/protocol/STBase.h>
|
#include <ripple/protocol/STBase.h>
|
||||||
#include <ripple/protocol/STAccount.h>
|
#include <ripple/protocol/STAccount.h>
|
||||||
#include <ripple/protocol/STArray.h>
|
#include <ripple/protocol/STArray.h>
|
||||||
@@ -119,7 +120,7 @@ bool STObject::setType (const SOTemplate& type)
|
|||||||
{
|
{
|
||||||
WriteLog (lsWARNING, STObject) <<
|
WriteLog (lsWARNING, STObject) <<
|
||||||
"setType( " << getFName ().getName () <<
|
"setType( " << getFName ().getName () <<
|
||||||
") invalid default " << e->e_field.fieldName;
|
" ) invalid default " << e->e_field.fieldName;
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
v.emplace_back(std::move(*iter));
|
v.emplace_back(std::move(*iter));
|
||||||
@@ -131,7 +132,7 @@ bool STObject::setType (const SOTemplate& type)
|
|||||||
{
|
{
|
||||||
WriteLog (lsWARNING, STObject) <<
|
WriteLog (lsWARNING, STObject) <<
|
||||||
"setType( " << getFName ().getName () <<
|
"setType( " << getFName ().getName () <<
|
||||||
") invalid missing " << e->e_field.fieldName;
|
" ) invalid missing " << e->e_field.fieldName;
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
v.emplace_back(detail::nonPresentObject, e->e_field);
|
v.emplace_back(detail::nonPresentObject, e->e_field);
|
||||||
@@ -144,7 +145,7 @@ bool STObject::setType (const SOTemplate& type)
|
|||||||
{
|
{
|
||||||
WriteLog (lsWARNING, STObject) <<
|
WriteLog (lsWARNING, STObject) <<
|
||||||
"setType( " << getFName ().getName () <<
|
"setType( " << getFName ().getName () <<
|
||||||
") invalid leftover " << e->getFName ().getName ();
|
" ) invalid leftover " << e->getFName ().getName ();
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,6 +155,20 @@ bool STObject::setType (const SOTemplate& type)
|
|||||||
return valid;
|
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 ()
|
bool STObject::isValidForType ()
|
||||||
{
|
{
|
||||||
auto it = v_.begin();
|
auto it = v_.begin();
|
||||||
@@ -219,6 +234,13 @@ bool STObject::set (SerialIter& sit, int depth)
|
|||||||
|
|
||||||
// Unflatten the field
|
// Unflatten the field
|
||||||
v_.emplace_back(sit, fn);
|
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.");
|
"]' 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
|
// This function is used by parseObject to parse any JSON type that doesn't
|
||||||
// recurse. Everything represented here is a leaf-type.
|
// 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);
|
return std::move (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,10 +850,17 @@ static boost::optional <detail::STVar> parseArray (
|
|||||||
|
|
||||||
auto ret = parseObject (ss.str (), objectFields,
|
auto ret = parseObject (ss.str (), objectFields,
|
||||||
nameField, depth + 1, error);
|
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 ||
|
if (ret->getFName().fieldType != STI_OBJECT)
|
||||||
(ret->getFName().fieldType != STI_OBJECT))
|
|
||||||
{
|
{
|
||||||
|
error = non_object_in_array (ss.str(), i);
|
||||||
return boost::none;
|
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_OFFER, "temBAD_OFFER", "Malformed: Bad offer." },
|
||||||
{ temBAD_PATH, "temBAD_PATH", "Malformed: Bad path." },
|
{ temBAD_PATH, "temBAD_PATH", "Malformed: Bad path." },
|
||||||
{ temBAD_PATH_LOOP, "temBAD_PATH_LOOP", "Malformed: Loop in 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_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_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." },
|
{ 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_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_SEQUENCE, "temBAD_SEQUENCE", "Malformed: Sequence is not in the past." },
|
||||||
{ temBAD_SIGNATURE, "temBAD_SIGNATURE", "Malformed: Bad signature." },
|
{ 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_SRC_ACCOUNT, "temBAD_SRC_ACCOUNT", "Malformed: Bad source account." },
|
||||||
{ temBAD_TRANSFER_RATE, "temBAD_TRANSFER_RATE", "Malformed: Transfer rate must be >= 1.0" },
|
{ temBAD_TRANSFER_RATE, "temBAD_TRANSFER_RATE", "Malformed: Transfer rate must be >= 1.0" },
|
||||||
{ temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." },
|
{ temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." },
|
||||||
|
|||||||
@@ -87,6 +87,13 @@ TxFormats::TxFormats ()
|
|||||||
add ("TicketCancel", ttTICKET_CANCEL)
|
add ("TicketCancel", ttTICKET_CANCEL)
|
||||||
<< SOElement (sfTicketID, SOE_REQUIRED)
|
<< 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)
|
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
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
|
#include <ripple/protocol/Indexes.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -60,12 +61,32 @@ Json::Value doAccountInfo (RPC::Context& context)
|
|||||||
if (!jvAccepted.empty ())
|
if (!jvAccepted.empty ())
|
||||||
return jvAccepted;
|
return jvAccepted;
|
||||||
|
|
||||||
auto asAccepted = context.netOps.getAccountState (ledger, naAccount);
|
AccountState::pointer asAccepted =
|
||||||
|
context.netOps.getAccountState (ledger, naAccount);
|
||||||
|
|
||||||
if (asAccepted)
|
if (asAccepted)
|
||||||
{
|
{
|
||||||
asAccepted->addJson (jvAccepted);
|
asAccepted->addJson (jvAccepted);
|
||||||
result[jss::account_data] = jvAccepted;
|
|
||||||
|
// See if there's a SignerEntries for this account.
|
||||||
|
Account const account = naAccount.getAccountID ();
|
||||||
|
uint256 const signerListIndex = getSignerListIndex (account);
|
||||||
|
SLE::pointer signerList = ledger->getSLEi (signerListIndex);
|
||||||
|
|
||||||
|
if (signerList)
|
||||||
|
{
|
||||||
|
// Return multi-signing information if there are multi-signers.
|
||||||
|
static const Json::StaticString multiSignersName("multisigners");
|
||||||
|
jvAccepted[multiSignersName] = signerList->getJson (0);
|
||||||
|
Json::Value& multiSignerJson = jvAccepted[multiSignersName];
|
||||||
|
|
||||||
|
// Remove unwanted fields.
|
||||||
|
multiSignerJson.removeMember (sfFlags.getName ());
|
||||||
|
multiSignerJson.removeMember (sfLedgerEntryType.getName ());
|
||||||
|
multiSignerJson.removeMember (sfOwnerNode.getName ());
|
||||||
|
multiSignerJson.removeMember ("index");
|
||||||
|
}
|
||||||
|
result[jss::account_data] = jvAccepted;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,5 +44,8 @@
|
|||||||
#include <ripple/app/tx/impl/CreateOffer.cpp>
|
#include <ripple/app/tx/impl/CreateOffer.cpp>
|
||||||
#include <ripple/app/tx/impl/CreateTicket.cpp>
|
#include <ripple/app/tx/impl/CreateTicket.cpp>
|
||||||
#include <ripple/app/tx/impl/CancelTicket.cpp>
|
#include <ripple/app/tx/impl/CancelTicket.cpp>
|
||||||
|
#include <ripple/app/tx/impl/SetSignerList.cpp>
|
||||||
|
#include <ripple/app/tx/impl/SignerEntries.cpp>
|
||||||
|
|
||||||
#include <ripple/app/tx/tests/OfferStream.test.cpp>
|
#include <ripple/app/tx/tests/OfferStream.test.cpp>
|
||||||
#include <ripple/app/tx/tests/Taker.test.cpp>
|
#include <ripple/app/tx/tests/Taker.test.cpp>
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
#include <ripple/protocol/impl/STLedgerEntry.cpp>
|
#include <ripple/protocol/impl/STLedgerEntry.cpp>
|
||||||
#include <ripple/protocol/impl/STObject.cpp>
|
#include <ripple/protocol/impl/STObject.cpp>
|
||||||
#include <ripple/protocol/impl/STParsedJSON.cpp>
|
#include <ripple/protocol/impl/STParsedJSON.cpp>
|
||||||
|
#include <ripple/protocol/impl/InnerObjectFormats.cpp>
|
||||||
#include <ripple/protocol/impl/STPathSet.cpp>
|
#include <ripple/protocol/impl/STPathSet.cpp>
|
||||||
#include <ripple/protocol/impl/STTx.cpp>
|
#include <ripple/protocol/impl/STTx.cpp>
|
||||||
#include <ripple/protocol/impl/STValidation.cpp>
|
#include <ripple/protocol/impl/STValidation.cpp>
|
||||||
@@ -53,6 +54,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <ripple/protocol/tests/BuildInfo.test.cpp>
|
#include <ripple/protocol/tests/BuildInfo.test.cpp>
|
||||||
|
#include <ripple/protocol/tests/InnerObjectFormats.test.cpp>
|
||||||
#include <ripple/protocol/tests/Issue.test.cpp>
|
#include <ripple/protocol/tests/Issue.test.cpp>
|
||||||
#include <ripple/protocol/tests/Quality.test.cpp>
|
#include <ripple/protocol/tests/Quality.test.cpp>
|
||||||
#include <ripple/protocol/tests/RippleAddress.test.cpp>
|
#include <ripple/protocol/tests/RippleAddress.test.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user