Add Escrow support:

Escrow replaces the existing SusPay implementation with improved
code that also adds hashlock support to escrow payments, making
RCL ILP enabled.

The new functionality is under the `Escrow` amendment, which
supersedes and replaces the `SusPay` amendment.

This commit also deprecates the `CryptoConditions` amendment
which is replaced by the `CryptoConditionSuite` amendment which,
once enabled, will allow use of cryptoconditions others than
hashlocks.
This commit is contained in:
Nik Bougalis
2017-02-28 07:45:05 -08:00
committed by seelabs
parent 0c97dda276
commit cfde591ac9
38 changed files with 1267 additions and 3705 deletions

View File

@@ -1325,6 +1325,12 @@
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\CreateTicket.h"> <ClInclude Include="..\..\src\ripple\app\tx\impl\CreateTicket.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\Escrow.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\Escrow.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\tx\impl\Offer.h"> <ClInclude Include="..\..\src\ripple\app\tx\impl\Offer.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\OfferStream.cpp"> <ClCompile Include="..\..\src\ripple\app\tx\impl\OfferStream.cpp">
@@ -1375,12 +1381,6 @@
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\SignerEntries.h"> <ClInclude Include="..\..\src\ripple\app\tx\impl\SignerEntries.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\SusPay.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\SusPay.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>
@@ -1821,36 +1821,26 @@
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Condition.h"> <ClInclude Include="..\..\src\ripple\conditions\Condition.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Ed25519.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Fulfillment.h"> <ClInclude Include="..\..\src\ripple\conditions\Fulfillment.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\impl\base64.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\conditions\impl\Condition.cpp"> <ClCompile Include="..\..\src\ripple\conditions\impl\Condition.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>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\Ed25519.cpp"> <ClCompile Include="..\..\src\ripple\conditions\impl\error.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>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\conditions\impl\error.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.cpp"> <ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.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>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\RsaSha256.cpp"> <ClInclude Include="..\..\src\ripple\conditions\impl\PreimageSha256.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> </ClInclude>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h"> <ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PrefixSha256.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PreimageSha256.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\RsaSha256.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\Config.h"> <ClInclude Include="..\..\src\ripple\core\Config.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\core\ConfigSections.h"> <ClInclude Include="..\..\src\ripple\core\ConfigSections.h">
@@ -4187,6 +4177,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\test\app\Escrow_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Flow_test.cpp"> <ClCompile Include="..\..\src\test\app\Flow_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>
@@ -4255,10 +4249,6 @@
<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\test\app\SusPay_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Taker_test.cpp"> <ClCompile Include="..\..\src\test\app\Taker_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>
@@ -4399,22 +4389,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\test\conditions\Ed25519_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PrefixSha256_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp"> <ClCompile Include="..\..\src\test\conditions\PreimageSha256_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>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\test\conditions\RsaSha256_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\core\Config_test.cpp"> <ClCompile Include="..\..\src\test\core\Config_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>

View File

@@ -1839,6 +1839,12 @@
<ClInclude Include="..\..\src\ripple\app\tx\impl\CreateTicket.h"> <ClInclude Include="..\..\src\ripple\app\tx\impl\CreateTicket.h">
<Filter>ripple\app\tx\impl</Filter> <Filter>ripple\app\tx\impl</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\Escrow.cpp">
<Filter>ripple\app\tx\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\Escrow.h">
<Filter>ripple\app\tx\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\tx\impl\Offer.h"> <ClInclude Include="..\..\src\ripple\app\tx\impl\Offer.h">
<Filter>ripple\app\tx\impl</Filter> <Filter>ripple\app\tx\impl</Filter>
</ClInclude> </ClInclude>
@@ -1890,12 +1896,6 @@
<ClInclude Include="..\..\src\ripple\app\tx\impl\SignerEntries.h"> <ClInclude Include="..\..\src\ripple\app\tx\impl\SignerEntries.h">
<Filter>ripple\app\tx\impl</Filter> <Filter>ripple\app\tx\impl</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\SusPay.cpp">
<Filter>ripple\app\tx\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\SusPay.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>
@@ -2439,39 +2439,27 @@
<ClInclude Include="..\..\src\ripple\conditions\Condition.h"> <ClInclude Include="..\..\src\ripple\conditions\Condition.h">
<Filter>ripple\conditions</Filter> <Filter>ripple\conditions</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Ed25519.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Fulfillment.h"> <ClInclude Include="..\..\src\ripple\conditions\Fulfillment.h">
<Filter>ripple\conditions</Filter> <Filter>ripple\conditions</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\impl\base64.h">
<Filter>ripple\conditions\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\conditions\impl\Condition.cpp"> <ClCompile Include="..\..\src\ripple\conditions\impl\Condition.cpp">
<Filter>ripple\conditions\impl</Filter> <Filter>ripple\conditions\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\Ed25519.cpp"> <ClCompile Include="..\..\src\ripple\conditions\impl\error.cpp">
<Filter>ripple\conditions\impl</Filter> <Filter>ripple\conditions\impl</Filter>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\conditions\impl\error.h">
<Filter>ripple\conditions\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.cpp"> <ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.cpp">
<Filter>ripple\conditions\impl</Filter> <Filter>ripple\conditions\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\RsaSha256.cpp"> <ClInclude Include="..\..\src\ripple\conditions\impl\PreimageSha256.h">
<Filter>ripple\conditions\impl</Filter> <Filter>ripple\conditions\impl</Filter>
</ClCompile> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h"> <ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
<Filter>ripple\conditions\impl</Filter> <Filter>ripple\conditions\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PrefixSha256.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PreimageSha256.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\RsaSha256.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\Config.h"> <ClInclude Include="..\..\src\ripple\core\Config.h">
<Filter>ripple\core</Filter> <Filter>ripple\core</Filter>
</ClInclude> </ClInclude>
@@ -4983,6 +4971,9 @@
<ClCompile Include="..\..\src\test\app\Discrepancy_test.cpp"> <ClCompile Include="..\..\src\test\app\Discrepancy_test.cpp">
<Filter>test\app</Filter> <Filter>test\app</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\test\app\Escrow_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Flow_test.cpp"> <ClCompile Include="..\..\src\test\app\Flow_test.cpp">
<Filter>test\app</Filter> <Filter>test\app</Filter>
</ClCompile> </ClCompile>
@@ -5034,9 +5025,6 @@
<ClCompile Include="..\..\src\test\app\SHAMapStore_test.cpp"> <ClCompile Include="..\..\src\test\app\SHAMapStore_test.cpp">
<Filter>test\app</Filter> <Filter>test\app</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\test\app\SusPay_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Taker_test.cpp"> <ClCompile Include="..\..\src\test\app\Taker_test.cpp">
<Filter>test\app</Filter> <Filter>test\app</Filter>
</ClCompile> </ClCompile>
@@ -5142,18 +5130,9 @@
<ClCompile Include="..\..\src\test\beast\SemanticVersion_test.cpp"> <ClCompile Include="..\..\src\test\beast\SemanticVersion_test.cpp">
<Filter>test\beast</Filter> <Filter>test\beast</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\test\conditions\Ed25519_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PrefixSha256_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp"> <ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp">
<Filter>test\conditions</Filter> <Filter>test\conditions</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\test\conditions\RsaSha256_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\core\Config_test.cpp"> <ClCompile Include="..\..\src\test\core\Config_test.cpp">
<Filter>test\core</Filter> <Filter>test\core</Filter>
</ClCompile> </ClCompile>

View File

@@ -42,7 +42,6 @@ supportedAmendments ()
{ "C6970A8B603D8778783B61C0D445C23D1633CCFAEF0D43E7DBCD1521D34BD7C3 SHAMapV2" }, { "C6970A8B603D8778783B61C0D445C23D1633CCFAEF0D43E7DBCD1521D34BD7C3 SHAMapV2" },
{ "4C97EBA926031A7CF7D7B36FDE3ED66DDA5421192D63DE53FFB46E43B9DC8373 MultiSign" }, { "4C97EBA926031A7CF7D7B36FDE3ED66DDA5421192D63DE53FFB46E43B9DC8373 MultiSign" },
{ "C1B8D934087225F509BEB5A8EC24447854713EE447D277F69545ABFA0E0FD490 Tickets" }, { "C1B8D934087225F509BEB5A8EC24447854713EE447D277F69545ABFA0E0FD490 Tickets" },
{ "DA1BD556B42D85EA9C84066D028D355B52416734D3283F85E216EA5DA6DB7E13 SusPay" },
{ "6781F8368C4771B83E8B821D88F580202BCB4228075297B19E4FDC5233F1EFDC TrustSetAuth" }, { "6781F8368C4771B83E8B821D88F580202BCB4228075297B19E4FDC5233F1EFDC TrustSetAuth" },
{ "42426C4D4F1009EE67080A9B7965B44656D7714D104A72F9B4369F97ABF044EE FeeEscalation" }, { "42426C4D4F1009EE67080A9B7965B44656D7714D104A72F9B4369F97ABF044EE FeeEscalation" },
{ "9178256A980A86CF3D70D0260A7DA6402AAFE43632FDBCB88037978404188871 OwnerPaysFee" }, { "9178256A980A86CF3D70D0260A7DA6402AAFE43632FDBCB88037978404188871 OwnerPaysFee" },
@@ -50,7 +49,9 @@ supportedAmendments ()
{ "740352F2412A9909880C23A559FCECEDA3BE2126FED62FC7660D628A06927F11 Flow" }, { "740352F2412A9909880C23A559FCECEDA3BE2126FED62FC7660D628A06927F11 Flow" },
{ "1562511F573A19AE9BD103B5D6B9E01B3B46805AEC5D3C4805C902B514399146 CryptoConditions" }, { "1562511F573A19AE9BD103B5D6B9E01B3B46805AEC5D3C4805C902B514399146 CryptoConditions" },
{ "532651B4FD58DF8922A49BA101AB3E996E5BFBF95A913B3E392504863E63B164 TickSize" }, { "532651B4FD58DF8922A49BA101AB3E996E5BFBF95A913B3E392504863E63B164 TickSize" },
{ "67431BE4A5F355C45E36EED94B38DFACC1F111F547360F444642512CBC0E18E1 RIPD1368" } { "67431BE4A5F355C45E36EED94B38DFACC1F111F547360F444642512CBC0E18E1 RIPD1368" },
{ "07D43DCE529B15A10827E5E04943B496762F9A88E3268269D69C44BE49E21104 Escrow" },
{ "86E83A7D2ECE3AD5FA87AB2195AE015C950469ABF0B72EAACED318F74886AE90 CryptoConditionsSuite" }
}; };
} }

View File

@@ -18,7 +18,7 @@
//============================================================================== //==============================================================================
#include <BeastConfig.h> #include <BeastConfig.h>
#include <ripple/app/tx/impl/SusPay.h> #include <ripple/app/tx/impl/Escrow.h>
#include <ripple/app/misc/HashRouter.h> #include <ripple/app/misc/HashRouter.h>
#include <ripple/basics/chrono.h> #include <ripple/basics/chrono.h>
#include <ripple/basics/Log.h> #include <ripple/basics/Log.h>
@@ -32,7 +32,7 @@
#include <ripple/protocol/XRPAmount.h> #include <ripple/protocol/XRPAmount.h>
#include <ripple/ledger/View.h> #include <ripple/ledger/View.h>
// During a SusPayFinish, the transaction must specify both // During an EscrowFinish, the transaction must specify both
// a condition and a fulfillment. We track whether that // a condition and a fulfillment. We track whether that
// fulfillment matches and validates the condition. // fulfillment matches and validates the condition.
#define SF_CF_INVALID SF_PRIVATE5 #define SF_CF_INVALID SF_PRIVATE5
@@ -41,88 +41,88 @@
namespace ripple { namespace ripple {
/* /*
SuspendedPayment Escrow allows an account holder to sequester any amount
of XRP in its own ledger entry, until the escrow process
either finishes or is canceled.
A suspended payment ("SusPay") sequesters XRP in its If the escrow process finishes successfully, then the
own ledger entry until a SusPayFinish or a SusPayCancel destination account (which must exist) will receives the
transaction mentioning the ledger entry is successfully sequestered XRP. If the escrow is, instead, canceled,
applied to the ledger. If the SusPayFinish succeeds, the account which created the escrow will receive the
the destination account (which must exist) receives the sequestered XRP back instead.
XRP. If the SusPayCancel succeeds, the account which
created the SusPay is credited the XRP.
SusPayCreate EscrowCreate
When the SusPay is created, an optional condition may When an escrow is created, an optional condition may
be attached. The condition is specified by providing be attached. If present, that condition must be
the cryptographic digest of the condition to be met. fulfilled for the escrow to successfully finish.
At the time of creation, one or both of the fields At the time of creation, one or both of the fields
sfCancelAfter and sfFinishAfter may be provided. If sfCancelAfter and sfFinishAfter may be provided. If
neither field is specified, the transaction is neither field is specified, the transaction is
malformed. malformed.
Since the SusPay eventually becomes a payment, an Since the escrow eventually becomes a payment, an
optional DestinationTag and an optional SourceTag optional DestinationTag and an optional SourceTag
is supported in the SusPayCreate transaction. are supported in the EscrowCreate transaction.
Validation rules: Validation rules:
sfCondition sfCondition
If present, specifies a condition, and the If present, specifies a condition; the same
condition along with its matching fulfillment condition along with its matching fulfillment
is required on a SusPayFinish. are required during EscrowFinish.
sfCancelAfter sfCancelAfter
If present, SusPay may be canceled after the If present, escrow may be canceled after the
specified time (seconds after the Ripple epoch). specified time (seconds after the Ripple epoch).
sfFinishAfter sfFinishAfter
If present, must be prior to sfCancelAfter. If present, must be prior to sfCancelAfter.
A SusPayFinish succeeds only in ledgers after A EscrowFinish succeeds only in ledgers after
sfFinishAfter but before sfCancelAfter. sfFinishAfter but before sfCancelAfter.
If absent, same as parentCloseTime If absent, same as parentCloseTime
Malformed if both sfCancelAfter, sfFinishAfter Malformed if both sfCancelAfter, sfFinishAfter
are absent. are absent.
Malformed if both sfFinishAfter, sfCancelAfter Malformed if both sfFinishAfter, sfCancelAfter
specified and sfCancelAfter <= sfFinishAfter specified and sfCancelAfter <= sfFinishAfter
SusPayFinish EscrowFinish
Any account may submit a SusPayFinish. If the SusPay Any account may submit a EscrowFinish. If the escrow
ledger entry specifies a condition, the SusPayFinish ledger entry specifies a condition, the EscrowFinish
must provide the same condition and its associated must provide the same condition and its associated
fulfillment in the sfFulfillment field, or else the fulfillment in the sfCondition and sfFulfillment
SusPayFinish will fail. fields, or else the EscrowFinish will fail.
If the SusPay ledger entry specifies sfFinishAfter, the If the escrow ledger entry specifies sfFinishAfter, the
transaction will fail if parentCloseTime <= sfFinishAfter. transaction will fail if parentCloseTime <= sfFinishAfter.
SusPayFinish transactions must be submitted before a EscrowFinish transactions must be submitted before
SusPay's sfCancelAfter if present. the escrow's sfCancelAfter if present.
If the SusPay ledger entry specifies sfCancelAfter, the If the escrow ledger entry specifies sfCancelAfter, the
transaction will fail if sfCancelAfter <= parentCloseTime. transaction will fail if sfCancelAfter <= parentCloseTime.
NOTE: The reason the condition must be specified again NOTE: The reason the condition must be specified again
is because it must always be possible to verify is because it must always be possible to verify
the condition without retrieving the SusPay the condition without retrieving the escrow
ledger entry. ledger entry.
SusPayCancel EscrowCancel
Any account may submit a SusPayCancel transaction. Any account may submit a EscrowCancel transaction.
If the SusPay ledger entry does not specify a If the escrow ledger entry does not specify a
sfCancelAfter, the cancel transaction will fail. sfCancelAfter, the cancel transaction will fail.
If parentCloseTime <= sfCancelAfter, the transaction If parentCloseTime <= sfCancelAfter, the transaction
will fail. will fail.
When a SusPay is canceled, the funds are returned to When a escrow is canceled, the funds are returned to
the source account. the source account.
By careful selection of fields in each transaction, By careful selection of fields in each transaction,
@@ -135,15 +135,15 @@ namespace ripple {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
XRPAmount XRPAmount
SusPayCreate::calculateMaxSpend(STTx const& tx) EscrowCreate::calculateMaxSpend(STTx const& tx)
{ {
return tx[sfAmount].xrp(); return tx[sfAmount].xrp();
} }
TER TER
SusPayCreate::preflight (PreflightContext const& ctx) EscrowCreate::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featureSusPay)) if (! ctx.rules.enabled(featureEscrow))
return temDISABLED; return temDISABLED;
auto const ret = preflight1 (ctx); auto const ret = preflight1 (ctx);
@@ -166,43 +166,30 @@ SusPayCreate::preflight (PreflightContext const& ctx)
if (auto const cb = ctx.tx[~sfCondition]) if (auto const cb = ctx.tx[~sfCondition])
{ {
if (! ctx.rules.enabled(featureCryptoConditions))
return temDISABLED;
using namespace ripple::cryptoconditions; using namespace ripple::cryptoconditions;
// TODO: Remove this try/catch once cryptoconditions std::error_code ec;
// no longer use exceptions.
try auto condition = Condition::deserialize(*cb, ec);
{ if (!condition)
auto condition = loadCondition(*cb);
if (!condition)
return temMALFORMED;
{
// TODO: This is here temporarily to ensure
// that the condition given doesn't
// contain unnecessary trailing junk.
// The new parsing API will simplify
// the checking here.
auto b = to_blob(*condition);
if (*cb != makeSlice(b))
return temMALFORMED;
}
}
catch (...)
{ {
JLOG(ctx.j.debug()) <<
"Malformed condition during escrow creation: " << ec.message();
return temMALFORMED; return temMALFORMED;
} }
// Conditions other than PrefixSha256 require the
// "CryptoConditionsSuite" amendment:
if (condition->type != Type::preimageSha256 &&
!ctx.rules.enabled(featureCryptoConditionsSuite))
return temDISABLED;
} }
return preflight2 (ctx); return preflight2 (ctx);
} }
TER TER
SusPayCreate::doApply() EscrowCreate::doApply()
{ {
auto const closeTime = ctx_.view ().info ().parentCloseTime; auto const closeTime = ctx_.view ().info ().parentCloseTime;
@@ -252,9 +239,9 @@ SusPayCreate::doApply()
return tecNO_TARGET; return tecNO_TARGET;
} }
// Create SusPay in ledger // Create escrow in ledger
auto const slep = std::make_shared<SLE>( auto const slep = std::make_shared<SLE>(
keylet::susPay(account, (*sle)[sfSequence] - 1)); keylet::escrow(account, (*sle)[sfSequence] - 1));
(*slep)[sfAmount] = ctx_.tx[sfAmount]; (*slep)[sfAmount] = ctx_.tx[sfAmount];
(*slep)[sfAccount] = account; (*slep)[sfAccount] = account;
(*slep)[~sfCondition] = ctx_.tx[~sfCondition]; (*slep)[~sfCondition] = ctx_.tx[~sfCondition];
@@ -266,7 +253,7 @@ SusPayCreate::doApply()
ctx_.view().insert(slep); ctx_.view().insert(slep);
// Add SusPay to owner directory // Add escrow to owner directory
{ {
uint64_t page; uint64_t page;
auto result = dirAdd(ctx_.view(), page, auto result = dirAdd(ctx_.view(), page,
@@ -293,48 +280,23 @@ checkCondition (Slice f, Slice c)
{ {
using namespace ripple::cryptoconditions; using namespace ripple::cryptoconditions;
// TODO: Remove this try/catch once cryptoconditions std::error_code ec;
// no longer use exceptions.
try
{
auto condition = loadCondition(c);
if (!condition) auto condition = Condition::deserialize(c, ec);
return false; if (!condition)
auto fulfillment = loadFulfillment(f);
if (!fulfillment)
return false;
{
// TODO: This is here temporarily to ensure
// that the condition & fulfillment
// given don't contain unnecessary
// trailing junk. The new parsing API
// will simplify the checking here.
auto cb = to_blob(*condition);
if (c != makeSlice(cb))
return false;
auto fb = to_blob(*fulfillment);
if (f != makeSlice(fb))
return false;
}
return validateTrigger (*fulfillment, *condition);
}
catch (...)
{
return false; return false;
}
auto fulfillment = Fulfillment::deserialize(f, ec);
if (!fulfillment)
return false;
return validate (*fulfillment, *condition);
} }
TER TER
SusPayFinish::preflight (PreflightContext const& ctx) EscrowFinish::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featureSusPay)) if (! ctx.rules.enabled(featureEscrow))
return temDISABLED; return temDISABLED;
{ {
@@ -346,12 +308,6 @@ SusPayFinish::preflight (PreflightContext const& ctx)
auto const cb = ctx.tx[~sfCondition]; auto const cb = ctx.tx[~sfCondition];
auto const fb = ctx.tx[~sfFulfillment]; auto const fb = ctx.tx[~sfFulfillment];
if (cb || fb)
{
if (! ctx.rules.enabled(featureCryptoConditions))
return temDISABLED;
}
// If you specify a condition, then you must also specify // If you specify a condition, then you must also specify
// a fulfillment. // a fulfillment.
if (static_cast<bool>(cb) != static_cast<bool>(fb)) if (static_cast<bool>(cb) != static_cast<bool>(fb))
@@ -388,7 +344,7 @@ SusPayFinish::preflight (PreflightContext const& ctx)
} }
std::uint64_t std::uint64_t
SusPayFinish::calculateBaseFee (PreclaimContext const& ctx) EscrowFinish::calculateBaseFee (PreclaimContext const& ctx)
{ {
std::uint64_t extraFee = 0; std::uint64_t extraFee = 0;
@@ -401,12 +357,10 @@ SusPayFinish::calculateBaseFee (PreclaimContext const& ctx)
return Transactor::calculateBaseFee (ctx) + extraFee; return Transactor::calculateBaseFee (ctx) + extraFee;
} }
TER TER
SusPayFinish::doApply() EscrowFinish::doApply()
{ {
// peek SusPay SLE auto const k = keylet::escrow(
auto const k = keylet::susPay(
ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
auto const slep = ctx_.view().peek(k); auto const slep = ctx_.view().peek(k);
if (! slep) if (! slep)
@@ -473,7 +427,7 @@ SusPayFinish::doApply()
AccountID const account = (*slep)[sfAccount]; AccountID const account = (*slep)[sfAccount];
// Remove SusPay from owner directory // Remove escrow from owner directory
{ {
auto const page = (*slep)[sfOwnerNode]; auto const page = (*slep)[sfOwnerNode];
TER const ter = dirDelete(ctx_.view(), true, TER const ter = dirDelete(ctx_.view(), true,
@@ -501,7 +455,7 @@ SusPayFinish::doApply()
(*sle)[sfOwnerCount] = (*sle)[sfOwnerCount] - 1; (*sle)[sfOwnerCount] = (*sle)[sfOwnerCount] - 1;
ctx_.view().update(sle); ctx_.view().update(sle);
// Remove SusPay from ledger // Remove escrow from ledger
ctx_.view().erase(slep); ctx_.view().erase(slep);
return tesSUCCESS; return tesSUCCESS;
@@ -510,9 +464,9 @@ SusPayFinish::doApply()
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
TER TER
SusPayCancel::preflight (PreflightContext const& ctx) EscrowCancel::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featureSusPay)) if (! ctx.rules.enabled(featureEscrow))
return temDISABLED; return temDISABLED;
auto const ret = preflight1 (ctx); auto const ret = preflight1 (ctx);
@@ -523,10 +477,9 @@ SusPayCancel::preflight (PreflightContext const& ctx)
} }
TER TER
SusPayCancel::doApply() EscrowCancel::doApply()
{ {
// peek SusPay SLE auto const k = keylet::escrow(
auto const k = keylet::susPay(
ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
auto const slep = ctx_.view().peek(k); auto const slep = ctx_.view().peek(k);
if (! slep) if (! slep)
@@ -540,7 +493,7 @@ SusPayCancel::doApply()
AccountID const account = (*slep)[sfAccount]; AccountID const account = (*slep)[sfAccount];
// Remove SusPay from owner directory // Remove escrow from owner directory
{ {
auto const page = (*slep)[sfOwnerNode]; auto const page = (*slep)[sfOwnerNode];
TER const ter = dirDelete(ctx_.view(), true, TER const ter = dirDelete(ctx_.view(), true,
@@ -557,7 +510,7 @@ SusPayCancel::doApply()
(*sle)[sfOwnerCount] = (*sle)[sfOwnerCount] - 1; (*sle)[sfOwnerCount] = (*sle)[sfOwnerCount] - 1;
ctx_.view().update(sle); ctx_.view().update(sle);
// Remove SusPay from ledger // Remove escrow from ledger
ctx_.view().erase(slep); ctx_.view().erase(slep);
return tesSUCCESS; return tesSUCCESS;

View File

@@ -17,19 +17,19 @@
*/ */
//============================================================================== //==============================================================================
#ifndef RIPPLE_TX_SUSPAY_H_INCLUDED #ifndef RIPPLE_TX_ESCROW_H_INCLUDED
#define RIPPLE_TX_SUSPAY_H_INCLUDED #define RIPPLE_TX_ESCROW_H_INCLUDED
#include <ripple/app/tx/impl/Transactor.h> #include <ripple/app/tx/impl/Transactor.h>
namespace ripple { namespace ripple {
class SusPayCreate class EscrowCreate
: public Transactor : public Transactor
{ {
public: public:
explicit explicit
SusPayCreate (ApplyContext& ctx) EscrowCreate (ApplyContext& ctx)
: Transactor(ctx) : Transactor(ctx)
{ {
} }
@@ -48,12 +48,12 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class SusPayFinish class EscrowFinish
: public Transactor : public Transactor
{ {
public: public:
explicit explicit
SusPayFinish (ApplyContext& ctx) EscrowFinish (ApplyContext& ctx)
: Transactor(ctx) : Transactor(ctx)
{ {
} }
@@ -72,12 +72,12 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class SusPayCancel class EscrowCancel
: public Transactor : public Transactor
{ {
public: public:
explicit explicit
SusPayCancel (ApplyContext& ctx) EscrowCancel (ApplyContext& ctx)
: Transactor(ctx) : Transactor(ctx)
{ {
} }

View File

@@ -25,12 +25,12 @@
#include <ripple/app/tx/impl/Change.h> #include <ripple/app/tx/impl/Change.h>
#include <ripple/app/tx/impl/CreateOffer.h> #include <ripple/app/tx/impl/CreateOffer.h>
#include <ripple/app/tx/impl/CreateTicket.h> #include <ripple/app/tx/impl/CreateTicket.h>
#include <ripple/app/tx/impl/Escrow.h>
#include <ripple/app/tx/impl/Payment.h> #include <ripple/app/tx/impl/Payment.h>
#include <ripple/app/tx/impl/SetAccount.h> #include <ripple/app/tx/impl/SetAccount.h>
#include <ripple/app/tx/impl/SetRegularKey.h> #include <ripple/app/tx/impl/SetRegularKey.h>
#include <ripple/app/tx/impl/SetSignerList.h> #include <ripple/app/tx/impl/SetSignerList.h>
#include <ripple/app/tx/impl/SetTrust.h> #include <ripple/app/tx/impl/SetTrust.h>
#include <ripple/app/tx/impl/SusPay.h>
#include <ripple/app/tx/impl/PayChan.h> #include <ripple/app/tx/impl/PayChan.h>
namespace ripple { namespace ripple {
@@ -45,9 +45,9 @@ invoke_preflight (PreflightContext const& ctx)
case ttOFFER_CANCEL: return CancelOffer ::preflight(ctx); case ttOFFER_CANCEL: return CancelOffer ::preflight(ctx);
case ttOFFER_CREATE: return CreateOffer ::preflight(ctx); case ttOFFER_CREATE: return CreateOffer ::preflight(ctx);
case ttPAYMENT: return Payment ::preflight(ctx); case ttPAYMENT: return Payment ::preflight(ctx);
case ttSUSPAY_CREATE: return SusPayCreate ::preflight(ctx); case ttESCROW_CREATE: return EscrowCreate ::preflight(ctx);
case ttSUSPAY_FINISH: return SusPayFinish ::preflight(ctx); case ttESCROW_FINISH: return EscrowFinish ::preflight(ctx);
case ttSUSPAY_CANCEL: return SusPayCancel ::preflight(ctx); case ttESCROW_CANCEL: return EscrowCancel ::preflight(ctx);
case ttREGULAR_KEY_SET: return SetRegularKey ::preflight(ctx); case ttREGULAR_KEY_SET: return SetRegularKey ::preflight(ctx);
case ttSIGNER_LIST_SET: return SetSignerList ::preflight(ctx); case ttSIGNER_LIST_SET: return SetSignerList ::preflight(ctx);
case ttTICKET_CANCEL: return CancelTicket ::preflight(ctx); case ttTICKET_CANCEL: return CancelTicket ::preflight(ctx);
@@ -118,9 +118,9 @@ invoke_preclaim (PreclaimContext const& ctx)
case ttOFFER_CANCEL: return invoke_preclaim<CancelOffer>(ctx); case ttOFFER_CANCEL: return invoke_preclaim<CancelOffer>(ctx);
case ttOFFER_CREATE: return invoke_preclaim<CreateOffer>(ctx); case ttOFFER_CREATE: return invoke_preclaim<CreateOffer>(ctx);
case ttPAYMENT: return invoke_preclaim<Payment>(ctx); case ttPAYMENT: return invoke_preclaim<Payment>(ctx);
case ttSUSPAY_CREATE: return invoke_preclaim<SusPayCreate>(ctx); case ttESCROW_CREATE: return invoke_preclaim<EscrowCreate>(ctx);
case ttSUSPAY_FINISH: return invoke_preclaim<SusPayFinish>(ctx); case ttESCROW_FINISH: return invoke_preclaim<EscrowFinish>(ctx);
case ttSUSPAY_CANCEL: return invoke_preclaim<SusPayCancel>(ctx); case ttESCROW_CANCEL: return invoke_preclaim<EscrowCancel>(ctx);
case ttREGULAR_KEY_SET: return invoke_preclaim<SetRegularKey>(ctx); case ttREGULAR_KEY_SET: return invoke_preclaim<SetRegularKey>(ctx);
case ttSIGNER_LIST_SET: return invoke_preclaim<SetSignerList>(ctx); case ttSIGNER_LIST_SET: return invoke_preclaim<SetSignerList>(ctx);
case ttTICKET_CANCEL: return invoke_preclaim<CancelTicket>(ctx); case ttTICKET_CANCEL: return invoke_preclaim<CancelTicket>(ctx);
@@ -147,9 +147,9 @@ invoke_calculateBaseFee(PreclaimContext const& ctx)
case ttOFFER_CANCEL: return CancelOffer::calculateBaseFee(ctx); case ttOFFER_CANCEL: return CancelOffer::calculateBaseFee(ctx);
case ttOFFER_CREATE: return CreateOffer::calculateBaseFee(ctx); case ttOFFER_CREATE: return CreateOffer::calculateBaseFee(ctx);
case ttPAYMENT: return Payment::calculateBaseFee(ctx); case ttPAYMENT: return Payment::calculateBaseFee(ctx);
case ttSUSPAY_CREATE: return SusPayCreate::calculateBaseFee(ctx); case ttESCROW_CREATE: return EscrowCreate::calculateBaseFee(ctx);
case ttSUSPAY_FINISH: return SusPayFinish::calculateBaseFee(ctx); case ttESCROW_FINISH: return EscrowFinish::calculateBaseFee(ctx);
case ttSUSPAY_CANCEL: return SusPayCancel::calculateBaseFee(ctx); case ttESCROW_CANCEL: return EscrowCancel::calculateBaseFee(ctx);
case ttREGULAR_KEY_SET: return SetRegularKey::calculateBaseFee(ctx); case ttREGULAR_KEY_SET: return SetRegularKey::calculateBaseFee(ctx);
case ttSIGNER_LIST_SET: return SetSignerList::calculateBaseFee(ctx); case ttSIGNER_LIST_SET: return SetSignerList::calculateBaseFee(ctx);
case ttTICKET_CANCEL: return CancelTicket::calculateBaseFee(ctx); case ttTICKET_CANCEL: return CancelTicket::calculateBaseFee(ctx);
@@ -189,9 +189,9 @@ invoke_calculateConsequences(STTx const& tx)
case ttOFFER_CANCEL: return invoke_calculateConsequences<CancelOffer>(tx); case ttOFFER_CANCEL: return invoke_calculateConsequences<CancelOffer>(tx);
case ttOFFER_CREATE: return invoke_calculateConsequences<CreateOffer>(tx); case ttOFFER_CREATE: return invoke_calculateConsequences<CreateOffer>(tx);
case ttPAYMENT: return invoke_calculateConsequences<Payment>(tx); case ttPAYMENT: return invoke_calculateConsequences<Payment>(tx);
case ttSUSPAY_CREATE: return invoke_calculateConsequences<SusPayCreate>(tx); case ttESCROW_CREATE: return invoke_calculateConsequences<EscrowCreate>(tx);
case ttSUSPAY_FINISH: return invoke_calculateConsequences<SusPayFinish>(tx); case ttESCROW_FINISH: return invoke_calculateConsequences<EscrowFinish>(tx);
case ttSUSPAY_CANCEL: return invoke_calculateConsequences<SusPayCancel>(tx); case ttESCROW_CANCEL: return invoke_calculateConsequences<EscrowCancel>(tx);
case ttREGULAR_KEY_SET: return invoke_calculateConsequences<SetRegularKey>(tx); case ttREGULAR_KEY_SET: return invoke_calculateConsequences<SetRegularKey>(tx);
case ttSIGNER_LIST_SET: return invoke_calculateConsequences<SetSignerList>(tx); case ttSIGNER_LIST_SET: return invoke_calculateConsequences<SetSignerList>(tx);
case ttTICKET_CANCEL: return invoke_calculateConsequences<CancelTicket>(tx); case ttTICKET_CANCEL: return invoke_calculateConsequences<CancelTicket>(tx);
@@ -220,9 +220,9 @@ invoke_apply (ApplyContext& ctx)
case ttOFFER_CANCEL: { CancelOffer p(ctx); return p(); } case ttOFFER_CANCEL: { CancelOffer p(ctx); return p(); }
case ttOFFER_CREATE: { CreateOffer p(ctx); return p(); } case ttOFFER_CREATE: { CreateOffer p(ctx); return p(); }
case ttPAYMENT: { Payment p(ctx); return p(); } case ttPAYMENT: { Payment p(ctx); return p(); }
case ttSUSPAY_CREATE: { SusPayCreate p(ctx); return p(); } case ttESCROW_CREATE: { EscrowCreate p(ctx); return p(); }
case ttSUSPAY_FINISH: { SusPayFinish p(ctx); return p(); } case ttESCROW_FINISH: { EscrowFinish p(ctx); return p(); }
case ttSUSPAY_CANCEL: { SusPayCancel p(ctx); return p(); } case ttESCROW_CANCEL: { EscrowCancel p(ctx); return p(); }
case ttREGULAR_KEY_SET: { SetRegularKey p(ctx); return p(); } case ttREGULAR_KEY_SET: { SetRegularKey p(ctx); return p(); }
case ttSIGNER_LIST_SET: { SetSignerList p(ctx); return p(); } case ttSIGNER_LIST_SET: { SetSignerList p(ctx); return p(); }
case ttTICKET_CANCEL: { CancelTicket p(ctx); return p(); } case ttTICKET_CANCEL: { CancelTicket p(ctx); return p(); }
@@ -341,4 +341,4 @@ doApply(PreclaimResult const& preclaimResult,
} }
} }
} // ripple } // ripple

View File

@@ -20,73 +20,91 @@
#ifndef RIPPLE_CONDITIONS_CONDITION_H #ifndef RIPPLE_CONDITIONS_CONDITION_H
#define RIPPLE_CONDITIONS_CONDITION_H #define RIPPLE_CONDITIONS_CONDITION_H
#include <ripple/conditions/impl/base64.h> // use Beast implementation #include <ripple/basics/Buffer.h>
#include <ripple/basics/Slice.h>
#include <ripple/conditions/impl/utils.h> #include <ripple/conditions/impl/utils.h>
#include <boost/optional.hpp>
#include <array> #include <array>
#include <cstdint> #include <cstdint>
#include <set>
#include <string> #include <string>
#include <system_error>
#include <vector>
namespace ripple { namespace ripple {
namespace cryptoconditions { namespace cryptoconditions {
// NIKB-TODO: These should move to a separate file: enum class Type
std::uint16_t constexpr condition_hashlock = 0; : std::uint8_t
std::uint16_t constexpr condition_prefix_sha256 = 1;
std::uint16_t constexpr condition_threshold_sha256 = 2;
std::uint16_t constexpr condition_rsa_sha256 = 3;
std::uint16_t constexpr condition_ed25519 = 4;
// NIKB-TODO: These should be `enum class : std::uint32_t`
std::uint32_t constexpr feature_sha256 = 1;
std::uint32_t constexpr feature_preimage = 2;
std::uint32_t constexpr feature_prefix = 4;
std::uint32_t constexpr feature_threshold = 8;
std::uint32_t constexpr feature_rsa_pss = 16;
std::uint32_t constexpr feature_ed25519 = 32;
/** The list of all feature suited defined in the RFC */
std::uint32_t constexpr definedFeatures =
feature_sha256 |
feature_preimage |
feature_prefix |
feature_threshold |
feature_rsa_pss |
feature_ed25519;
/** The largest fulfillment supported by this implementation.
Fulfillments larger than this value cannot be processed
and will not be generated.
*/
constexpr std::size_t maxSupportedFulfillmentLength = 65535;
struct Condition
{ {
std::uint16_t type; preimageSha256 = 0,
prefixSha256 = 1,
thresholdSha256 = 2,
rsaSha256 = 3,
ed25519Sha256 = 4
};
/** The maximum length of a fulfillment for this condition. class Condition
{
public:
/** The largest binary condition we support.
While it is possible for a fulfillment to be smaller @note This value will be increased in the future, but it
it can never be bigger than this. must never decrease, as that could cause conditions
that were previously considered valid to no longer
be allowed.
*/ */
std::uint16_t maxFulfillmentLength; static constexpr std::size_t maxSerializedCondition = 128;
/** The features suites required to process a fulfillment. */ /** Load a condition from its binary form
std::uint32_t featureBitmask;
@param s The buffer containing the fulfillment to load.
@param ec Set to the error, if any occurred.
The binary format for a condition is specified in the
cryptoconditions RFC. See:
https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.2
*/
static
std::unique_ptr<Condition>
deserialize(Slice s, std::error_code& ec);
public:
Type type;
/** An identifier for this condition. /** An identifier for this condition.
This fingerprint is meant to be unique only with This fingerprint is meant to be unique only with
respect to other conditions of the same type. respect to other conditions of the same type.
*/ */
std::array<std::uint8_t, 32> fingerprint; Buffer fingerprint;
// Can this be deleted? /** The cost associated with this condition. */
Condition () = default; std::uint32_t cost;
Condition (Condition const&) = default; /** For compound conditions, set of conditions includes */
Condition (Condition&&) = default; std::set<Type> subtypes;
Condition(Type t, std::uint32_t c, Slice fp)
: type(t)
, fingerprint(fp)
, cost(c)
{
}
Condition(Type t, std::uint32_t c, Buffer&& fp)
: type(t)
, fingerprint(std::move(fp))
, cost(c)
{
}
~Condition() = default;
Condition(Condition const&) = default;
Condition(Condition&&) = default;
Condition() = delete;
}; };
inline inline
@@ -95,9 +113,9 @@ operator== (Condition const& lhs, Condition const& rhs)
{ {
return return
lhs.type == rhs.type && lhs.type == rhs.type &&
lhs.featureBitmask == rhs.featureBitmask && lhs.cost == rhs.cost &&
lhs.maxFulfillmentLength == rhs.maxFulfillmentLength && lhs.subtypes == rhs.subtypes &&
lhs.fingerprint == rhs.fingerprint; lhs.fingerprint == rhs.fingerprint;
} }
inline inline
@@ -107,99 +125,6 @@ operator!= (Condition const& lhs, Condition const& rhs)
return !(lhs == rhs); return !(lhs == rhs);
} }
/** Determine if a given condition is valid.
@note this function checks whether it understands the
type of the condition, and if so, whether it meets
the requirements mandated by the RFC.
*/
inline
bool
validate (Condition const& c)
{
// This check can never trigger because of the range of
// the maxFulfillmentLength type. It's here in case the
// type is changed in the future.
if (c.maxFulfillmentLength > maxSupportedFulfillmentLength)
return false;
if (c.type == condition_hashlock)
return (c.featureBitmask == (feature_sha256 | feature_preimage));
// A prefix condition contains a subfulfillment; it
// requires all the features its child may require.
if (c.type == condition_prefix_sha256)
{
auto const mask = (feature_sha256 | feature_prefix);
// We need to have at least our own feature suites:
auto const cf1 = c.featureBitmask & mask;
if (cf1 != mask)
return false;
// And at least one more feature suite for our
// subfulfillment (since you need to terminate a
// chain of prefix conditions with a non-prefix)
auto const cf2 = c.featureBitmask & ~mask;
if (cf2 == 0)
return false;
return (cf2 & definedFeatures) == cf2;
}
if (c.type == condition_rsa_sha256)
return (c.featureBitmask == (feature_rsa_pss | feature_sha256));
if (c.type == condition_ed25519)
return (c.featureBitmask == feature_ed25519);
return false;
}
/** True if condition type is specified in the RFC.
@note: this function may return true even if the type
of condition isn't presently supported by this
implementation.
*/
inline
bool
isCondition (std::uint16_t type)
{
switch(type)
{
case condition_hashlock:
case condition_prefix_sha256:
case condition_threshold_sha256:
case condition_rsa_sha256:
case condition_ed25519:
return true;
default:
return false;
}
}
/** Load a serialized condition either from its string or binary form */
/** @{ */
boost::optional<Condition>
loadCondition(std::string const& s);
boost::optional<Condition>
loadCondition(Slice s);
/** @} */
// Convert a condition to its string form
std::string
to_string (Condition const& c);
// Convert a condition to its binary form
std::vector<std::uint8_t>
to_blob (Condition const& c);
} }
} }

View File

@@ -1,100 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONDITIONS_ED25519_H
#define RIPPLE_CONDITIONS_ED25519_H
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <array>
#include <cstdint>
namespace ripple {
namespace cryptoconditions {
class Ed25519 final
: public Fulfillment
{
static std::size_t constexpr signature_size_ = 64;
static std::size_t constexpr pubkey_size_ = 32;
std::array<std::uint8_t,
pubkey_size_ + signature_size_> payload_;
public:
Ed25519 () = default;
/** Create a fulfillment given a keypair and the message */
Ed25519 (
SecretKey const& secretKey,
PublicKey const& publicKey,
Slice message);
/** Create a fulfillment given a secret key and the message */
Ed25519 (
SecretKey const& secretKey,
Slice message);
Condition
condition() const override;
std::uint16_t
type () const override
{
return condition_ed25519;
}
std::uint32_t
features () const override
{
return feature_ed25519;
}
bool
ok () const override
{
return true;
}
std::size_t
payloadSize () const override
{
return payload_.size();
}
Buffer
payload() const override
{
return { payload_.data(), payload_.size() };
}
bool
validate (Slice data) const override;
bool
parsePayload (Slice s) override;
};
}
}
#endif

View File

@@ -31,69 +31,86 @@ namespace cryptoconditions {
struct Fulfillment struct Fulfillment
{ {
public:
/** The largest binary fulfillment we support.
@note This value will be increased in the future, but it
must never decrease, as that could cause fulfillments
that were previously considered valid to no longer
be allowed.
*/
static constexpr std::size_t maxSerializedFulfillment = 256;
/** Load a fulfillment from its binary form
@param s The buffer containing the fulfillment to load.
@param ec Set to the error, if any occurred.
The binary format for a fulfillment is specified in the
cryptoconditions RFC. See:
https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.3
*/
static
std::unique_ptr<Fulfillment>
deserialize(
Slice s,
std::error_code& ec);
public: public:
virtual ~Fulfillment() = default; virtual ~Fulfillment() = default;
Fulfillment () = default; /** Returns the fulfillment's fingerprint:
/** Returns the size of the fulfillment's payload. */ The fingerprint is an octet string uniquely
virtual representing this fulfillment's condition
std::size_t with respect to other conditions of the
payloadSize() const = 0; same type.
*/
/** Returns the fulfillment's payload */
virtual virtual
Buffer Buffer
payload() const = 0; fingerprint() const = 0;
/** Generates the condition */ /** Returns the type of this condition. */
virtual virtual
Condition Type
condition() const = 0;
/** Returns the type */
virtual
std::uint16_t
type () const = 0; type () const = 0;
/** Returns the features suites required.
For any given fulfillment, the result includes all
the feature suites that an implementation must
support in order to be able to successfully parse
the fulfillment.
@note fulfillments of the same type may require
different features.
*/
virtual
std::uint32_t
features () const = 0;
/** Determines if this fulfillment is well-formed */
virtual
bool
ok () const = 0;
/** Validates a fulfillment. */ /** Validates a fulfillment. */
virtual virtual
bool bool
validate (Slice data) const = 0; validate (Slice data) const = 0;
/** Parses the fulfillment's payload. */ /** Calculates the cost associated with this fulfillment. *
The cost function is deterministic and depends on the
type and properties of the condition and the fulfillment
that the condition is generated from.
*/
virtual virtual
bool std::uint32_t
parsePayload (Slice s) = 0; cost() const = 0;
/** Returns the condition associated with the given fulfillment.
This process is completely deterministic. All implementations
will, if compliant, produce the identical condition for the
same fulfillment.
*/
virtual
Condition
condition() const = 0;
}; };
inline inline
bool bool
operator== (Fulfillment const& lhs, Fulfillment const& rhs) operator== (Fulfillment const& lhs, Fulfillment const& rhs)
{ {
// FIXME: for compound conditions, need to also check subtypes
return return
lhs.type() == rhs.type() && lhs.type() == rhs.type() &&
lhs.ok() == rhs.ok() && lhs.cost() == rhs.cost() &&
lhs.payload() == rhs.payload(); lhs.fingerprint() == rhs.fingerprint();
} }
inline inline
@@ -103,37 +120,9 @@ operator!= (Fulfillment const& lhs, Fulfillment const& rhs)
return !(lhs == rhs); return !(lhs == rhs);
} }
/** Load a fulfillment from its string serialization. /** Determine whether the given fulfillment and condition match */
The format is specified in Section 2.5.1 of the
cryptoconditions RFC:
https://tools.ietf.org/html/draft-thomas-crypto-conditions-00#section-2.5.1
*/
std::unique_ptr<Fulfillment>
loadFulfillment (std::string const& s);
/** Load a fulfillment from its binary serialization.
The format is specified in Section 2.5.2 of the
cryptoconditions RFC:
https://tools.ietf.org/html/draft-thomas-crypto-conditions-00#section-2.5.2
*/
std::unique_ptr<Fulfillment>
loadFulfillment (Slice s);
// Convert a fulfillment to its string form
std::string
to_string (Fulfillment const& f);
// Convert a fulfillment to its binary form
std::vector<std::uint8_t>
to_blob (Fulfillment const& f);
/** Determine whether a fulfillment fulfills a given condition */
bool bool
fulfills ( match (
Fulfillment const& f, Fulfillment const& f,
Condition const& c); Condition const& c);
@@ -141,10 +130,11 @@ fulfills (
@param f The fulfillment @param f The fulfillment
@param c The condition @param c The condition
@param m The message; note that the message is not @param m The message
relevant for some conditions (e.g. hashlocks)
and a fulfillment will successfully satisfy its @note the message is not relevant for some conditions
condition for any given message. and a fulfillment will successfully satisfy its
condition for any given message.
*/ */
bool bool
validate ( validate (
@@ -166,7 +156,7 @@ validate (
@param c The condition @param c The condition
*/ */
bool bool
validateTrigger ( validate (
Fulfillment const& f, Fulfillment const& f,
Condition const& c); Condition const& c);

View File

@@ -1,218 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONDITIONS_PREFIX_SHA256_H
#define RIPPLE_CONDITIONS_PREFIX_SHA256_H
#include <ripple/basics/base_uint.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/protocol/digest.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>
namespace ripple {
namespace cryptoconditions {
class PrefixSha256 final
: public Fulfillment
{
Buffer prefix_;
std::unique_ptr<Fulfillment> subfulfillment_;
public:
PrefixSha256 () = default;
std::size_t
payloadSize () const override
{
return
oer::predict_octetstring_size(prefix_.size()) +
subfulfillment_->payloadSize();
}
Buffer
payload() const override
{
// We should never have a condition in a state that
// isn't ok to call payload on.
if (!ok())
return {};
auto const subpayload = to_blob (*subfulfillment_);
Buffer b (subpayload.size() +
oer::predict_octetstring_size (prefix_.size()));
auto out = oer::encode_octetstring (
prefix_.size(),
prefix_.data(),
prefix_.data() + prefix_.size(),
b.data());
std::memcpy (out, subpayload.data(), subpayload.size());
return b;
}
Condition
condition() const override
{
auto const sc = subcondition();
auto const blob = to_blob (sc);
Buffer b (blob.size() +
oer::predict_octetstring_size (prefix_.size()));
auto out = oer::encode_octetstring (
prefix_.size(),
prefix_.data(),
prefix_.data() + prefix_.size(),
b.data());
std::memcpy (out, blob.data(), blob.size());
sha256_hasher h;
h (b.data(), b.size());
Condition cc;
cc.type = type();
cc.featureBitmask = features();
cc.maxFulfillmentLength = payloadSize();
cc.fingerprint = static_cast<sha256_hasher::result_type>(h);
return cc;
}
std::uint16_t
type () const override
{
return condition_prefix_sha256;
}
std::uint32_t
features () const override
{
return
feature_sha256 |
feature_prefix |
subfulfillment_->features();
}
bool
ok () const override
{
return static_cast<bool>(subfulfillment_);
}
bool
validate (Slice m) const override
{
if (!ok())
return false;
// Prepend the prefix to the message:
Buffer b (prefix_.size() + m.size());
if (prefix_.size())
std::memcpy (b.data(), prefix_.data(), prefix_.size());
if (m.size())
std::memcpy (b.data() + prefix_.size(), m.data(), m.size());
return subfulfillment_->validate (b);
}
Fulfillment const&
subfulfillment () const
{
return *subfulfillment_;
}
Condition
subcondition () const
{
return subfulfillment_->condition();
}
bool
parsePayload (Slice s) override
{
// The payload consists of the prefix, followed by
// a subfulfillment. It cannot be empty:
if (s.empty())
return false;
auto start = s.data();
auto finish = s.data() + s.size();
std::size_t len;
std::tie (start, len) = oer::decode_length (
start, finish);
if (len != 0)
{
if (std::distance (start, finish) < len)
return false;
std::memcpy (prefix_.alloc (len), start, len);
std::advance (start, len);
}
s += std::distance (s.data(), start);
// The remaining bytes in the slice are a fulfillment
// so we parse it as such. If we can, then we've
// succeeded.
subfulfillment_ = loadFulfillment (s);
if (!subfulfillment_)
{
prefix_.clear();
return false;
}
return true;
}
void setPrefix (Slice prefix)
{
prefix_ = prefix;
}
Slice prefix() const
{
return prefix_;
}
void setSubfulfillment (std::unique_ptr<Fulfillment> subfulfillment)
{
subfulfillment_ = std::move (subfulfillment);
}
};
}
}
#endif

View File

@@ -1,123 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONDITIONS_PREIMAGE_SHA256_H
#define RIPPLE_CONDITIONS_PREIMAGE_SHA256_H
#include <ripple/basics/base_uint.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/protocol/digest.h>
#include <algorithm>
#include <iterator>
#include <vector>
namespace ripple {
namespace cryptoconditions {
class PreimageSha256 final
: public Fulfillment
{
Buffer payload_;
public:
PreimageSha256() = default;
PreimageSha256 (Slice s)
: payload_ (s)
{
// TODO: We don't want to throw. Devise better
// interface for constructing hashlock from
// given buffer.
if (payload_.size() > maxSupportedFulfillmentLength)
throw std::length_error (
"Maximum fulfillment length exceeded");
}
std::size_t
payloadSize () const override
{
return payload_.size();
}
Buffer
payload() const override
{
return { payload_.data(), payload_.size() };
}
Condition
condition() const override
{
sha256_hasher h;
h (payload_.data(), payload_.size());
Condition cc;
cc.type = type();
cc.featureBitmask = features();
cc.maxFulfillmentLength = payload_.size();
cc.fingerprint = static_cast<sha256_hasher::result_type>(h);
return cc;
}
std::uint16_t
type () const override
{
return condition_hashlock;
}
std::uint32_t
features () const override
{
return feature_sha256 | feature_preimage;
}
bool
ok () const override
{
return true;
}
bool
validate (Slice) const override
{
// Perhaps counterintuitively, the message isn't
// relevant.
return true;
}
bool
parsePayload (Slice s) override
{
// The payload may be empty
if (s.size() > maxSupportedFulfillmentLength)
return false;
payload_ = s;
return true;
}
};
}
}
#endif

View File

@@ -20,6 +20,7 @@
#include <ripple/basics/contract.h> #include <ripple/basics/contract.h>
#include <ripple/conditions/Condition.h> #include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h> #include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/impl/PreimageSha256.h>
#include <ripple/conditions/impl/utils.h> #include <ripple/conditions/impl/utils.h>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@@ -29,160 +30,212 @@
namespace ripple { namespace ripple {
namespace cryptoconditions { namespace cryptoconditions {
boost::optional<Condition> namespace detail {
loadCondition(std::string const& s) // The binary encoding of conditions differs based on their
// type. All types define at least a fingerprint and cost
// sub-field. Some types, such as the compound condition
// types, define additional sub-fields that are required to
// convey essential properties of the cryptocondition (such
// as the sub-types used by sub-conditions in the case of
// the compound types).
//
// Conditions are encoded as follows:
//
// Condition ::= CHOICE {
// preimageSha256 [0] SimpleSha256Condition,
// prefixSha256 [1] CompoundSha256Condition,
// thresholdSha256 [2] CompoundSha256Condition,
// rsaSha256 [3] SimpleSha256Condition,
// ed25519Sha256 [4] SimpleSha256Condition
// }
//
// SimpleSha256Condition ::= SEQUENCE {
// fingerprint OCTET STRING (SIZE(32)),
// cost INTEGER (0..4294967295)
// }
//
// CompoundSha256Condition ::= SEQUENCE {
// fingerprint OCTET STRING (SIZE(32)),
// cost INTEGER (0..4294967295),
// subtypes ConditionTypes
// }
//
// ConditionTypes ::= BIT STRING {
// preImageSha256 (0),
// prefixSha256 (1),
// thresholdSha256 (2),
// rsaSha256 (3),
// ed25519Sha256 (4)
// }
constexpr std::size_t fingerprintSize = 32;
std::unique_ptr<Condition>
loadSimpleSha256(Type type, Slice s, std::error_code& ec)
{ {
static boost::regex const re_current ( using namespace der;
"^" // start of line
"cc:" // 'cc' for cryptocondition
"([1-9a-f][0-9a-f]{0,3}|0):" // type (hexadecimal)
"([1-9a-f][0-9a-f]{0,15}):" // feature bitmask (hexadecimal)
"([a-zA-Z0-9_-]{0,86}):" // fingerprint (base64url)
"([1-9][0-9]{0,17}|0)" // fulfillment length (decimal)
"$" // end of line
, boost::regex_constants::optimize
);
boost::smatch match; auto p = parsePreamble(s, ec);
if (!boost::regex_match (s, match, re_current)) if (ec)
return boost::none; return {};
try if (!isPrimitive(p) || !isContextSpecific(p))
{ {
Condition c; ec = error::incorrect_encoding;
return {};
c.type = parse_hexadecimal<std::uint16_t> (match[1]);
if (!isCondition (c.type))
return boost::none;
c.featureBitmask = parse_hexadecimal<std::uint32_t>(match[2]);
c.maxFulfillmentLength = parse_decimal<std::uint16_t>(match[4]);
if (c.maxFulfillmentLength > maxSupportedFulfillmentLength)
return boost::none;
// TODO: Avoid copying by decoding directly
// into the condition's buffer
auto fingerprint = base64url_decode(match[3]);
if (fingerprint.size() != c.fingerprint.size())
return boost::none;
std::memcpy(
c.fingerprint.data(),
fingerprint.data(),
fingerprint.size());
return c;
} }
catch (std::exception const&)
if (p.tag != 0)
{ {
return boost::none; ec = error::unexpected_tag;
return {};
} }
}
boost::optional<Condition> if (p.length != fingerprintSize)
loadCondition(Slice s)
{
if (s.empty())
return boost::none;
try
{ {
auto start = s.data(); ec = error::fingerprint_size;
auto finish = s.data() + s.size(); return {};
}
Condition c; Buffer b = parseOctetString(s, p.length, ec);
std::tie (start, c.type) = if (ec)
oer::decode_integer<std::uint16_t> ( return {};
start, finish);
if (!isCondition (c.type)) p = parsePreamble(s, ec);
return boost::none;
std::tie (start, c.featureBitmask) = if (ec)
oer::decode_varuint<std::uint32_t> ( return {};
start, finish);
if (!isPrimitive(p) || !isContextSpecific(p))
{
ec = error::malformed_encoding;
return{};
}
if (p.tag != 1)
{
ec = error::unexpected_tag;
return {};
}
auto cost = parseInteger<std::uint32_t>(s, p.length, ec);
if (ec)
return {};
if (!s.empty())
{
ec = error::trailing_garbage;
return {};
}
switch (type)
{
case Type::preimageSha256:
if (cost > PreimageSha256::maxPreimageLength)
{ {
std::size_t len; ec = error::preimage_too_long;
return {};
std::tie (start, len) =
oer::decode_length (start, finish);
// Incorrect signature length
if (len != c.fingerprint.size())
return boost::none;
// Short buffer
if (std::distance (start, finish) < len)
return boost::none;
auto p = c.fingerprint.data();
while (len--)
*p++ = *start++;
} }
break;
if (start == finish) default:
return boost::none; break;
std::tie (start, c.maxFulfillmentLength) =
oer::decode_varuint<std::uint16_t> (
start, finish);
// The maximum supported length of a fulfillment is
// the largest allowable value, so checking here is
// not helpful.
return c;
} }
catch (std::exception const&)
return std::make_unique<Condition>(type, cost, std::move(b));
}
}
std::unique_ptr<Condition>
Condition::deserialize(Slice s, std::error_code& ec)
{
// Per the RFC, in a condition we choose a type based
// on the tag of the item we contain:
//
// Condition ::= CHOICE {
// preimageSha256 [0] SimpleSha256Condition,
// prefixSha256 [1] CompoundSha256Condition,
// thresholdSha256 [2] CompoundSha256Condition,
// rsaSha256 [3] SimpleSha256Condition,
// ed25519Sha256 [4] SimpleSha256Condition
// }
if (s.empty())
{ {
return boost::none; ec = error::buffer_empty;
return {};
} }
}
std::string using namespace der;
to_string (Condition const& c)
{
return std::string("cc:") +
to_hex (c.type) + ":" +
to_hex (c.featureBitmask) + ":" +
base64url_encode(c.fingerprint) + ":" +
to_dec (c.maxFulfillmentLength);
}
std::vector<std::uint8_t> auto const p = parsePreamble(s, ec);
to_blob (Condition const& c) if (ec)
{ return {};
// TODO: optimize this
std::vector<std::uint8_t> v;
v.reserve (48);
oer::encode_integer ( // All fulfillments are context-specific, constructed
c.type, // types
std::back_inserter(v)); if (!isConstructed(p) || !isContextSpecific(p))
{
ec = error::malformed_encoding;
return {};
}
oer::encode_varuint ( if (p.length > s.size())
c.featureBitmask, {
std::back_inserter(v)); ec = error::buffer_underfull;
return {};
}
oer::encode_octetstring ( if (s.size() > maxSerializedCondition)
c.fingerprint.size(), {
c.fingerprint.begin(), ec = error::large_size;
c.fingerprint.end(), return {};
std::back_inserter(v)); }
oer::encode_varuint ( std::unique_ptr<Condition> c;
c.maxFulfillmentLength,
std::back_inserter(v));
return v; switch (p.tag)
{
case 0: // PreimageSha256
c = detail::loadSimpleSha256(
Type::preimageSha256,
Slice(s.data(), p.length), ec);
if (!ec)
s += p.length;
break;
case 1: // PrefixSha256
ec = error::unsupported_type;
return {};
case 2: // ThresholdSha256
ec = error::unsupported_type;
return {};
case 3: // RsaSha256
ec = error::unsupported_type;
return {};
case 4: // Ed25519Sha256
ec = error::unsupported_type;
return {};
default:
ec = error::unknown_type;
return {};
}
if (!s.empty())
{
ec = error::trailing_garbage;
return {};
}
return c;
} }
} }
} }

View File

@@ -1,115 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/basics/contract.h>
#include <ed25519-donna/ed25519.h>
namespace ripple {
namespace cryptoconditions {
Ed25519::Ed25519 (
SecretKey const& secretKey,
PublicKey const& publicKey,
Slice message)
{
if (publicKeyType (publicKey) != KeyType::ed25519)
LogicError ("An Ed25519 public key is required.");
// When PublicKey wraps an Ed25519 key it prefixes
// the key itself with a 0xED byte. We carefully
// skip that byte.
std::memcpy (
payload_.data(),
publicKey.data() + 1,
publicKey.size() - 1);
// Now sign:
ed25519_sign (
message.data(),
message.size(),
secretKey.data(),
payload_.data(),
payload_.data() + pubkey_size_);
}
/** Create a fulfillment given a secret key and the message */
Ed25519::Ed25519 (
SecretKey const& secretKey,
Slice message)
{
// First derive the public key, and place it in the
// payload:
ed25519_publickey (
secretKey.data(),
payload_.data());
ed25519_sign (
message.data(),
message.size(),
secretKey.data(),
payload_.data(),
payload_.data() + pubkey_size_);
}
Condition
Ed25519::condition() const
{
Condition cc;
cc.type = type();
cc.featureBitmask = features();
cc.maxFulfillmentLength = payloadSize();
std::memcpy (
cc.fingerprint.data(),
payload_.data(),
pubkey_size_);
return cc;
}
bool
Ed25519::validate (Slice data) const
{
return ed25519_sign_open (
data.data(),
data.size(),
payload_.data(),
payload_.data() + pubkey_size_) == 0;
}
bool
Ed25519::parsePayload (Slice s)
{
// The payload consists of 96 consecutive bytes:
// The public key is the first 32 and the
// remaining 64 bytes are the signature.
if (s.size() != sizeof(payload_))
return false;
std::memcpy (payload_.data(), s.data(), s.size());
return true;
}
}
}

View File

@@ -19,10 +19,7 @@
#include <ripple/conditions/Condition.h> #include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h> #include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/PreimageSha256.h> #include <ripple/conditions/impl/PreimageSha256.h>
#include <ripple/conditions/PrefixSha256.h>
#include <ripple/conditions/RsaSha256.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/conditions/impl/utils.h> #include <ripple/conditions/impl/utils.h>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@@ -33,7 +30,7 @@ namespace ripple {
namespace cryptoconditions { namespace cryptoconditions {
bool bool
fulfills ( match (
Fulfillment const& f, Fulfillment const& f,
Condition const& c) Condition const& c)
{ {
@@ -42,16 +39,9 @@ fulfills (
if (f.type() != c.type) if (f.type() != c.type)
return false; return false;
// Ensure that the condition is well-formed // Derive the condition from the given fulfillment
if (!validate (c)) // and ensure that it matches the given condition.
return false; return c == f.condition();
// The fulfillment payload can be no larger than the
// what the condition allows.
if (f.payloadSize() > c.maxFulfillmentLength)
return false;
return f.condition() == c;
} }
bool bool
@@ -60,11 +50,11 @@ validate (
Condition const& c, Condition const& c,
Slice m) Slice m)
{ {
return fulfills (f, c) && f.validate (m); return match (f, c) && f.validate (m);
} }
bool bool
validateTrigger ( validate (
Fulfillment const& f, Fulfillment const& f,
Condition const& c) Condition const& c)
{ {
@@ -72,157 +62,100 @@ validateTrigger (
} }
std::unique_ptr<Fulfillment> std::unique_ptr<Fulfillment>
loadFulfillment (std::uint16_t type, Slice payload) Fulfillment::deserialize(
Slice s,
std::error_code& ec)
{ {
std::unique_ptr<Fulfillment> p; // Per the RFC, in a fulfillment we choose a type based
// on the tag of the item we contain:
//
// Fulfillment ::= CHOICE {
// preimageSha256 [0] PreimageFulfillment ,
// prefixSha256 [1] PrefixFulfillment,
// thresholdSha256 [2] ThresholdFulfillment,
// rsaSha256 [3] RsaSha256Fulfillment,
// ed25519Sha256 [4] Ed25519Sha512Fulfillment
// }
switch (type) if (s.empty())
{ {
case condition_hashlock: ec = error::buffer_empty;
p = std::make_unique<PreimageSha256>(); return nullptr;
}
using namespace der;
auto const p = parsePreamble(s, ec);
if (ec)
return nullptr;
// All fulfillments are context-specific, constructed types
if (!isConstructed(p) || !isContextSpecific(p))
{
ec = error::malformed_encoding;
return nullptr;
}
if (p.length > s.size())
{
ec = error::buffer_underfull;
return {};
}
if (p.length < s.size())
{
ec = error::buffer_overfull;
return {};
}
if (p.length > maxSerializedFulfillment)
{
ec = error::large_size;
return {};
}
std::unique_ptr<Fulfillment> f;
switch (static_cast<Type>(p.tag))
{
case Type::preimageSha256:
f = PreimageSha256::deserialize(Slice(s.data(), p.length), ec);
if (ec)
return {};
s += p.length;
break; break;
case condition_prefix_sha256: case Type::prefixSha256:
p = std::make_unique<PrefixSha256>(); ec = error::unsupported_type;
return {};
break; break;
case condition_rsa_sha256: case Type::thresholdSha256:
p = std::make_unique<RsaSha256>(); ec = error::unsupported_type;
return {};
break; break;
case condition_ed25519: case Type::rsaSha256:
p = std::make_unique<Ed25519>(); ec = error::unsupported_type;
return {};
break; break;
case Type::ed25519Sha256:
ec = error::unsupported_type;
return {};
default: default:
throw std::domain_error ( ec = error::unknown_type;
"Unknown cryptocondition type " + return {};
std::to_string (type));
} }
// If the payload can't be parsed, the load should if (!s.empty())
// fail.
if (p && !p->parsePayload(payload))
p.reset();
return p;
}
// Parse a condition from its string form
std::unique_ptr<Fulfillment>
loadFulfillment (std::string const& s)
{
// CHECKME: custom parser maybe? probably faster but
// more work and probability of error.
// TODO: use two regex: one that accepts anything the
// standard supports and one which accepts only what
// we support. Parse with both for improved errors?
static boost::regex const re_current (
"^" // start of line
"cf:" // 'cf' for fulfillment
"([1-9a-f][0-9a-f]{0,3}|0):" // type
"([a-zA-Z0-9_-]*)" // fulfillment payload (base64url)
"$" // end of line
, boost::regex_constants::optimize
);
try
{ {
boost::smatch match; ec = error::trailing_garbage;
return {};
if (!boost::regex_match (s, match, re_current))
return nullptr;
std::uint16_t const type =
parse_hexadecimal<std::uint16_t>(match[1]);
auto payload = base64url_decode (match[2]);
if (payload.size() > maxSupportedFulfillmentLength)
return nullptr;
return loadFulfillment (type, makeSlice (payload));
} }
catch (std::exception const&)
{
return nullptr;
}
}
std::unique_ptr<Fulfillment> return f;
loadFulfillment (Slice s)
{
if (s.empty())
return nullptr;
try
{
auto start = s.data();
auto finish = s.data() + s.size();
std::uint16_t type;
std::size_t len;
std::tie (start, type) =
oer::decode_integer<std::uint16_t> (
start, finish);
if (!isCondition (type))
return nullptr;
if (start == finish)
return nullptr;
std::tie (start, len) =
oer::decode_length(
start, finish);
if (len)
{
if (len > maxSupportedFulfillmentLength)
return nullptr;
if (std::distance (start, finish) < len)
return nullptr;
}
return loadFulfillment (type, Slice{ start, len });
}
catch (std::exception const&)
{
return nullptr;
}
}
std::string
to_string (Fulfillment const& f)
{
return std::string("cf:") + to_hex(f.type())
+ ":" + base64url_encode (f.payload());
}
std::vector<std::uint8_t>
to_blob (Fulfillment const& f)
{
// NIKB TODO optimize this
std::vector<std::uint8_t> v;
auto const p = f.payload();
oer::encode_integer (
f.type(),
std::back_inserter(v));
oer::encode_length (
p.size(), std::back_inserter(v));
oer::encode_octetstring (
p.data(),
p.data() + p.size(),
std::back_inserter(v));
return v;
} }
} }

View File

@@ -0,0 +1,158 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONDITIONS_PREIMAGE_SHA256_H
#define RIPPLE_CONDITIONS_PREIMAGE_SHA256_H
#include <ripple/basics/Buffer.h>
#include <ripple/basics/Slice.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/impl/error.h>
#include <ripple/protocol/digest.h>
#include <memory>
namespace ripple {
namespace cryptoconditions {
class PreimageSha256 final
: public Fulfillment
{
public:
/** The maximum allowed length of a preimage.
The specification does not specify a minimum supported
length, nor does it require all conditions to support
the same minimum length.
While future versions of this code will never lower
this limit, they may opt to raise it.
*/
static constexpr std::size_t maxPreimageLength = 128;
/** Parse the payload for a PreimageSha256 condition
@param s A slice containing the DER encoded payload
@param ec indicates success or failure of the operation
@return the preimage, if successful; empty pointer otherwise.
*/
static
std::unique_ptr<Fulfillment>
deserialize(
Slice s,
std::error_code& ec)
{
// Per the RFC, a preimage fulfulliment is defined as
// follows:
//
// PreimageFulfillment ::= SEQUENCE {
// preimage OCTET STRING
// }
using namespace der;
auto p = parsePreamble(s, ec);
if (ec)
return nullptr;
if (!isPrimitive(p) || !isContextSpecific(p))
{
ec = error::incorrect_encoding;
return {};
}
if (p.tag != 0)
{
ec = error::unexpected_tag;
return {};
}
if (s.size() != p.length)
{
ec = error::trailing_garbage;
return {};
}
if (s.size() > maxPreimageLength)
{
ec = error::preimage_too_long;
return {};
}
auto b = parseOctetString(s, p.length, ec);
if (ec)
return {};
return std::make_unique<PreimageSha256>(std::move(b));
}
private:
Buffer payload_;
public:
PreimageSha256(Buffer&& b) noexcept
: payload_(std::move(b))
{
}
PreimageSha256(Slice s) noexcept
: payload_(s)
{
}
Type
type() const override
{
return Type::preimageSha256;
}
Buffer
fingerprint() const override
{
sha256_hasher h;
h(payload_.data(), payload_.size());
auto const d = static_cast<sha256_hasher::result_type>(h);
return{ d.data(), d.size() };
}
std::uint32_t
cost() const override
{
return static_cast<std::uint32_t>(payload_.size());
}
Condition
condition() const override
{
return { type(), cost(), fingerprint() };
}
bool
validate(Slice) const override
{
// Perhaps counterintuitively, the message isn't
// relevant.
return true;
}
};
}
}
#endif

View File

@@ -1,314 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/RsaSha256.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/protocol/digest.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <boost/algorithm/clamp.hpp>
#include <algorithm>
#include <iterator>
namespace ripple {
namespace cryptoconditions {
namespace detail {
struct rsa_deleter
{
void operator() (RSA* rsa) const
{
RSA_free (rsa);
}
};
using RsaKey = std::unique_ptr<RSA, rsa_deleter>;
struct bn_deleter
{
void operator() (BIGNUM* bn) const
{
BN_free (bn);
}
};
using BigNum = std::unique_ptr<BIGNUM, bn_deleter>;
// Check whether the public modulus meets the length
// requirements imposed by section 4.4.1 of the RFC.
bool
checkModulusLength (int len)
{
if (len <= 0)
return false;
return len == boost::algorithm::clamp(len, 128, 512);
}
bool
signHelper (
RSA* key,
Slice message,
Buffer& modulus,
Buffer& signature)
{
int const keySize = RSA_size(key);
if (!checkModulusLength (keySize))
return false;
sha256_hasher h;
h (message.data(), message.size());
auto digest = static_cast<sha256_hasher::result_type>(h);
Buffer buf;
// Pad the result (-1 -> use hash length as salt length)
if (!RSA_padding_add_PKCS1_PSS(key,
buf.alloc(keySize), digest.data(),
EVP_sha256(), -1))
return false;
// Sign - we've manually padded the input already.
auto ret = RSA_private_encrypt(keySize, buf.data(),
signature.alloc (buf.size()), key, RSA_NO_PADDING);
if (ret == -1)
return false;
BN_bn2bin (key->n, modulus.alloc(BN_num_bytes (key->n)));
return true;
}
bool
validateHelper (
RSA* key,
Slice message,
Slice signature)
{
int const keySize = RSA_size(key);
if (!checkModulusLength (keySize))
return false;
Buffer buf;
auto ret = RSA_public_decrypt(
keySize,
signature.data(),
buf.alloc (keySize),
key,
RSA_NO_PADDING);
if (ret == -1)
return false;
sha256_hasher h;
h (message.data(), message.size());
auto digest = static_cast<sha256_hasher::result_type>(h);
return RSA_verify_PKCS1_PSS(key, digest.data(), EVP_sha256(), buf.data(), -1) == 1;
}
bool
parsePayloadHelper(
Slice s,
Buffer& modulus,
Buffer& signature)
{
auto start = s.data ();
auto finish = s.data () + s.size();
std::size_t len;
std::tie (start, len) = oer::decode_length (
start, finish);
if (std::distance (start, finish) < len)
return false;
std::memcpy (modulus.alloc (len), start, len);
std::advance (start, len);
std::tie (start, len) = oer::decode_length (
start, finish);
if (std::distance (start, finish) < len)
return false;
std::memcpy (signature.alloc (len), start, len);
std::advance (start, len);
// Per 4.4.2 of the RFC we must check whether the
// signature and modulus consist of the same number
// of octets:
if (signature.size() != modulus.size())
return false;
// Enforce constraints from the RFC:
BigNum sig (BN_bin2bn (
signature.data(), signature.size(), nullptr));
BigNum mod (BN_bin2bn (
modulus.data(), modulus.size(), nullptr));
if (!sig || !mod)
return false;
// Per 4.4.1 of the RFC we are required to reject
// moduli smaller than 128 bytes or greater than
// 512 bytes.
int modBytes = BN_num_bytes (mod.get());
if (!checkModulusLength (modBytes))
return false;
// Per 4.4.2 of the RFC we must check that the signature
// is numerically less than the modulus:
return BN_cmp (sig.get(), mod.get()) < 0;
}
}
Condition
RsaSha256::condition() const
{
std::vector<std::uint8_t> m;
m.reserve (1024);
oer::encode_octetstring (
modulus_.size(),
modulus_.data(),
modulus_.data() + modulus_.size(),
std::back_inserter(m));
sha256_hasher h;
h (m.data(), m.size());
Condition cc;
cc.type = type();
cc.featureBitmask = features();
cc.maxFulfillmentLength = payloadSize();
cc.fingerprint = static_cast<sha256_hasher::result_type>(h);
return cc;
}
std::size_t
RsaSha256::payloadSize () const
{
return
oer::predict_octetstring_size (modulus_.size()) +
oer::predict_octetstring_size (signature_.size());
}
Buffer
RsaSha256::payload() const
{
Buffer b (payloadSize());
auto out = oer::encode_octetstring (
modulus_.size(),
modulus_.data(),
modulus_.data() + modulus_.size(),
b.data());
oer::encode_octetstring (
signature_.size(),
signature_.data(),
signature_.data() + modulus_.size(),
out);
return b;
}
bool
RsaSha256::validate (Slice data) const
{
if (!ok())
return false;
detail::RsaKey rsa (RSA_new());
rsa->n = BN_new();
BN_bin2bn(modulus_.data(), modulus_.size(), rsa->n);
rsa->e = BN_new();
BN_set_word (rsa->e, 65537);
return detail::validateHelper (rsa.get(), data, signature_);
}
/** Sign the given message with an RSA key */
bool
RsaSha256::sign (
std::string const& key,
Slice message)
{
// This ugly const_cast/reinterpret_cast is needed
// on some machines. Although the documentation
// suggests that the function accepts a void const*
// argument, apparently some platforms have OpenSSL
// libraries that are up-to-date but accept void*.
auto bio = BIO_new_mem_buf(
const_cast<void*>(static_cast<void const*>(key.data())),
key.size());
if (!bio)
return false;
detail::RsaKey rsa (PEM_read_bio_RSAPrivateKey(
bio, NULL, NULL, NULL));
BIO_free(bio);
if (!rsa)
return false;
if (detail::signHelper (rsa.get(), message, modulus_, signature_))
return true;
modulus_.clear();
signature_.clear();
return false;
}
bool
RsaSha256::parsePayload (Slice s)
{
// The payload may not be empty
if (!s.empty() && detail::parsePayloadHelper (s, modulus_, signature_))
return true;
// Clear the state
modulus_.clear();
signature_.clear();
return false;
}
}
}

View File

@@ -1,204 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BASE64_HPP
#define BEAST_DETAIL_BASE64_HPP
#include <ripple/basics/Buffer.h>
#include <ripple/basics/Slice.h>
#include <array>
#include <cctype>
#include <string>
#include <cstdint>
#include <vector>
/*
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
Copyright notice:
base64.cpp and base64.h
Copyright (C) 2004-2008 Ren<65> Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
namespace ripple {
namespace cryptoconditions {
// NIKB NOTE: This has *NOT* standard base64 - it's base64url,
// which replaces the `+` with a `-` and the the `/` with a `_`
// with the padding suppressed on encoding and rejected on
// decoding.
template <class = void>
std::string const&
base64url_alphabet()
{
static std::string const alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
return alphabet;
}
inline
bool
is_base64url(unsigned char c)
{
return (std::isalnum(c) || (c == '-') || (c == '_'));
}
template <class = void>
std::string
base64url_encode (std::uint8_t const* data,
std::size_t in_len)
{
unsigned char c3[3], c4[4];
int i = 0;
int j = 0;
std::string ret;
ret.reserve (3 + in_len * 8 / 6);
char const* alphabet (base64url_alphabet().data());
while(in_len--)
{
c3[i++] = *(data++);
if(i == 3)
{
c4[0] = (c3[0] & 0xfc) >> 2;
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
c4[3] = c3[2] & 0x3f;
for(i = 0; (i < 4); i++)
ret += alphabet[c4[i]];
i = 0;
}
}
if(i)
{
for(j = i; j < 3; j++)
c3[j] = '\0';
c4[0] = (c3[0] & 0xfc) >> 2;
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
c4[3] = c3[2] & 0x3f;
for(j = 0; (j < i + 1); j++)
ret += alphabet[c4[j]];
}
return ret;
}
template <size_t N>
std::string
base64url_encode (std::array<std::uint8_t, N> const& d)
{
return base64url_encode (d.data(), d.size());
}
template <class = void>
std::string
base64url_encode (std::vector<std::uint8_t> const& d)
{
return base64url_encode (d.data(), d.size());
}
template <class = void>
std::string
base64url_encode (Buffer const& d)
{
return base64url_encode (d.data(), d.size());
}
template <class = void>
std::string
base64url_encode (Slice d)
{
return base64url_encode (d.data(), d.size());
}
template <class = void>
std::vector<std::uint8_t>
base64url_decode(std::string const& data)
{
int in_len = data.size();
std::uint8_t c4[4];
int i = 0;
int j = 0;
int in_ = 0;
std::vector<std::uint8_t> ret;
ret.reserve (in_len * 6 / 8);
while(in_len-- && is_base64url(data[in_]))
{
c4[i++] = data[in_]; in_++;
if(i == 4) {
for(i = 0; i < 4; i++)
c4[i] = static_cast<unsigned char>(
base64url_alphabet().find(c4[i]));
ret.push_back ((c4[0] << 2) + ((c4[1] & 0x30) >> 4));
ret.push_back (((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2));
ret.push_back (((c4[2] & 0x3) << 6) + c4[3]);
i = 0;
}
}
if(i)
{
for(j = i; j < 4; j++)
c4[j] = 0;
for(j = 0; j < 4; j++)
c4[j] = static_cast<unsigned char>(
base64url_alphabet().find(c4[j]));
std::uint8_t c3[3];
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
for(j = 0; (j < i - 1); j++)
ret.push_back (c3[j]);
}
return ret;
}
}
}
#endif

View File

@@ -0,0 +1,143 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/conditions/impl/error.h>
#include <system_error>
#include <string>
#include <type_traits>
namespace ripple {
namespace cryptoconditions {
namespace detail {
class cryptoconditions_error_category
: public std::error_category
{
public:
const char*
name() const noexcept override
{
return "cryptoconditions";
}
std::string
message(int ev) const override
{
switch (static_cast<error>(ev))
{
case error::unsupported_type:
return "Specification: Requested type not supported.";
case error::unsupported_subtype:
return "Specification: Requested subtype not supported.";
case error::unknown_type:
return "Specification: Requested type not recognized.";
case error::unknown_subtype:
return "Specification: Requested subtypes not recognized.";
case error::fingerprint_size:
return "Specification: Incorrect fingerprint size.";
case error::incorrect_encoding:
return "Specification: Incorrect encoding.";
case error::trailing_garbage:
return "Bad buffer: contains trailing garbage.";
case error::buffer_empty:
return "Bad buffer: no data.";
case error::buffer_overfull:
return "Bad buffer: overfull.";
case error::buffer_underfull:
return "Bad buffer: underfull.";
case error::malformed_encoding:
return "Malformed DER encoding.";
case error::unexpected_tag:
return "Malformed DER encoding: Unexpected tag.";
case error::short_preamble:
return "Malformed DER encoding: Short preamble.";
case error::long_tag:
return "Implementation limit: Overlong tag.";
case error::large_size:
return "Implementation limit: Large payload.";
case error::preimage_too_long:
return "Implementation limit: Specified preimage is too long.";
case error::generic:
default:
return "generic error";
}
}
std::error_condition
default_error_condition(int ev) const noexcept override
{
return std::error_condition{ ev, *this };
}
bool
equivalent(
int ev,
std::error_condition const& condition) const noexcept override
{
return &condition.category() == this &&
condition.value() == ev;
}
bool
equivalent(
std::error_code const& error,
int ev) const noexcept override
{
return &error.category() == this &&
error.value() == ev;
}
};
inline
std::error_category const&
get_cryptoconditions_error_category()
{
static cryptoconditions_error_category const cat{};
return cat;
}
} // detail
std::error_code
make_error_code(error ev)
{
return std::error_code {
static_cast<std::underlying_type<error>::type>(ev),
detail::get_cryptoconditions_error_category()
};
}
}
}

View File

@@ -17,67 +17,51 @@
*/ */
//============================================================================== //==============================================================================
#ifndef RIPPLE_CONDITIONS_RSA_SHA256_H #ifndef RIPPLE_CONDITIONS_ERROR_H
#define RIPPLE_CONDITIONS_RSA_SHA256_H #define RIPPLE_CONDITIONS_ERROR_H
#include <ripple/conditions/Condition.h> #include <system_error>
#include <ripple/conditions/Fulfillment.h> #include <string>
#include <ripple/basics/Buffer.h>
#include <cstdint>
namespace ripple { namespace ripple {
namespace cryptoconditions { namespace cryptoconditions {
class RsaSha256 final enum class error
: public Fulfillment
{ {
Buffer modulus_; generic = 1,
Buffer signature_; unsupported_type,
unsupported_subtype,
public: unknown_type,
RsaSha256 () = default; unknown_subtype,
fingerprint_size,
Condition incorrect_encoding,
condition() const override; trailing_garbage,
buffer_empty,
std::uint16_t buffer_overfull,
type () const override buffer_underfull,
{ malformed_encoding,
return condition_rsa_sha256; short_preamble,
} unexpected_tag,
long_tag,
std::uint32_t large_size,
features () const override preimage_too_long
{
return feature_rsa_pss | feature_sha256;
}
bool
ok () const override
{
return !modulus_.empty() && !signature_.empty();
}
std::size_t
payloadSize () const override;
Buffer
payload() const override;
bool
validate (Slice data) const override;
/** Sign the given message with an RSA key */
bool sign (
std::string const& key,
Slice message);
bool
parsePayload (Slice s) override;
}; };
} std::error_code
make_error_code(error ev);
} } // cryptoconditions
} // ripple
namespace std
{
template<>
struct is_error_code_enum<ripple::cryptoconditions::error>
{
static bool const value = true;
};
} // std
#endif #endif

View File

@@ -21,6 +21,9 @@
#define RIPPLE_CONDITIONS_UTILS_H #define RIPPLE_CONDITIONS_UTILS_H
#include <ripple/basics/strHex.h> #include <ripple/basics/strHex.h>
#include <ripple/conditions/impl/error.h>
#include <boost/dynamic_bitset.hpp>
#include <limits>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -31,338 +34,208 @@
namespace ripple { namespace ripple {
namespace cryptoconditions { namespace cryptoconditions {
inline // A collection of functions to decode binary blobs
std::string // encoded with X.690 Distinguished Encoding Rules.
hexstr (std::vector<std::uint8_t> const& data) //
// This is a very trivial decoder and only implements
// the bare minimum needed to support PreimageSha256.
namespace der {
// The preamble encapsulates the DER identifier and
// length octets:
struct Preamble
{ {
std::string s; std::uint8_t type = 0;
s.reserve (data.size() * 2); std::size_t tag = 0;
std::size_t length = 0;
};
for (auto d : data) inline
{ bool
s.push_back (charHex (d >> 4)); isPrimitive(Preamble const& p)
s.push_back (charHex (d & 15)); {
} return (p.type & 0x20) == 0;
return s;
} }
inline inline
std::vector<std::uint8_t> bool
hexblob (std::string const& s) isConstructed(Preamble const& p)
{ {
std::vector<std::uint8_t> result; return !isPrimitive(p);
result.reserve (1 + (s.size () / 2));
auto iter = s.cbegin ();
if (s.size () & 1)
{
int c = charUnHex (*iter++);
if (c < 0)
Throw<std::runtime_error>("Invalid hex in blob");
result.push_back(c);
}
while (iter != s.cend ())
{
int cHigh = charUnHex (*iter++);
if (cHigh < 0)
Throw<std::runtime_error>("Invalid hex in blob");
int cLow = charUnHex (*iter);
if (cLow < 0)
Throw<std::runtime_error>("Invalid hex in blob");
iter++;
result.push_back (
static_cast<std::uint8_t>(cHigh << 4) |
static_cast<std::uint8_t>(cLow));
}
return result;
} }
template <class T>
T parse_decimal(std::string const& s)
{
T t = 0;
for (auto const c : s)
{
if (c < '0' || c > '9')
throw std::domain_error ("invalid decimal digit");
t = (t * 10) + (c - '0');
}
return t;
}
template <class T>
T parse_hexadecimal(std::string const& s)
{
T t = 0;
for (auto const c : s)
{
if (c >= '0' && c <= '9')
t = (t * 16) + (c - '0');
else if (c >= 'a' && c <= 'f')
t = (t * 16) + 10 + (c - 'a');
else if (c >= 'A' && c <= 'F')
t = (t * 16) + 10 + (c - 'A');
else
throw std::domain_error ("invalid hexadecimal digit");
}
return t;
}
template <class Integer>
std::string
to_hex (Integer value)
{
std::stringstream ss;
ss << std::hex << value;
return ss.str();
}
template <class Integer>
std::string
to_dec (Integer value)
{
std::stringstream ss;
ss << std::dec << value;
return ss.str();
}
// ISO/IEC 8825/7 or ITU-T X.696: Octet Encoding Rules
// FIXME: This assumes a little-endian architecture!
namespace oer
{
// Simple conversion: write integer as big-endian byte stream
// This needs to be improved and optimized:
template <class Integer, class OutputIt>
void
encode_integer(Integer value, OutputIt out)
{
static_assert (
std::is_same<Integer, std::uint8_t>::value ||
std::is_same<Integer, std::uint16_t>::value ||
std::is_same<Integer, std::uint32_t>::value ||
std::is_same<Integer, std::uint64_t>::value,
"encode_integer accepts only std::uint{8,16,32,64}_t");
std::size_t n = sizeof(Integer);
while(n--)
{
*out++ = static_cast<std::uint8_t>(
(value >> (n * 8)) & 0xFF);
}
}
// Simple conversion: big-endian byte stream to integer
template <class Integer, class InputIt>
std::pair<InputIt, Integer>
decode_integer(InputIt begin, InputIt end)
{
static_assert (
std::is_same<Integer, std::uint8_t>::value ||
std::is_same<Integer, std::uint16_t>::value ||
std::is_same<Integer, std::uint32_t>::value ||
std::is_same<Integer, std::uint64_t>::value,
"decode_integer accepts only std::uint{8,16,32,64}_t");
std::size_t size = std::distance (begin, end);
if (size < sizeof(Integer))
Throw<std::length_error>("short integer: " + std::to_string(size));
Integer res = 0;
for (std::size_t i = 0; i < sizeof(Integer); ++i)
res = (res << 8) | *begin++;
return { begin, res };
}
template <class OutputIt>
inline inline
OutputIt bool
encode_length (std::size_t len, OutputIt out) isUniversal(Preamble const& p)
{ {
if (len <= 0x7F) return (p.type & 0xC0) == 0;
{
*out++ = static_cast<std::uint8_t>(len & 0x7F);
return out;
}
// Decide how many bytes we need:
if (len <= 0xFFFF)
{
*out++ = 0x82;
*out++ = static_cast<std::uint8_t>((len >> 8) & 0xFF);
*out++ = static_cast<std::uint8_t>(len & 0xFF);
return out;
}
if (len <= 0xFFFFFF)
{
*out++ = 0x83;
*out++ = static_cast<std::uint8_t>((len >> 16) & 0xFF);
*out++ = static_cast<std::uint8_t>((len >> 8) & 0xFF);
*out++ = static_cast<std::uint8_t>(len & 0xFF);
return out;
}
if (len <= 0xFFFFFFFF)
{
*out++ = 0x84;
*out++ = static_cast<std::uint8_t>((len >> 24) & 0xFF);
*out++ = static_cast<std::uint8_t>((len >> 16) & 0xFF);
*out++ = static_cast<std::uint8_t>((len >> 8) & 0xFF);
*out++ = static_cast<std::uint8_t>(len & 0xFF);
return out;
}
// Note: OER can represent lengths up to (2^1016) - 1,
// which is, truly, enough for everyone. We never
// exceed 2^32.
Throw<std::length_error>("overlong encoding length: " + std::to_string(len));
} }
// A "streambuf" would serve us better here - instead of the
// crazy paired return, we consume data from it and things
// just magically work.
template <class InputIt>
std::pair<InputIt, std::size_t>
decode_length (InputIt begin, InputIt end)
{
if (begin == end)
Throw<std::length_error>("empty buffer");
std::size_t bytes = *begin++;
if (bytes < 128)
return { begin, bytes };
bytes &= 0x7F;
if (bytes > 4)
Throw<std::length_error>("overlong encoded length: " + std::to_string(bytes));
std::size_t len = 0;
if (std::distance (begin, end) < bytes)
Throw<std::length_error>("short encoded length: " + std::to_string(bytes));
while (bytes--)
len = (len << 8) | *begin++;
return { begin, len };
}
/** Encode a fixed-size octet string: OER 2.6 (2) */
template <class InputIt, class OutputIt>
OutputIt
encode_octetstring(InputIt begin, InputIt end, OutputIt out)
{
while (begin != end)
*out++ = *begin++;
return out;
}
/** Encode a dynamic size octet string: OER 2.6 (1) */
inline inline
std::size_t bool
predict_octetstring_size(std::size_t size) isApplication(Preamble const& p)
{ {
// Alternatively, always guess 4 + size and call it a day? return (p.type & 0xC0) == 0x40;
if (size <= 0x7F)
return size + 1;
// Decide how many bytes we need:
if (size <= 0xFFFF)
return size + 3;
if (size <= 0xFFFFFF)
return size + 4;
if (size <= 0xFFFFFFFF)
return size + 5;
Throw<std::length_error>("overlong encoding length: " + std::to_string(size));
} }
/** Encode an dynamic size octet string: OER 2.6 (1) */ inline
template <class InputIt, class OutputIt> bool
OutputIt isContextSpecific(Preamble const& p)
encode_octetstring(std::size_t size, InputIt begin, InputIt end, OutputIt out)
{ {
// This will encode the length first, followed by the return (p.type & 0xC0) == 0x80;
// payload octets:
return encode_octetstring (
begin,
end,
oer::encode_length (size, out));
} }
template <class Integer, class OutputIt> inline
std::enable_if_t<std::is_unsigned<Integer>::value> bool
encode_varuint (Integer value, OutputIt out) isPrivate(Preamble const& p)
{ {
auto count = [](Integer n) return (p.type & 0xC0) == 0xC0;
}
inline
Preamble
parsePreamble(Slice& s, std::error_code& ec)
{
Preamble p;
if (s.size() < 2)
{ {
std::size_t c = 0; ec = error::short_preamble;
return p;
}
do p.type = s[0] & 0xE0;
p.tag = s[0] & 0x1F;
s += 1;
if (p.tag == 0x1F)
{ // Long tag form, which we do not support:
ec = error::long_tag;
return p;
}
p.length = s[0];
s += 1;
if (p.length & 0x80)
{ // Long form length:
std::size_t const cnt = p.length & 0x7F;
if (cnt == 0)
{ {
n >>= 8; ec = error::malformed_encoding;
++c; return p;
} while (n); }
return c; if (cnt > sizeof(std::size_t))
}; {
ec = error::large_size;
return p;
}
std::size_t c = count (value); if (cnt > s.size())
{
ec = error::short_preamble;
return p;
}
out = encode_length (c, out); p.length = 0;
while(c--) for (std::size_t i = 0; i != cnt; ++i)
{ p.length = (p.length << 8) + s[i];
*out++ = static_cast<std::uint8_t>(
(value >> (c * 8)) & 0xFF); s += cnt;
if (p.length == 0)
{
ec = error::malformed_encoding;
return p;
}
} }
return p;
} }
template <class Integer, class InputIt> inline
std::enable_if_t<std::is_unsigned<Integer>::value, std::pair<InputIt, Integer>> Buffer
decode_varuint (InputIt begin, InputIt end) parseOctetString(Slice& s, std::uint32_t count, std::error_code& ec)
{ {
auto y = decode_length (begin, end); if (count > s.size())
{
ec = error::buffer_underfull;
return {};
}
if (y.second > sizeof(Integer)) if (count > 65535)
Throw<std::length_error>("Encoded integer exceeds allowable range: " + std::to_string(y.second)); {
ec = error::large_size;
return {};
}
Integer x = 0; Buffer b(s.data(), count);
s += count;
for (std::size_t i = 0; i != y.second; ++i) return b;
x = (x << 8) + *y.first++;
return { y.first, x };
} }
template <class Integer>
Integer
parseInteger(Slice& s, std::size_t count, std::error_code& ec)
{
Integer v{0};
if (s.empty())
{
// can never have zero sized integers
ec = error::malformed_encoding;
return v;
}
if (count > s.size())
{
ec = error::buffer_underfull;
return v;
}
const bool isSigned = std::numeric_limits<Integer>::is_signed;
// unsigned types may have a leading zero octet
const size_t maxLength = isSigned ? sizeof(Integer) : sizeof(Integer) + 1;
if (count > maxLength)
{
ec = error::large_size;
return v;
}
if (!isSigned && (s[0] & (1 << 7)))
{
// trying to decode a negative number into a positive value
ec = error::malformed_encoding;
return v;
}
if (!isSigned && count == sizeof(Integer) + 1 && s[0])
{
// since integers are coded as two's complement, the first byte may
// be zero for unsigned reps
ec = error::malformed_encoding;
return v;
}
v = 0;
for (size_t i = 0; i < count; ++i)
v = (v << 8) | (s[i] & 0xff);
if (isSigned && (s[0] & (1 << 7)))
{
for (int i = count; i < sizeof(Integer); ++i)
v |= (Integer(0xff) << (8 * i));
}
s += count;
return v;
} }
}
} } // der
} // cryptoconditions
} // ripple
#endif #endif

View File

@@ -623,7 +623,7 @@ ApplyStateTable::threadOwners (ReadView const& base,
// Nothing to do // Nothing to do
break; break;
} }
case ltSUSPAY: case ltESCROW:
{ {
threadTx (base, meta, (*sle)[sfAccount], mods, j); threadTx (base, meta, (*sle)[sfAccount], mods, j);
threadTx (base, meta, (*sle)[sfDestination], mods, j); threadTx (base, meta, (*sle)[sfDestination], mods, j);

View File

@@ -36,7 +36,6 @@ feature (const char* name);
extern uint256 const featureMultiSign; extern uint256 const featureMultiSign;
extern uint256 const featureTickets; extern uint256 const featureTickets;
extern uint256 const featureSusPay;
extern uint256 const featureTrustSetAuth; extern uint256 const featureTrustSetAuth;
extern uint256 const featureFeeEscalation; extern uint256 const featureFeeEscalation;
extern uint256 const featureOwnerPaysFee; extern uint256 const featureOwnerPaysFee;
@@ -47,6 +46,8 @@ extern uint256 const featureFlow;
extern uint256 const featureCryptoConditions; extern uint256 const featureCryptoConditions;
extern uint256 const featureTickSize; extern uint256 const featureTickSize;
extern uint256 const featureRIPD1368; extern uint256 const featureRIPD1368;
extern uint256 const featureEscrow;
extern uint256 const featureCryptoConditionsSuite;
} // ripple } // ripple

View File

@@ -234,9 +234,9 @@ Keylet page (uint256 const& key)
return { ltDIR_NODE, key }; return { ltDIR_NODE, key };
} }
/** A SuspendedPayment */ /** An escrow entry */
Keylet Keylet
susPay (AccountID const& source, std::uint32_t seq); escrow (AccountID const& source, std::uint32_t seq);
/** A PaymentChannel */ /** A PaymentChannel */
Keylet Keylet

View File

@@ -78,7 +78,7 @@ enum LedgerEntryType
ltFEE_SETTINGS = 's', ltFEE_SETTINGS = 's',
ltSUSPAY = 'u', ltESCROW = 'u',
// Simple unidirection xrp channel // Simple unidirection xrp channel
ltPAYCHAN = 'x', ltPAYCHAN = 'x',
@@ -105,7 +105,7 @@ enum LedgerNameSpace
spaceBookDir = 'B', // Directory of order books. spaceBookDir = 'B', // Directory of order books.
spaceContract = 'c', spaceContract = 'c',
spaceSkipList = 's', spaceSkipList = 's',
spaceSusPay = 'u', spaceEscrow = 'u',
spaceAmendment = 'f', spaceAmendment = 'f',
spaceFee = 'e', spaceFee = 'e',
spaceTicket = 'T', spaceTicket = 'T',

View File

@@ -35,10 +35,10 @@ enum TxType
ttINVALID = -1, ttINVALID = -1,
ttPAYMENT = 0, ttPAYMENT = 0,
ttSUSPAY_CREATE = 1, ttESCROW_CREATE = 1,
ttSUSPAY_FINISH = 2, ttESCROW_FINISH = 2,
ttACCOUNT_SET = 3, ttACCOUNT_SET = 3,
ttSUSPAY_CANCEL = 4, ttESCROW_CANCEL = 4,
ttREGULAR_KEY_SET = 5, ttREGULAR_KEY_SET = 5,
ttNICKNAME_SET = 6, // open ttNICKNAME_SET = 6, // open
ttOFFER_CREATE = 7, ttOFFER_CREATE = 7,

View File

@@ -47,7 +47,6 @@ feature (const char* name)
uint256 const featureMultiSign = feature("MultiSign"); uint256 const featureMultiSign = feature("MultiSign");
uint256 const featureTickets = feature("Tickets"); uint256 const featureTickets = feature("Tickets");
uint256 const featureSusPay = feature("SusPay");
uint256 const featureTrustSetAuth = feature("TrustSetAuth"); uint256 const featureTrustSetAuth = feature("TrustSetAuth");
uint256 const featureFeeEscalation = feature("FeeEscalation"); uint256 const featureFeeEscalation = feature("FeeEscalation");
uint256 const featureOwnerPaysFee = feature("OwnerPaysFee"); uint256 const featureOwnerPaysFee = feature("OwnerPaysFee");
@@ -58,5 +57,7 @@ uint256 const featureFlow = feature("Flow");
uint256 const featureCryptoConditions = feature("CryptoConditions"); uint256 const featureCryptoConditions = feature("CryptoConditions");
uint256 const featureTickSize = feature("TickSize"); uint256 const featureTickSize = feature("TickSize");
uint256 const featureRIPD1368 = feature("RIPD1368"); uint256 const featureRIPD1368 = feature("RIPD1368");
uint256 const featureEscrow = feature("Escrow");
uint256 const featureCryptoConditionsSuite = feature("CryptoConditionsSuite");
} // ripple } // ripple

View File

@@ -313,14 +313,14 @@ Keylet page(Keylet const& root,
} }
Keylet Keylet
susPay (AccountID const& source, std::uint32_t seq) escrow (AccountID const& source, std::uint32_t seq)
{ {
sha512_half_hasher h; sha512_half_hasher h;
using beast::hash_append; using beast::hash_append;
hash_append(h, spaceSusPay); hash_append(h, spaceEscrow);
hash_append(h, source); hash_append(h, source);
hash_append(h, seq); hash_append(h, seq);
return { ltSUSPAY, static_cast<uint256>(h) }; return { ltESCROW, static_cast<uint256>(h) };
} }
Keylet Keylet

View File

@@ -82,7 +82,7 @@ LedgerFormats::LedgerFormats ()
<< SOElement (sfHighQualityOut, SOE_OPTIONAL) << SOElement (sfHighQualityOut, SOE_OPTIONAL)
; ;
add ("SuspendedPayment", ltSUSPAY) << add ("Escrow", ltESCROW) <<
SOElement (sfAccount, SOE_REQUIRED) << SOElement (sfAccount, SOE_REQUIRED) <<
SOElement (sfDestination, SOE_REQUIRED) << SOElement (sfDestination, SOE_REQUIRED) <<
SOElement (sfAmount, SOE_REQUIRED) << SOElement (sfAmount, SOE_REQUIRED) <<

View File

@@ -67,7 +67,7 @@ TxFormats::TxFormats ()
<< SOElement (sfDeliverMin, SOE_OPTIONAL) << SOElement (sfDeliverMin, SOE_OPTIONAL)
; ;
add ("SuspendedPaymentCreate", ttSUSPAY_CREATE) << add ("EscrowCreate", ttESCROW_CREATE) <<
SOElement (sfDestination, SOE_REQUIRED) << SOElement (sfDestination, SOE_REQUIRED) <<
SOElement (sfAmount, SOE_REQUIRED) << SOElement (sfAmount, SOE_REQUIRED) <<
SOElement (sfCondition, SOE_OPTIONAL) << SOElement (sfCondition, SOE_OPTIONAL) <<
@@ -75,13 +75,13 @@ TxFormats::TxFormats ()
SOElement (sfFinishAfter, SOE_OPTIONAL) << SOElement (sfFinishAfter, SOE_OPTIONAL) <<
SOElement (sfDestinationTag, SOE_OPTIONAL); SOElement (sfDestinationTag, SOE_OPTIONAL);
add ("SuspendedPaymentFinish", ttSUSPAY_FINISH) << add ("EscrowFinish", ttESCROW_FINISH) <<
SOElement (sfOwner, SOE_REQUIRED) << SOElement (sfOwner, SOE_REQUIRED) <<
SOElement (sfOfferSequence, SOE_REQUIRED) << SOElement (sfOfferSequence, SOE_REQUIRED) <<
SOElement (sfFulfillment, SOE_OPTIONAL) << SOElement (sfFulfillment, SOE_OPTIONAL) <<
SOElement (sfCondition, SOE_OPTIONAL); SOElement (sfCondition, SOE_OPTIONAL);
add ("SuspendedPaymentCancel", ttSUSPAY_CANCEL) << add ("EscrowCancel", ttESCROW_CANCEL) <<
SOElement (sfOwner, SOE_REQUIRED) << SOElement (sfOwner, SOE_REQUIRED) <<
SOElement (sfOfferSequence, SOE_REQUIRED); SOElement (sfOfferSequence, SOE_REQUIRED);

View File

@@ -27,6 +27,7 @@
#include <ripple/app/tx/impl/Change.cpp> #include <ripple/app/tx/impl/Change.cpp>
#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/Escrow.cpp>
#include <ripple/app/tx/impl/OfferStream.cpp> #include <ripple/app/tx/impl/OfferStream.cpp>
#include <ripple/app/tx/impl/Payment.cpp> #include <ripple/app/tx/impl/Payment.cpp>
#include <ripple/app/tx/impl/PayChan.cpp> #include <ripple/app/tx/impl/PayChan.cpp>
@@ -35,7 +36,6 @@
#include <ripple/app/tx/impl/SetSignerList.cpp> #include <ripple/app/tx/impl/SetSignerList.cpp>
#include <ripple/app/tx/impl/SetTrust.cpp> #include <ripple/app/tx/impl/SetTrust.cpp>
#include <ripple/app/tx/impl/SignerEntries.cpp> #include <ripple/app/tx/impl/SignerEntries.cpp>
#include <ripple/app/tx/impl/SusPay.cpp>
#include <ripple/app/tx/impl/Taker.cpp> #include <ripple/app/tx/impl/Taker.cpp>
#include <ripple/app/tx/impl/ApplyContext.cpp> #include <ripple/app/tx/impl/ApplyContext.cpp>
#include <ripple/app/tx/impl/Transactor.cpp> #include <ripple/app/tx/impl/Transactor.cpp>

View File

@@ -21,5 +21,4 @@
#include <ripple/conditions/impl/Condition.cpp> #include <ripple/conditions/impl/Condition.cpp>
#include <ripple/conditions/impl/Fulfillment.cpp> #include <ripple/conditions/impl/Fulfillment.cpp>
#include <ripple/conditions/impl/Ed25519.cpp> #include <ripple/conditions/impl/error.cpp>
#include <ripple/conditions/impl/RsaSha256.cpp>

View File

@@ -1,4 +1,4 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/* /*
This file is part of rippled: https://github.com/ripple/rippled This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc. Copyright (c) 2012, 2013 Ripple Labs Inc.
@@ -28,67 +28,48 @@
namespace ripple { namespace ripple {
namespace test { namespace test {
struct SusPay_test : public beast::unit_test::suite struct Escrow_test : public beast::unit_test::suite
{ {
// An Ed25519 conditional trigger fulfillment and its // A PreimageSha256 fulfillments and its associated condition.
// condition std::array<std::uint8_t, 4> const fb1 =
std::array<std::uint8_t, 99> const fb1 =
{{ {{
0x00, 0x04, 0x60, 0x3B, 0x6A, 0x27, 0xBC, 0xCE, 0xB6, 0xA4, 0x2D, 0x62, 0xA0, 0x02, 0x80, 0x00
0xA3, 0xA8, 0xD0, 0x2A, 0x6F, 0x0D, 0x73, 0x65, 0x32, 0x15, 0x77, 0x1D,
0xE2, 0x43, 0xA6, 0x3A, 0xC0, 0x48, 0xA1, 0x8B, 0x59, 0xDA, 0x29, 0x8F,
0x89, 0x5B, 0x3C, 0xAF, 0xE2, 0xC9, 0x50, 0x60, 0x39, 0xD0, 0xE2, 0xA6,
0x63, 0x82, 0x56, 0x80, 0x04, 0x67, 0x4F, 0xE8, 0xD2, 0x37, 0x78, 0x50,
0x92, 0xE4, 0x0D, 0x6A, 0xAF, 0x48, 0x3E, 0x4F, 0xC6, 0x01, 0x68, 0x70,
0x5F, 0x31, 0xF1, 0x01, 0x59, 0x61, 0x38, 0xCE, 0x21, 0xAA, 0x35, 0x7C,
0x0D, 0x32, 0xA0, 0x64, 0xF4, 0x23, 0xDC, 0x3E, 0xE4, 0xAA, 0x3A, 0xBF,
0x53, 0xF8, 0x03,
}}; }};
std::array<std::uint8_t, 39> const cb1 = std::array<std::uint8_t, 39> const cb1 =
{{ {{
0x00, 0x04, 0x01, 0x20, 0x20, 0x3B, 0x6A, 0x27, 0xBC, 0xCE, 0xB6, 0xA4, 0xA0, 0x25, 0x80, 0x20, 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14,
0x2D, 0x62, 0xA3, 0xA8, 0xD0, 0x2A, 0x6F, 0x0D, 0x73, 0x65, 0x32, 0x15, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, 0x27, 0xAE, 0x41, 0xE4,
0x77, 0x1D, 0xE2, 0x43, 0xA6, 0x3A, 0xC0, 0x48, 0xA1, 0x8B, 0x59, 0xDA, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55,
0x29, 0x01, 0x60 0x81, 0x01, 0x00
}}; }};
// A prefix.prefix.ed25519 conditional trigger fulfillment: // Another PreimageSha256 fulfillments and its associated condition.
std::array<std::uint8_t, 106> const fb2 = std::array<std::uint8_t, 7> const fb2 =
{{ {{
0x00, 0x01, 0x67, 0x03, 0x61, 0x62, 0x63, 0x00, 0x04, 0x60, 0x76, 0xA1, 0xA0, 0x05, 0x80, 0x03, 0x61, 0x61, 0x61
0x59, 0x20, 0x44, 0xA6, 0xE4, 0xF5, 0x11, 0x26, 0x5B, 0xCA, 0x73, 0xA6,
0x04, 0xD9, 0x0B, 0x05, 0x29, 0xD1, 0xDF, 0x60, 0x2B, 0xE3, 0x0A, 0x19,
0xA9, 0x25, 0x76, 0x60, 0xD1, 0xF5, 0xAE, 0xC6, 0xAB, 0x6A, 0x91, 0x22,
0xAF, 0xF0, 0xF7, 0xDC, 0xB9, 0x66, 0x7F, 0xF6, 0x13, 0x13, 0x68, 0x94,
0x73, 0x2B, 0x6E, 0x78, 0xC2, 0x6F, 0x5B, 0x67, 0x31, 0x01, 0xE2, 0x67,
0xFE, 0x2E, 0x2B, 0x65, 0xFA, 0x4D, 0x53, 0xDA, 0xD4, 0x78, 0xA1, 0xAD,
0xA6, 0x4D, 0x50, 0xFD, 0x1D, 0xFD, 0xB7, 0xD9, 0x49, 0x20, 0xDC, 0x3E,
0x1A, 0x56, 0x4A, 0x64, 0x7B, 0x1C, 0xBA, 0x35, 0x60, 0x01,
}}; }};
std::array<std::uint8_t, 39> const cb2 = std::array<std::uint8_t, 39> const cb2 =
{{ {{
0xA0, 0x25, 0x80, 0x20, 0x98, 0x34, 0x87, 0x6D, 0xCF, 0xB0, 0x5C, 0xB1,
0x00, 0x01, 0x01, 0x25, 0x20, 0x28, 0x7A, 0x8B, 0xD8, 0xAD, 0xAE, 0x8A, 0x67, 0xA5, 0xC2, 0x49, 0x53, 0xEB, 0xA5, 0x8C, 0x4A, 0xC8, 0x9B, 0x1A,
0xCA, 0x0C, 0x87, 0x1C, 0xE7, 0xC2, 0x5F, 0xBA, 0xA5, 0xA8, 0xBE, 0x10, 0xDF, 0x57, 0xF2, 0x8F, 0x2F, 0x9D, 0x09, 0xAF, 0x10, 0x7E, 0xE8, 0xF0,
0xD0, 0xE4, 0xDB, 0x1F, 0x56, 0xAE, 0xEE, 0x8B, 0xB3, 0xAD, 0xCE, 0xE5, 0x81, 0x01, 0x03
0x5B, 0x01, 0x64
}}; }};
// A prefix+preimage conditional trigger fulfillment // Another PreimageSha256 fulfillment and its associated condition.
std::array<std::uint8_t, 7> const fb3 = std::array<std::uint8_t, 8> const fb3 =
{{ {{
0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x06, 0x80, 0x04, 0x6E, 0x69, 0x6B, 0x62
}}; }};
std::array<std::uint8_t, 39> const cb3 = std::array<std::uint8_t, 39> const cb3 =
{{ {{
0xA0, 0x25, 0x80, 0x20, 0x6E, 0x4C, 0x71, 0x45, 0x30, 0xC0, 0xA4, 0x26,
0x00, 0x01, 0x01, 0x07, 0x20, 0x62, 0x36, 0xB7, 0xA8, 0x58, 0xFB, 0x35, 0x8B, 0x3F, 0xA6, 0x3B, 0x1B, 0x60, 0x6F, 0x2D, 0x26, 0x4A, 0x2D, 0x85,
0x2F, 0xD5, 0xC3, 0x01, 0x3B, 0x68, 0x98, 0xCF, 0x26, 0x8B, 0x3E, 0xB8, 0x7B, 0xE8, 0xA0, 0x9C, 0x1D, 0xFD, 0x57, 0x0D, 0x15, 0x85, 0x8B, 0xD4,
0x50, 0xB3, 0x4A, 0xD2, 0x65, 0x24, 0xB0, 0xF8, 0x56, 0xC3, 0x72, 0xD9, 0x81, 0x01, 0x04
0x73, 0x01, 0x01
}}; }};
static static
@@ -99,7 +80,7 @@ struct SusPay_test : public beast::unit_test::suite
{ {
using namespace jtx; using namespace jtx;
Json::Value jv; Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentCreate"; jv[jss::TransactionType] = "EscrowCreate";
jv[jss::Flags] = tfUniversal; jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human(); jv[jss::Account] = account.human();
jv[jss::Destination] = to.human(); jv[jss::Destination] = to.human();
@@ -129,7 +110,7 @@ struct SusPay_test : public beast::unit_test::suite
{ {
using namespace jtx; using namespace jtx;
Json::Value jv; Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentCreate"; jv[jss::TransactionType] = "EscrowCreate";
jv[jss::Flags] = tfUniversal; jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human(); jv[jss::Account] = account.human();
jv[jss::Destination] = to.human(); jv[jss::Destination] = to.human();
@@ -147,7 +128,7 @@ struct SusPay_test : public beast::unit_test::suite
{ {
using namespace jtx; using namespace jtx;
Json::Value jv; Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentCreate"; jv[jss::TransactionType] = "EscrowCreate";
jv[jss::Flags] = tfUniversal; jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human(); jv[jss::Account] = account.human();
jv[jss::Destination] = to.human(); jv[jss::Destination] = to.human();
@@ -164,7 +145,7 @@ struct SusPay_test : public beast::unit_test::suite
jtx::Account const& from, std::uint32_t seq) jtx::Account const& from, std::uint32_t seq)
{ {
Json::Value jv; Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentFinish"; jv[jss::TransactionType] = "EscrowFinish";
jv[jss::Flags] = tfUniversal; jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human(); jv[jss::Account] = account.human();
jv["Owner"] = from.human(); jv["Owner"] = from.human();
@@ -179,7 +160,7 @@ struct SusPay_test : public beast::unit_test::suite
Slice condition, Slice fulfillment) Slice condition, Slice fulfillment)
{ {
Json::Value jv; Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentFinish"; jv[jss::TransactionType] = "EscrowFinish";
jv[jss::Flags] = tfUniversal; jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human(); jv[jss::Account] = account.human();
jv["Owner"] = from.human(); jv["Owner"] = from.human();
@@ -195,7 +176,7 @@ struct SusPay_test : public beast::unit_test::suite
jtx::Account const& from, std::uint32_t seq) jtx::Account const& from, std::uint32_t seq)
{ {
Json::Value jv; Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentCancel"; jv[jss::TransactionType] = "EscrowCancel";
jv[jss::Flags] = tfUniversal; jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human(); jv[jss::Account] = account.human();
jv["Owner"] = from.human(); jv["Owner"] = from.human();
@@ -211,13 +192,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx; using namespace jtx;
using namespace std::chrono; using namespace std::chrono;
{ // SusPay enabled { // Escrow not enabled
Env env(*this, features(featureSusPay));
env.fund(XRP(5000), "alice", "bob");
env(lockup("alice", "bob", XRP(1000), env.now() + 1s));
}
{ // SusPay not enabled
Env env(*this); Env env(*this);
env.fund(XRP(5000), "alice", "bob"); env.fund(XRP(5000), "alice", "bob");
env(lockup("alice", "bob", XRP(1000), env.now() + 1s), ter(temDISABLED)); env(lockup("alice", "bob", XRP(1000), env.now() + 1s), ter(temDISABLED));
@@ -225,50 +200,11 @@ struct SusPay_test : public beast::unit_test::suite
env(cancel("bob", "alice", 1), ter(temDISABLED)); env(cancel("bob", "alice", 1), ter(temDISABLED));
} }
{ // SusPay enabled, CryptoConditions disabled { // Escrow enabled
Env env(*this, Env env(*this, features(featureEscrow));
features(featureSusPay));
env.fund(XRP(5000), "alice", "bob"); env.fund(XRP(5000), "alice", "bob");
env(lockup("alice", "bob", XRP(1000), env.now() + 1s));
auto const seq = env.seq("alice");
// Fail: no cryptoconditions allowed
env(condpay("alice", "bob", XRP(1000),
makeSlice (cb1), env.now() + 1s), ter(temDISABLED));
// Succeed: doesn't have a cryptocondition
env(lockup("alice", "bob", XRP(1000),
env.now() + 1s));
// Fail: can't specify conditional finishes if
// cryptoconditions aren't allowed.
{
auto f = finish("bob", "alice", seq,
makeSlice(cb1), makeSlice(fb1));
env (f, ter(temDISABLED));
auto fnc = f;
fnc.removeMember ("Condition");
env (fnc, ter(temDISABLED));
auto fnf = f;
fnf.removeMember ("Fulfillment");
env (fnf, ter(temDISABLED));
}
// Succeeds
env.close(); env.close();
env(finish("bob", "alice", seq));
}
{ // SusPay enabled, CryptoConditions enabled
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
env.fund(XRP(5000), "alice", "bob");
auto const seq = env.seq("alice"); auto const seq = env.seq("alice");
@@ -287,23 +223,20 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx; using namespace jtx;
using namespace std::chrono; using namespace std::chrono;
{ Env env(*this, features(featureEscrow));
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
auto const alice = Account("alice"); auto const alice = Account("alice");
env.fund(XRP(5000), alice, "bob"); env.fund(XRP(5000), alice, "bob");
auto const seq = env.seq(alice); auto const seq = env.seq(alice);
// set source and dest tags // set source and dest tags
env(condpay(alice, "bob", XRP(1000), env(condpay(alice, "bob", XRP(1000),
makeSlice (cb1), env.now() + 1s), makeSlice (cb1), env.now() + 1s),
stag(1), dtag(2)); stag(1), dtag(2));
auto const sle = env.le(keylet::susPay(alice.id(), seq)); auto const sle = env.le(keylet::escrow(alice.id(), seq));
BEAST_EXPECT((*sle)[sfSourceTag] == 1); BEAST_EXPECT(sle);
BEAST_EXPECT((*sle)[sfDestinationTag] == 2); BEAST_EXPECT((*sle)[sfSourceTag] == 1);
} BEAST_EXPECT((*sle)[sfDestinationTag] == 2);
} }
void void
@@ -314,9 +247,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx; using namespace jtx;
using namespace std::chrono; using namespace std::chrono;
Env env(*this, Env env(*this, features(featureEscrow));
features(featureSusPay),
features(featureCryptoConditions));
env.fund(XRP(5000), "alice", "bob"); env.fund(XRP(5000), "alice", "bob");
env.close(); env.close();
@@ -419,7 +350,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace std::chrono; using namespace std::chrono;
{ // Unconditional { // Unconditional
Env env(*this, features(featureSusPay)); Env env(*this, features(featureEscrow));
env.fund(XRP(5000), "alice", "bob"); env.fund(XRP(5000), "alice", "bob");
auto const seq = env.seq("alice"); auto const seq = env.seq("alice");
env(lockup("alice", "alice", XRP(1000), env.now() + 1s)); env(lockup("alice", "alice", XRP(1000), env.now() + 1s));
@@ -434,9 +365,7 @@ struct SusPay_test : public beast::unit_test::suite
} }
{ // Conditional { // Conditional
Env env(*this, Env env(*this, features(featureEscrow));
features(featureSusPay),
features(featureCryptoConditions));
env.fund(XRP(5000), "alice", "bob"); env.fund(XRP(5000), "alice", "bob");
auto const seq = env.seq("alice"); auto const seq = env.seq("alice");
env(lockup("alice", "alice", XRP(1000), makeSlice(cb2), env.now() + 1s)); env(lockup("alice", "alice", XRP(1000), makeSlice(cb2), env.now() + 1s));
@@ -459,9 +388,9 @@ struct SusPay_test : public beast::unit_test::suite
} }
void void
testCondPay() testEscrowConditions()
{ {
testcase ("Conditional Payments"); testcase ("Escrow Conditions");
using namespace jtx; using namespace jtx;
using namespace std::chrono; using namespace std::chrono;
@@ -469,8 +398,7 @@ struct SusPay_test : public beast::unit_test::suite
{ // Test cryptoconditions { // Test cryptoconditions
Env env(*this, Env env(*this,
features(featureSusPay), features(featureEscrow));
features(featureCryptoConditions));
auto T = [&env](NetClock::duration const& d) auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; }; { return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol"); env.fund(XRP(5000), "alice", "bob", "carol");
@@ -507,7 +435,7 @@ struct SusPay_test : public beast::unit_test::suite
// Attempt to finish with the correct condition & fulfillment // Attempt to finish with the correct condition & fulfillment
env(finish("bob", "alice", seq, makeSlice(cb1), makeSlice(fb1)), fee(1500)); env(finish("bob", "alice", seq, makeSlice(cb1), makeSlice(fb1)), fee(1500));
// SLE removed on finish // SLE removed on finish
BEAST_EXPECT(! env.le(keylet::susPay(Account("alice").id(), seq))); BEAST_EXPECT(! env.le(keylet::escrow(Account("alice").id(), seq)));
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0); BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
env.require(balance("carol", XRP(6000))); env.require(balance("carol", XRP(6000)));
env(cancel("bob", "alice", seq), ter(tecNO_TARGET)); env(cancel("bob", "alice", seq), ter(tecNO_TARGET));
@@ -518,8 +446,7 @@ struct SusPay_test : public beast::unit_test::suite
{ // Test cancel when condition is present { // Test cancel when condition is present
Env env(*this, Env env(*this,
features(featureSusPay), features(featureEscrow));
features(featureCryptoConditions));
auto T = [&env](NetClock::duration const& d) auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; }; { return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol"); env.fund(XRP(5000), "alice", "bob", "carol");
@@ -532,13 +459,11 @@ struct SusPay_test : public beast::unit_test::suite
env(cancel("bob", "alice", seq)); env(cancel("bob", "alice", seq));
env.require(balance("alice", XRP(5000) - drops(10))); env.require(balance("alice", XRP(5000) - drops(10)));
// SLE removed on cancel // SLE removed on cancel
BEAST_EXPECT(! env.le(keylet::susPay(Account("alice").id(), seq))); BEAST_EXPECT(! env.le(keylet::escrow(Account("alice").id(), seq)));
} }
{ {
Env env(*this, Env env(*this, features(featureEscrow));
features(featureSusPay),
features(featureCryptoConditions));
auto T = [&env](NetClock::duration const& d) auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; }; { return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol"); env.fund(XRP(5000), "alice", "bob", "carol");
@@ -558,9 +483,7 @@ struct SusPay_test : public beast::unit_test::suite
} }
{ // Test long & short conditions during creation { // Test long & short conditions during creation
Env env(*this, Env env(*this, features(featureEscrow));
features(featureSusPay),
features(featureCryptoConditions));
auto T = [&env](NetClock::duration const& d) auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; }; { return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol"); env.fund(XRP(5000), "alice", "bob", "carol");
@@ -601,8 +524,7 @@ struct SusPay_test : public beast::unit_test::suite
{ // Test long and short conditions & fulfillments during finish { // Test long and short conditions & fulfillments during finish
Env env(*this, Env env(*this,
features(featureSusPay), features(featureEscrow));
features(featureCryptoConditions));
auto T = [&env](NetClock::duration const& d) auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; }; { return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol"); env.fund(XRP(5000), "alice", "bob", "carol");
@@ -687,9 +609,7 @@ struct SusPay_test : public beast::unit_test::suite
{ // Test empty condition during creation and { // Test empty condition during creation and
// empty condition & fulfillment during finish // empty condition & fulfillment during finish
Env env(*this, Env env(*this, features(featureEscrow));
features(featureSusPay),
features(featureCryptoConditions));
auto T = [&env](NetClock::duration const& d) auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; }; { return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol"); env.fund(XRP(5000), "alice", "bob", "carol");
@@ -726,6 +646,28 @@ struct SusPay_test : public beast::unit_test::suite
env.require(balance ("carol", XRP(6000))); env.require(balance ("carol", XRP(6000)));
env.require(balance ("alice", XRP(4000) - drops(10))); env.require(balance ("alice", XRP(4000) - drops(10)));
} }
{ // Test a condition other than PreimageSha256, which
// would require a separate amendment
Env env(*this, features(featureEscrow));
auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol");
std::array<std::uint8_t, 45> cb =
{{
0xA2, 0x2B, 0x80, 0x20, 0x42, 0x4A, 0x70, 0x49, 0x49, 0x52,
0x92, 0x67, 0xB6, 0x21, 0xB3, 0xD7, 0x91, 0x19, 0xD7, 0x29,
0xB2, 0x38, 0x2C, 0xED, 0x8B, 0x29, 0x6C, 0x3C, 0x02, 0x8F,
0xA9, 0x7D, 0x35, 0x0F, 0x6D, 0x07, 0x81, 0x03, 0x06, 0x34,
0xD2, 0x82, 0x02, 0x03, 0xC8
}};
// FIXME: this transaction should, eventually, return temDISABLED
// instead of temMALFORMED.
env(condpay("alice", "carol", XRP(1000), makeSlice(cb), T(S{1})),
ter(temMALFORMED));
}
} }
void void
@@ -735,9 +677,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx; using namespace jtx;
using namespace std::chrono; using namespace std::chrono;
Env env(*this, Env env(*this, features(featureEscrow));
features(featureSusPay),
features(featureCryptoConditions));
env.fund(XRP(5000), "alice", "bob", "carol"); env.fund(XRP(5000), "alice", "bob", "carol");
env(condpay("alice", "carol", XRP(1000), makeSlice(cb1), env.now() + 1s)); env(condpay("alice", "carol", XRP(1000), makeSlice(cb1), env.now() + 1s));
@@ -751,9 +691,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx; using namespace jtx;
using namespace std::chrono; using namespace std::chrono;
Env env(*this, Env env(*this, features(featureEscrow));
features(featureSusPay),
features(featureCryptoConditions));
env.memoize("alice"); env.memoize("alice");
env.memoize("bob"); env.memoize("bob");
@@ -806,13 +744,13 @@ struct SusPay_test : public beast::unit_test::suite
testTags(); testTags();
testFails(); testFails();
testLockup(); testLockup();
testCondPay(); testEscrowConditions();
testMeta(); testMeta();
testConsequences(); testConsequences();
} }
}; };
BEAST_DEFINE_TESTSUITE(SusPay,app,ripple); BEAST_DEFINE_TESTSUITE(Escrow,app,ripple);
} // test } // test
} // ripple } // ripple

View File

@@ -1,199 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/basics/strHex.h>
#include <ripple/beast/unit_test.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <vector>
namespace ripple {
namespace cryptoconditions {
class Ed25519_test : public beast::unit_test::suite
{
void
check (
std::array<std::uint8_t, 32> const& secretKey,
std::vector<std::uint8_t> const& message,
std::string const& fulfillment,
std::string const& condition)
{
SecretKey const sk { makeSlice (secretKey) };
PublicKey const pk = derivePublicKey (KeyType::ed25519, sk);
auto f = loadFulfillment (fulfillment);
auto c = loadCondition (condition);
BEAST_EXPECT (f);
BEAST_EXPECT (c);
if (f && c)
{
// Ensure that loading works correctly
BEAST_EXPECT (to_string (*f) == fulfillment);
BEAST_EXPECT (to_string (*c) == condition);
// Ensures that the fulfillment generates
// the condition correctly:
BEAST_EXPECT (f->condition() == c);
// Check fulfillment
BEAST_EXPECT (validate (*f, *c, makeSlice(message)));
// Check correct creation of fulfillment
BEAST_EXPECT (*f == Ed25519 (sk, pk, makeSlice(message)));
}
}
void testKnownVectors ()
{
testcase ("Known Vectors");
std::array<std::uint8_t, 32> sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
std::vector<std::uint8_t> const payload (512, 0x21);
check (sk, payload,
"cf:4:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVGfTbzglso5Uo3i2O2WVP6abH1dz5k0H5DLylizTeL5UC0VSptUN4VCkhtbwx3B00pCeWNy1H78rq6OTXzok-EH",
"cc:4:20:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
sk.fill (0x00);
check (sk, hexblob (""),
"cf:4:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2imPiVs8r-LJUGA50OKmY4JWgARnT-jSN3hQkuQNaq9IPk_GAWhwXzHxAVlhOM4hqjV8DTKgZPQj3D7kqjq_U_gD",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96");
sk.fill (0xff);
check (sk, hexblob ("616263"),
"cf:4:dqFZIESm5PURJlvKc6YE2QsFKdHfYCvjChmpJXZg0fWuxqtqkSKv8PfcuWZ_9hMTaJRzK254wm9bZzEB4mf-Litl-k1T2tR4oa2mTVD9Hf232Ukg3D4aVkpkexy6NWAB",
"cc:4:20:dqFZIESm5PURJlvKc6YE2QsFKdHfYCvjChmpJXZg0fU:96");
}
void testFulfillment ()
{
testcase ("Fulfillment");
std::array<std::uint8_t, 32> sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
std::vector<std::uint8_t> const v1 (512, 0x21);
std::vector<std::uint8_t> const v2 (512, 0x22);
Ed25519 const f ({ sk }, makeSlice(v1));
// First check against incorrect conditions:
char const* const ccs[] =
{
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256",
"cc:1:25:XkflBmyISKuevH8-850LuMrzN-HT1Ds9zKUEzaZ2Wk0:103",
"cc:2:2b:d3O4epRCo_3rj17Bf3v8hp5ig7vq84ivPok07T9Rdl0:146",
"cc:3:11:uKkFs6dhGZCwD51c69vVvHYSp25cRi9IlvXfFaxhMjo:518",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96"
};
for (auto cc : ccs)
{
auto c = loadCondition (cc);
if (BEAST_EXPECT (c))
{
BEAST_EXPECT (! validate (f, c.get(), makeSlice(v1)));
BEAST_EXPECT (! validate (f, c.get(), makeSlice(v2)));
}
}
// Now, finally, check the correct condition:
auto c = loadCondition (
"cc:4:20:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
if (BEAST_EXPECT (c))
{
BEAST_EXPECT (validate (f, c.get(), makeSlice(v1)));
BEAST_EXPECT (! validate (f, c.get(), makeSlice(v2)));
}
// Under the existing spec, multiple messages sharing
// the same key should generate the same fulfillment:
{
Ed25519 const f1 ({ sk }, makeSlice (v1));
Ed25519 const f2 ({ sk }, makeSlice (v2));
BEAST_EXPECT (f1.condition () == f2.condition ());
}
}
void testMalformedCondition ()
{
testcase ("Malformed Condition");
// This is malformed and will not load because a
// feature suite of 0 is not supported.
auto c1 = loadCondition (
"cc:4:0:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (!c1);
// The following will load but fail in different ways
auto c2 = loadCondition ( // only sha256
"cc:4:1:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c2 && !validate(*c2));
auto c3 = loadCondition ( // only preimage
"cc:4:2:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c3 && !validate(*c3));
auto c4 = loadCondition ( // sha256+preimage
"cc:4:3:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c4 && !validate(*c4));
auto c5 = loadCondition ( // Ed25519+sha256+preimage
"cc:1:23:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c5 && !validate(*c5));
auto c6 = loadCondition ( // Ed25519+threshold
"cc:1:28:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c6 && !validate(*c6));
}
void run ()
{
testKnownVectors ();
testFulfillment ();
testMalformedCondition ();
}
};
BEAST_DEFINE_TESTSUITE (Ed25519, conditions, ripple);
}
}

View File

@@ -1,562 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/basics/strHex.h>
#include <ripple/beast/unit_test.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/PrefixSha256.h>
#include <ripple/conditions/PreimageSha256.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/conditions/impl/utils.h>
#include <numeric>
namespace ripple {
namespace cryptoconditions {
class PrefixSha256_test : public beast::unit_test::suite
{
void check (
Fulfillment const& f,
Condition const& c,
Slice test,
Slice good)
{
BEAST_EXPECT (validate (f, c, test) ==
((test == good) && (f.condition() == c)));
}
void testMalformedCondition ()
{
testcase ("Malformed Condition");
// This is malformed and will not load because a
// feature suite of 0 is not supported.
auto c1 = loadCondition (
"cc:1:0:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (!c1);
// The following will load but fail in different ways
auto c2 = loadCondition ( // only sha256
"cc:1:1:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c2 && !validate(*c2));
auto c3 = loadCondition ( // only preimage
"cc:1:4:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c3 && !validate(*c3));
auto c4 = loadCondition ( // only sha256+preimage
"cc:1:5:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c4 && !validate(*c4));
}
void testPrefix ()
{
testcase ("Prefix");
std::string const prefix1 = "prefix1";
std::string const prefix2 = "prefix2";
std::uint8_t msg[8];
std::iota (std::begin(msg), std::end(msg), std::uint8_t(39));
std::array<std::uint8_t, 32> const sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
{
PrefixSha256 f1;
f1.setPrefix(makeSlice (prefix1));
f1.setSubfulfillment(std::make_unique<PreimageSha256> (
makeSlice (prefix1)));
PrefixSha256 f2;
f2.setPrefix(makeSlice (prefix2));
f2.setSubfulfillment(std::make_unique<PreimageSha256> (
makeSlice (prefix1)));
BEAST_EXPECT (f1 != f2);
BEAST_EXPECT (f1.condition() != f2.condition());
// Validating with own condition should succeed.
BEAST_EXPECT (validate (f1, f1.condition(), {}));
BEAST_EXPECT (validate (f2, f2.condition(), {}));
for (std::size_t i = 1; i != sizeof(msg); ++i)
{
BEAST_EXPECT (validate (f1, f1.condition(),
Slice{msg, i}));
BEAST_EXPECT (validate (f2, f2.condition(),
Slice{msg, i}));
}
// The rest should fail:
BEAST_EXPECT (! validate (f1, f2.condition(), {}));
BEAST_EXPECT (! validate (f2, f1.condition(), {}));
for (std::size_t i = 1; i != sizeof(msg); ++i)
{
BEAST_EXPECT (! validate (f1, f2.condition(),
Slice{msg, i}));
BEAST_EXPECT (! validate (f2, f1.condition(),
Slice{msg, i}));
}
}
{
PrefixSha256 f1;
f1.setPrefix(makeSlice (prefix1));
f1.setSubfulfillment(std::make_unique<PreimageSha256> (
makeSlice (prefix1)));
PrefixSha256 f2;
f2.setPrefix(makeSlice (prefix2));
f2.setSubfulfillment(std::make_unique<PreimageSha256> (
makeSlice (prefix2)));
BEAST_EXPECT (f1 != f2);
BEAST_EXPECT (f1.condition() != f2.condition());
BEAST_EXPECT (validate (f1, f1.condition(), {}));
BEAST_EXPECT (validate (f2, f2.condition(), {}));
BEAST_EXPECT (! validate (f1, f2.condition(), {}));
BEAST_EXPECT (! validate (f2, f1.condition(), {}));
// For preimage conditions, the message shouldn't
// matter, so verify that it does not:
for (std::size_t i = 1; i != sizeof(msg); ++i)
{
BEAST_EXPECT (validate (f1, f1.condition(),
Slice(msg, i)));
BEAST_EXPECT (validate (f2, f2.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f1, f2.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f2, f1.condition(),
Slice(msg, i)));
}
}
{
PrefixSha256 f1;
f1.setPrefix(makeSlice (prefix1));
f1.setSubfulfillment(std::make_unique<Ed25519> (
SecretKey{ sk }, makeSlice (prefix1)));
PrefixSha256 f2;
f2.setPrefix(makeSlice (prefix2));
f2.setSubfulfillment(std::make_unique<Ed25519> (
SecretKey{ sk }, makeSlice (prefix2)));
BEAST_EXPECT (f1 != f2);
BEAST_EXPECT (f1.condition() != f2.condition());
BEAST_EXPECT (validate (f1, f1.condition(), {}));
BEAST_EXPECT (validate (f2, f2.condition(), {}));
BEAST_EXPECT (! validate (f1, f2.condition(), {}));
BEAST_EXPECT (! validate (f2, f1.condition(), {}));
// For non-prefix conditions, the message matters
// so verify that it does:
for (std::size_t i = 1; i < sizeof(msg); ++i)
{
BEAST_EXPECT (! validate (f1, f1.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f2, f2.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f1, f2.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f2, f1.condition(),
Slice(msg, i)));
}
}
{ // Test signing with non-empty prefix and non-empty
// message to ensure that the prefix is properly
// prepended to the message:
std::string const m = prefix1 + prefix2;
// Construct a prefix condition with the prefix
// prefix1, containing a Ed25519 signature for prefix1+prefix2
// and check that it passing prefix2 validates, while
// passing anything else fails:
PrefixSha256 f;
f.setPrefix(makeSlice (prefix1));
f.setSubfulfillment(std::make_unique<Ed25519> (
SecretKey{ sk }, makeSlice (m)));
BEAST_EXPECT (to_string(f) ==
"cf:1:B3ByZWZpeDEABGBEKZMGUASqHkxI9N0BWBlMA"
"jSbzGZM2W5ADqJpkYqNUTiaLmMYVDHrc-tKqXcmRIT"
"RFqtYxru4rMSIplCYRP71H9tD09mnfqw4eu5FAJZw1"
"wa_NOmw78ADIlB4_ENJWAo");
auto const c = f.condition();
BEAST_EXPECT (validate (f, c, makeSlice(prefix2)));
BEAST_EXPECT (! validate (f, c, {}));
BEAST_EXPECT (! validate (f, c, makeSlice(prefix1)));
BEAST_EXPECT (! validate (f, c, makeSlice(m)));
for (std::size_t i = 1; i < sizeof(msg); ++i)
BEAST_EXPECT (! validate (f, c, Slice(msg, i)));
}
}
void testKnown ()
{
testcase ("Known");
Slice const empty {};
Slice const abc { "abc", 3 };
Slice const abcd { "abcd", 4 };
Slice const vwxyz { "vwxyz", 5 };
{ // empty prefix with an empty PREIMAGE-SHA256 subfulfillment
auto f = loadFulfillment ("cf:1:AAAAAA");
BEAST_EXPECT (f);
{
auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
auto c = loadCondition ("cc:1:7:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c);
{
auto c1 = loadCondition (to_string(*c));
BEAST_EXPECT (c1);
BEAST_EXPECT (*c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(*c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (*c == *c2);
}
// Ensure that it has the correct features set
BEAST_EXPECT (f->features() ==
(feature_sha256 | feature_prefix | feature_preimage));
// Test manual construction
{
PrefixSha256 f2;
f2.setPrefix({});
f2.setSubfulfillment(loadFulfillment ("cf:0:"));
BEAST_EXPECT (f2 == *f);
BEAST_EXPECT (f2.condition() == *c);
}
// The PREIMAGE-SHA256 we contain validates for
// any message. So, this condition should work
// with any buffer:
check (*f, c.get(), empty, empty);
check (*f, c.get(), abc, abc);
check (*f, c.get(), abcd, abcd);
check (*f, c.get(), vwxyz, vwxyz);
}
{ // A PREFIX-SHA256 with an empty prefix, wrapping
// the PREFIX-SHA256 condition we created above
// which contains a PREIMAGE-SHA256
auto f = loadFulfillment ("cf:1:AAABBAAAAAA");
BEAST_EXPECT (f);
{
auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
auto c = loadCondition ("cc:1:7:Mp5A0CLrJOMAUMe0-qFb-_5U2C0X-iuwwfvumOT0go8:2");
BEAST_EXPECT (c);
{
auto c1 = loadCondition (to_string(*c));
BEAST_EXPECT (c1);
BEAST_EXPECT (*c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(*c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (*c == *c2);
}
// Ensure that it has the correct features set
BEAST_EXPECT (f->features() ==
(feature_sha256 | feature_prefix | feature_preimage));
// Test manual construction
{
PrefixSha256 f2;
f2.setPrefix({});
f2.setSubfulfillment(loadFulfillment ("cf:1:AAAAAA"));
BEAST_EXPECT (f2 == *f);
BEAST_EXPECT (f2.condition() == *c);
}
// The PREIMAGE-SHA256 we contain validates for
// any message. So, this condition should work
// with any buffer:
check (*f, c.get(), empty, empty);
check (*f, c.get(), abc, abc);
check (*f, c.get(), abcd, abcd);
check (*f, c.get(), vwxyz, vwxyz);
}
{ // A PREFIX-SHA256, with the prefix set to 'abc'
// that wraps around an ED25519 condition signing
// the message 'abc':
auto f = loadFulfillment (
"cf:1:A2FiYwAEYHahWSBEpuT1ESZbynOmBNkLBSnR32Ar4woZqSV2YNH1rsara"
"pEir_D33Llmf_YTE2iUcytueMJvW2cxAeJn_i4rZfpNU9rUeKGtpk1Q_R39t9l"
"JINw-GlZKZHscujVgAQ");
BEAST_EXPECT (f);
{
auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
auto c = loadCondition ("cc:1:25:KHqL2K2uisoMhxznwl-6pai-ENDk2x9Wru6Ls63O5Vs:100");
BEAST_EXPECT (c);
{
auto c1 = loadCondition (to_string(*c));
BEAST_EXPECT (c1);
BEAST_EXPECT (*c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(*c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (*c == *c2);
}
// Ensure that it has the correct features set
BEAST_EXPECT (f->features() ==
(feature_sha256 | feature_prefix | feature_ed25519));
// Test manual construction
{
PrefixSha256 f2;
f2.setPrefix(abc);
f2.setSubfulfillment(loadFulfillment (
"cf:4:dqFZIESm5PURJlvKc6YE2QsFKdHfYCvjChmpJXZg0fWuxqtqkSKv8"
"PfcuWZ_9hMTaJRzK254wm9bZzEB4mf-Litl-k1T2tR4oa2mTVD9Hf232Uk"
"g3D4aVkpkexy6NWAB"));
// Check the subfulfillment directly:
auto sc = f2.subcondition();
check (f2.subfulfillment(), sc, empty, abc);
check (f2.subfulfillment(), sc, abc, abc);
check (f2.subfulfillment(), sc, abcd, abc);
check (f2.subfulfillment(), sc, vwxyz, abc);
// This may seem counterintuitive, but it's
// not: the subfulfillment signed the message
// "abc"; our prefix is also "abc" so in order
// to verify this condition successfully, the
// message must be empty:
check (f2, c.get(), empty, empty);
check (f2, c.get(), abc, empty);
check (f2, c.get(), abcd, empty);
check (f2, c.get(), vwxyz, empty);
}
// Like before, the ED25519 condition we contain
// signed the message 'abc' which is our prefix
// which means that this will only validate with
// an empty message:
check (*f, c.get(), empty, empty);
check (*f, c.get(), abc, empty);
check (*f, c.get(), abcd, empty);
check (*f, c.get(), vwxyz, empty);
}
}
void testBinaryCodec()
{
testcase ("Binary Encoding");
// A sample prefix+Ed25519 fulfillment and its
// associated condition:
std::string const xf =
"cf:1:DUhlbGxvIFdvcmxkISAABGDsFyuTrV5WO_STL"
"HDhJFA0w1Rn7y79TWTr-BloNGfiv7YikfrZQy-PKYu"
"cSkiV2-KT9v_aGmja3wzN719HoMchKl_qPNqXo_TAP"
"qny6Kwc7IalHUUhJ6vboJ0bbzMcBwo";
std::string const xc =
"cc:1:25:1EMtp3YUOBZgeW3lX1lOIoAbUjx9maUty9TMJpMgXo4:110";
// The subfulfillment for the above, along with its
// associated condition:
std::string const xsf =
"cf:4:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZa"
"DRn4r-2IpH62UMvjymLnEpIldvik_b_2hpo2t8Mze9"
"fR6DHISpf6jzal6P0wD6p8uisHOyGpR1FISer26CdG"
"28zHAcK";
std::string const xsc =
"cc:4:20:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r8:96";
auto f = loadFulfillment (xf);
BEAST_EXPECT (f);
BEAST_EXPECT (to_string(*f) == xf);
auto c = loadCondition (xc);
BEAST_EXPECT (c);
BEAST_EXPECT (to_string(*c) == xc);
BEAST_EXPECT (f->condition() == c);
BEAST_EXPECT (to_string(f->condition()) == xc);
auto subf = loadFulfillment (xsf);
BEAST_EXPECT (subf);
BEAST_EXPECT (to_string(*subf) == xsf);
auto subc = loadCondition (xsc);
BEAST_EXPECT (subc);
BEAST_EXPECT (to_string(*subc) == xsc);
// Now generate the binary versions and ensure
// that they match what we expect. Then load them
// and ensure they're identical:
{
auto const fblob1 = hexblob(
"0001710d48656c6c6f20576f726c642120000460ec172b93ad5e563bf4"
"932c70e1245034c35467ef2efd4d64ebf819683467e2bfb62291fad943"
"2f8f298b9c4a4895dbe293f6ffda1a68dadf0ccdef5f47a0c7212a5fea"
"3cda97a3f4c03ea9f2e8ac1cec86a51d452127abdba09d1b6f331c070a");
auto const fblob2 = to_blob(*f);
BEAST_EXPECT (fblob1 == fblob2);
auto f2 = loadFulfillment(makeSlice(fblob2));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
{
auto const cblob1 = hexblob (
"0001012520d4432da77614381660796de55f594e22801b523c7d99a52d"
"cbd4cc2693205e8e016e");
auto const cblob2 = to_blob(*c);
BEAST_EXPECT (cblob1 == cblob2);
auto c2 = loadCondition(makeSlice(cblob2));
BEAST_EXPECT (c2);
BEAST_EXPECT (*c == c2);
}
}
void testNested()
{
testcase ("Nested");
std::string const abc = "abc";
std::string const def = "def";
std::string const abcdef = abc + def;
{ // prefix ("abc", prefix ("def", ed25519 (..., "abcdef")))
std::array<std::uint8_t, 32> sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
auto edf = std::make_unique<Ed25519> (
SecretKey{ sk }, makeSlice (abcdef));
// Inner
auto pif = std::make_unique<PrefixSha256>();
pif->setPrefix(makeSlice (abc));
pif->setSubfulfillment (std::move(edf));
// Outer
auto pof = std::make_unique<PrefixSha256>();
pof->setPrefix(makeSlice (def));
pof->setSubfulfillment (std::move (pif));
auto const c = pof->condition();
// The condition should validate with an empty
// message, since the nested prefixes contain
// the full message.
check (*pof, c, {}, {});
// It should fail with anything else.
check (*pof, c, makeSlice (abc), {});
check (*pof, c, makeSlice (def), {});
check (*pof, c, makeSlice (abcdef), {});
}
{ // prefix ("abc", prefix ("def", preimage (...)))
auto const v = hexblob (
"6B62BA0A77D5C7A423A5FC937EE5FF09");
auto img = std::make_unique<PreimageSha256> (
makeSlice (v));
// Inner
auto pif = std::make_unique<PrefixSha256>();
pif->setPrefix(makeSlice (abc));
pif->setSubfulfillment (std::move(img));
// Outer
auto pof = std::make_unique<PrefixSha256>();
pof->setPrefix(makeSlice (def));
pof->setSubfulfillment (std::move (pif));
auto const c = pof->condition();
// The condition should validate with any message
// since it terminates at a preimage, which
// validates for any message:
check (*pof, c, {}, {});
check (*pof, c, makeSlice (abc), makeSlice (abc));
check (*pof, c, makeSlice (def), makeSlice (def));
check (*pof, c, makeSlice (abcdef), makeSlice (abcdef));
}
}
void run ()
{
testKnown ();
testNested ();
testPrefix ();
testBinaryCodec ();
testMalformedCondition ();
}
};
BEAST_DEFINE_TESTSUITE (PrefixSha256, conditions, ripple);
}
}

View File

@@ -17,13 +17,16 @@
*/ */
//============================================================================== //==============================================================================
#include <ripple/basics/Buffer.h>
#include <ripple/basics/strHex.h> #include <ripple/basics/strHex.h>
#include <ripple/basics/Slice.h>
#include <ripple/beast/unit_test.h> #include <ripple/beast/unit_test.h>
#include <ripple/conditions/Condition.h> #include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h> #include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/PreimageSha256.h> #include <ripple/conditions/impl/PreimageSha256.h>
#include <algorithm> #include <algorithm>
#include <numeric> #include <string>
#include <utility>
#include <vector> #include <vector>
namespace ripple { namespace ripple {
@@ -31,305 +34,155 @@ namespace cryptoconditions {
class PreimageSha256_test : public beast::unit_test::suite class PreimageSha256_test : public beast::unit_test::suite
{ {
void inline
check ( Buffer
std::vector<std::uint8_t> const& payload, hexblob(std::string const& s)
std::string const& fulfillment,
std::string const& condition)
{ {
auto f = loadFulfillment (fulfillment); std::vector<std::uint8_t> x;
auto c = loadCondition (condition); x.reserve(s.size() / 2);
BEAST_EXPECT (f); auto iter = s.cbegin();
BEAST_EXPECT (c);
if (f && c) while (iter != s.cend())
{ {
// Ensure that loading works correctly int cHigh = charUnHex(*iter++);
BEAST_EXPECT (to_string (*f) == fulfillment);
{ if (cHigh < 0)
auto f2 = loadFulfillment (makeSlice(to_blob (*f))); return {};
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
BEAST_EXPECT (to_string (*c) == condition); int cLow = charUnHex(*iter++);
{ if (cLow < 0)
auto c1 = loadCondition (makeSlice(to_blob (*c))); return {};
BEAST_EXPECT (c1);
BEAST_EXPECT (*c == *c1);
auto c2 = loadCondition (to_string (*c)); x.push_back(
BEAST_EXPECT (c2); static_cast<std::uint8_t>(cHigh << 4) |
BEAST_EXPECT (*c == *c2); static_cast<std::uint8_t>(cLow));
}
// Ensures that the fulfillment generates
// the condition correctly:
BEAST_EXPECT (f->condition() == c);
// Ensure that the fulfillment contains the
// correct payload:
BEAST_EXPECT (f->payload() == makeSlice(payload));
// Check fulfillment
BEAST_EXPECT (validate (*f, *c, {}));
// Preimage ignores the message. Verify that it does:
std::string test = "aaabbc";
do
{
for (Slice t = makeSlice (test); !t.empty(); t += 1)
BEAST_EXPECT (validate (*f, *c, t));
} while (std::next_permutation(test.begin(), test.end()));
} }
return { x.data(), x.size() };
} }
void testKnownVectors () void
testKnownVectors()
{ {
testcase ("Known Vectors"); testcase("Known Vectors");
check (hexblob (""), std::pair<std::string, std::string> const known[] =
"cf:0:",
"cc:0:3:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:0");
check (hexblob ("00"),
"cf:0:AA",
"cc:0:3:bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoB0:1");
check (hexblob ("ff"),
"cf:0:_w",
"cc:0:3:qBAK5qoZQNC2Y7sxzUZhQuu9vVGHExuS2TgYmHgy64k:1");
check (hexblob ("feff"),
"cf:0:_v8",
"cc:0:3:8ZdpKBDUV-KX_OnFZTsCWB_5mlCFI3DynX5f5H2dN-Y:2");
check (hexblob ("fffe"),
"cf:0:__4",
"cc:0:3:s9UQ7wQnXKjmmOWzy7Ds45Se-SUvDNyDnp7jR0CaIgk:2");
check (hexblob ("00ff"),
"cf:0:AP8",
"cc:0:3:But9amnuGeX733SQGNPSq_oEvL0TZdsxLrhtxxaTibg:2");
check (hexblob ("0001"),
"cf:0:AAE",
"cc:0:3:tBP0fRPuL-bIRbLuFBr4HehY307FSaWLeXC7lmRbyNI:2");
check (hexblob ("616263"),
"cf:0:YWJj",
"cc:0:3:ungWv48Bz-pBQUDeXa4iI7ADYaOWF3qctBD_YfIAFa0:3");
check (hexblob ("f1f2f3f4f5f6f7f8f9fafbfcfdfeff"),
"cf:0:8fLz9PX29_j5-vv8_f7_",
"cc:0:3:ipyQ4jcC1AbAYiuzYDZ1YkAr4O1IxOe5XBKJdJ17nPA:15");
std::vector<std::uint8_t> const v1 (256, 0x00);
check (v1,
"cf:0:"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAA",
"cc:0:3:U0HmsmRpeacOV2UwB6HzEBaUIeyb3Z8aVkj3Wt4AWvE:256");
std::vector<std::uint8_t> const v2 (256, 0xFF);
check (v2,
"cf:0:"
"__________________________________________________________________________________"
"__________________________________________________________________________________"
"__________________________________________________________________________________"
"__________________________________________________________________________________"
"_____________w",
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256");
std::vector<std::uint8_t> v3 (256);
std::iota (v3.begin(), v3.end(), std::uint8_t(0));
check (v3,
"cf:0:"
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD"
"0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6"
"e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7"
"i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T1"
"9vf4-fr7_P3-_w",
"cc:0:3:QK_y6dLYki5Hr9RkjmlnSXFYeF-9Hahw5xECZr-USIA:256");
std::vector<std::uint8_t> v4 (4096);
std::iota (v4.begin(), v4.end(), std::uint8_t(0));
check (v4,
"cf:0:"
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD"
"0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6"
"e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7"
"i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T1"
"9vf4-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMj"
"M0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9w"
"cXJzdHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra"
"6vsLGys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zNzs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err"
"7O3u7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKC"
"kqKywtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVm"
"Z2hpamtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6"
"SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh"
"4uPk5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh"
"8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltc"
"XV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZ"
"qbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX"
"2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFB"
"UWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFS"
"U1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5"
"CRkpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zN"
"zs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O3u7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCg"
"sMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdI"
"SUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhY"
"aHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLD"
"xMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh4uPk5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AA"
"ECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-"
"P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3"
"x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5"
"uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19v"
"f4-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0"
"NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcX"
"JzdHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra6v"
"sLGys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zNzs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O"
"3u7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkq"
"KywtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2"
"hpamtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6Sl"
"pqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh4u"
"Pk5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8g"
"ISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV"
"5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqb"
"nJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2N"
"na29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFBUW"
"FxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1"
"RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5CR"
"kpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zNzs"
"_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O3u7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCgsM"
"DQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdISU"
"pLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaH"
"iImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLDxM"
"XGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh4uPk5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AAEC"
"AwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0"
"BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9"
"fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5ur"
"u8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4"
"-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NT"
"Y3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJz"
"dHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra6vsL"
"Gys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zNzs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O3u"
"7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKy"
"wtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hp"
"amtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6Slpq"
"eoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh4uPk"
"5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gIS"
"IjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f"
"YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ"
"2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna"
"29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w",
"cc:0:3:yPXQNB1U2VGnGxNubir8sU0R7YSJp64Sao_uDfbs8ZM:4096");
}
void testOverlong ()
{
testcase ("Fulfillment Maximum Payload Length");
std::vector<std::uint8_t> v;
// As long as we don't exceed the fulfillment length
// we should succeed:
unexcept ([this, &v]()
{ {
v.resize(maxSupportedFulfillmentLength - 1); { "A0028000",
PreimageSha256 h1 { makeSlice(v) }; "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100" },
auto c1 = h1.condition(); { "A0058003616161",
BEAST_EXPECT (c1.maxFulfillmentLength == h1.payload().size()); "A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103" },
v.resize(maxSupportedFulfillmentLength);
PreimageSha256 h2 { makeSlice(v) };
auto c2 = h2.condition();
BEAST_EXPECT (c2.maxFulfillmentLength == h2.payload().size());
});
except ([this, &v] ()
{
v.resize(maxSupportedFulfillmentLength + 1);
PreimageSha256 h3 { makeSlice(v) };
});
}
void testFulfillment ()
{
testcase ("Fulfillment");
std::vector<std::uint8_t> const v (256, 0x00);
PreimageSha256 const f (makeSlice(v));
// First check against incorrect conditions:
char const* const ccs[] =
{
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256",
"cc:1:25:XkflBmyISKuevH8-850LuMrzN-HT1Ds9zKUEzaZ2Wk0:103",
"cc:2:2b:d3O4epRCo_3rj17Bf3v8hp5ig7vq84ivPok07T9Rdl0:146",
"cc:3:11:uKkFs6dhGZCwD51c69vVvHYSp25cRi9IlvXfFaxhMjo:518",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96"
}; };
for (auto cc : ccs) std::error_code ec;
{
auto c = loadCondition (cc);
if (BEAST_EXPECT (c)) auto f1 = Fulfillment::deserialize (hexblob(known[0].first), ec);
{ BEAST_EXPECT (f1);
BEAST_EXPECT (! validate (f, c.get(), {})); BEAST_EXPECT (!ec);
for (Slice m = makeSlice(v); !m.empty(); m += 1)
BEAST_EXPECT (! validate (f, c.get(), m));
}
}
// Now, finally, check the correct condition: auto c1 = Condition::deserialize (hexblob(known[0].second), ec);
auto c = loadCondition ( BEAST_EXPECT (c1);
"cc:0:3:U0HmsmRpeacOV2UwB6HzEBaUIeyb3Z8aVkj3Wt4AWvE:256"); BEAST_EXPECT (!ec);
if (BEAST_EXPECT (c)) auto f2 = Fulfillment::deserialize(hexblob(known[1].first), ec);
{ BEAST_EXPECT(f2);
// Note that the message may not, necessarily BEAST_EXPECT(!ec);
// have anything to do with the fulfillment, so
// we expect the validation to succeed with any auto c2 = Condition::deserialize(hexblob(known[1].second), ec);
// message: BEAST_EXPECT(c2);
BEAST_EXPECT (validate (f, c.get(), {})); BEAST_EXPECT(!ec);
for (Slice m = makeSlice(v); !m.empty(); m += 1)
BEAST_EXPECT (validate (f, c.get(), m)); // Check equality and inequality
} BEAST_EXPECT (f1->condition() == *c1);
BEAST_EXPECT (f1->condition() != *c2);
BEAST_EXPECT (f2->condition() == *c2);
BEAST_EXPECT (f2->condition() != *c1);
BEAST_EXPECT (*c1 != *c2);
BEAST_EXPECT (*c1 == *c1);
BEAST_EXPECT (f1->condition() == f1->condition());
// Should validate with the empty string
BEAST_EXPECT (validate (*f1, *c1));
BEAST_EXPECT (validate(*f2, *c2));
// And with any string - the message doesn't matter for PrefixSha256
BEAST_EXPECT (validate (*f1, *c1, makeSlice(known[0].first)));
BEAST_EXPECT (validate(*f1, *c1, makeSlice(known[0].second)));
BEAST_EXPECT (validate(*f2, *c2, makeSlice(known[0].first)));
BEAST_EXPECT (validate(*f2, *c2, makeSlice(known[0].second)));
// Shouldn't validate if the fulfillment & condition don't match
// regardless of the message.
BEAST_EXPECT (! validate(*f2, *c1));
BEAST_EXPECT (! validate(*f2, *c1, makeSlice(known[0].first)));
BEAST_EXPECT (! validate(*f2, *c1, makeSlice(known[0].second)));
BEAST_EXPECT (! validate(*f1, *c2));
BEAST_EXPECT (! validate(*f1, *c2, makeSlice(known[0].first)));
BEAST_EXPECT (! validate(*f1, *c2, makeSlice(known[0].second)));
} }
void testMalformedCondition () void testOtherTypes()
{ {
testcase ("Malformed Condition"); testcase ("Other Types");
// This is malformed and will not load because a std::pair<std::string, std::string> const others[] =
// feature suite of 0 is not supported. {
auto c1 = loadCondition ( // PREFIX + PREIMAGE:
"cc:0:0:U0HmsmRpeacOV2UwB6HzEBaUIeyb3Z8aVkj3Wt4AWvE:256"); { "A10B8000810100A204A0028000",
BEAST_EXPECT (!c1);
// The following will load but fail in different ways "A12A8020BB1AC5260C0141B7E54B26EC2330637C5597BF811951AC09E744AD20FF77E287810204"
auto c2 = loadCondition ( // only sha256 "0082020780" },
"cc:0:1:U0HmsmRpeacOV2UwB6HzEBaUIeyb3Z8aVkj3Wt4AWvE:256");
BEAST_EXPECT (c2 && !validate(*c2)); // THRESHOLD:
{ "A208A004A0028000A100",
"A22A8020B4B84136DF48A71D73F4985C04C6767A778ECB65BA7023B4506823BEEE7631B9810204"
"0082020780" },
// RSA:
{ "A382020880820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97"
"A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C48"
"3E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A"
"4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67E"
"D17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB45233973875275"
"9639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77"
"E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B981820100BD42D6569F6599AED455F96B"
"C0ED08ED1480BF36CD9E1467F9C6F74461C9E3A749334B2F6404AA5F9F6BAFE76C347D069250B35D"
"1C970C793059EE733A8193F30FA78FEC7CAE459E3DDFD7633805D476940D0CB53D7FB389DCDAEAF6"
"E8CF48C4B5635430E4F2BCDFE505C2C0FC17B40D93C7EDB7C261EBF43895A705E024AA0549A660F7"
"0A32150647522DBE6B63520497CFF8F8D5D74768A27C5B86E580BE3FCDC96F1976293CBA0D58DFC6"
"0B518B632A6DC1E950C43E231FE1A379AA6DDCC52C70EDF851C6C0123A964261CFDB3857CD6CD5AD"
"C37D8DA2CC924EDAE1D84CF6124587F274C1FA3697DA2901F0269F03B243C03B614E0385E1961FAC"
"5000F9BB",
"A32580204849505152535455484950515253545548495051525354554849505152535455810101" },
// ED25519:
{ "A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E556"
"4300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E"
"39701CF9B46BD25BF5F0595BBE24655141438E7A100B",
"A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F810302"
"0000" }
};
for (auto x : others)
{
std::error_code ec;
BEAST_EXPECT (!Fulfillment::deserialize(hexblob(x.first), ec));
BEAST_EXPECT (!Condition::deserialize (hexblob(x.second), ec));
}
auto c3 = loadCondition ( // only preimage
"cc:1:2:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c3 && !validate(*c3));
auto c4 = loadCondition ( // only prefix+sha256
"cc:1:20:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c4 && !validate(*c4));
} }
void run () void run ()
{ {
testKnownVectors (); testKnownVectors();
testOverlong (); testOtherTypes();
testFulfillment ();
testMalformedCondition ();
} }
}; };

View File

@@ -1,362 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/basics/strHex.h>
#include <ripple/beast/unit_test.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/RsaSha256.h>
#include <algorithm>
namespace ripple {
namespace cryptoconditions {
class RsaSha256_test : public beast::unit_test::suite
{
// A well-known message, its fulfillment and its condition
std::string const knownMessage = "aaa";
std::string const knownFulfillment =
"cf:3:ggEA4e-LJNb3awnIHtd1KqJi8ETwSodNQ4CdMc6mEvmbDJeotDdBU-Pu89ZmF"
"oQ-DkHCkyZLcbYXPbHPDWzVWMWGV3Bvzwl_cExIPlnL_f1bPue8gNdAxeDwR_PoX8D"
"XWBV3am8_I8XcXnlxOaaILjgzakpfs2E3Yg_zZj264yhHKAGGL3Ly-HsgK5yJrdfNW"
"woHb3xT41A59n7RfsgV5bQwXMYxlwaNXm5Xm6beX04-V99eTgcv8s5MZutFIzlzh1J"
"1ljnwJXv1fb1cRD-1FYzOCj02rce6AfM6C7bbsr-YnWBxEvI0TZk-d-VjwdNh3t9X2"
"pbvLPxoXwArY4JGpbMJuYIBAEjolF7-AHVW1b9NXySeSAj3MH4pUR0yYtrvYdiAmPm"
"qSovAYjqMl1c49l1r9FnVQ_KJ1zy8evTqOjP78-xEQER5EdcilAkeVhgzYo5Jp3LtY"
"I3mxEWVqR4-F9bPXsOyUo1j0q3WRjmJsS7sV332Rwlg32gyqdhMNg0cIXrWTIYlvbW"
"U-wraCGzey73lgNQkv5dG0vDDEoJtu7AK1otSxMt9RxVro146mByXOGN5LMgNBKGAI"
"QpSQVhltks6YXdLHTl114qYsIIe5Vyg-GMF1CUp4Q6wFc79QC-1myq7je7lKm8kR9I"
"oRgPSGc1OjPnP_dVJiInDeAtZ3WpX731zJiA";
std::string const knownCondition =
"cc:3:11:uKkFs6dhGZCwD51c69vVvHYSp25cRi9IlvXfFaxhMjo:518";
// Some RSA keys we use to check
std::string const goodKey =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEpAIBAAKCAQEAq9QZZzSmdXaAFeSkUgK8/xuyKPQEFNkiEzatMSmmGN+DpCR7\n"
"HAK4W3wHfW6jegQFPlsvWLWbtnwgwCHhv1oW4jiL7BDD3prJIuJmhCE/w6WPKTFb\n"
"WhvxQY5sbqCDnjcd0x/adKjNLaTpSRANscR+hahbQA1vqPperHz/Z20reRPQ6aDn\n"
"w+qBL7dnVFgCPu8QrueyaZ0I5xQMIJiF0CnLXbPbU0ybxDVNgBDXsYeHE4VEM7ek\n"
"NQAMrsr6wodD5y94jynXHharEv5dzKsQFPRAGKzTvwQqWSiZr+Fgq4q1GqQW+oUa\n"
"xssbXWkGHEOxPBz6RFqLOFd8JnM9yVlWs2nWDwIDAQABAoIBAQCW9WNQCbCImBBV\n"
"q6c1qdQzaDiwxBjl3BGUwb+M5qNXTN9RkP9bj4Q6U5AdAdu7sdaNfvzsubjQrOL1\n"
"CY9UVqiuHLHJNr1uT5yP+knIoZFsqIJK1WMFmnDtgFwBISIhGRkpx91cCoUgKbcO\n"
"in0Nha0Gbe+lKWjFExmj/rlAO2grGO4yYd+P27BZ99mHBXPMQIIwQbSeRUTBLiRy\n"
"VhN7Mb60wag2m4F9zriEzhcj7pePNKHvpqNiuT5FCVoUNZW2CqFoXgEghF1VdWj5\n"
"UovZITUCN9zrGdFHWQj3Hx1LZo3UQz3auUp4XQ89dIm1GefqcZYpnzth+D43UXtC\n"
"f33nK9shAoGBANOm6aAhAh8Ahtc+52u3ykTnRGwI+H3QvgvzE7keZGIUb0yKtd/+\n"
"yuxYI3DgN/Mn69p3pLrYh/CJ9VonhELYv4oekGZfCmqEtUxmkTa9EgTKAIxIDw9W\n"
"t/jNBzzBccF22kl9w+nYHNOqo8M9yUlx0xwLnfioVX45G4jouucfPUDxAoGBAM/V\n"
"CmykuH8vIYluyobldKFglXKeFMQKlKG+Dv9wG71RSYPyamd7lu3fPgZyGcbAGd49\n"
"/Wewpq8ieagjrTuCEGlI0lhrL35axvBDKQcS+LTp+uD6vpxnFm986cxLgYRTNq0/\n"
"eMUvJy72Ms5zajZUMdjM/nqTA9zpVDofL+xb6Ib/AoGAVheU/H+wvy+VqcR6mgRe\n"
"kHyKBm/3tCXOyEmOAkTsjEDHrRjXNlAL9us7L1TlLVFVzL3SEfa2BQ/47z0XvaEw\n"
"+FvKXPnX4NAudu9ZrixmQfBxHJ7LEXAy0U+E3B/Lx+gyjqZLpLk1sJu+lVJyqB9W\n"
"whevoE/Ixtkv7BbOv+ijH+ECgYAb0WQnzpRzUZenkZDCJYxK3WajhM06wD/MtmfD\n"
"gPn1iR/R7WyYlU5KYIsoybTxiVztBlcYvehRoMev3baeNHaF4R1mgFJHE1d1aUfg\n"
"joWDkZ3m5ykEPjgejBWvJpwbXhf/cHN10S3pd0Ktp30b8IELh8S4G111ADYp4WrE\n"
"tDiXeQKBgQCROYcVuUEfkOiKeGHkBGbbsgj77KzZ1x7wUBzueKAND9+e2kX8kcc2\n"
"lX9DDkShUvZau0EJtQsFTehsZZeeBwtSvWu1A+2Wn9D19Hxe0qJCNZ6bqYQM9i2S\n"
"JjI6wG7YYl/foT3B3Zf3A3G9gUOc+P5/dnUi+6r+l7GUvQ5WnVQjDw==\n"
"-----END RSA PRIVATE KEY-----\n";
std::string const shortKey =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MGMCAQACEQCtbMmYUOdPy+XwgP+xXzfrAgMBAAECEQCFPVJ5GpdMnxfbcKFUUb2\n"
"JAgkA2e+AQuY6Ns8CCQDLtxhLU8j0JQIJAKkphE9pUUp1AghYypxPMNy09QIISr\n"
"srXHy9nPk=\n"
"-----END RSA PRIVATE KEY-----\n";
std::string const longKey =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIJYwIBAAKCAg4Aqx3vteNh7iinTC6EEVaYmCDAeQ2oxXcjYRlx5h1m0ddHplsr\n"
"/e2QbwqExsz8zK9Wlis3EiPBoVX/CI6JxJfCkrpUjLH22jP3J2KTjZ6BkZ7hahOW\n"
"iqttFYFCta9iGirDQk04Wfubbtc4JBHexNUBCyGClCN/Ovd8Yv3KpoL3YOBma6ct\n"
"40Gf2xvG2k6OlLiGg7zvsI2KyD/a0/xFoMrC/X5wWunRFvHZYeb4LM2hqGe79LeK\n"
"NrbdYN+p8S+jzS6jo91C9EoltmS9UNXaax51JT9G83sw1U6KMIWA1jK9+3C92mry\n"
"gkLkdqJzsCsHWLWUZFJfKrZE7KAbw1TBGPSjSakTY3KOgrMghP41k3XGDsmkLMgx\n"
"JGd/F46YqEOB5vfdiZs7y0MuYYZmKY1o6DBNu+cL+sKBzNdtkvTVEskhsrL/OzPA\n"
"sN8srZdQm/Kim+2yKR+lxWz2kamcV74KCQMn2ALlMflfbFG/6RyHGzbVbiGTlW/8\n"
"gJQY9acMIDaJF0cPeSAa7aEHcQE5o1zrBcORqkWYbhZpPZtuS7GZ3gXwwYHxXvzq\n"
"tg5rMTqvcVzS2o08O+q4BopqAkU0mTF64Yh7izJQ5WGgk+g058WohNm4QB5XPETi\n"
"WlGSsEJjuzpHECq19DNoDe88HhZCwrqJSOl07MN1LPete9YptE1J+zoRasNy5wxJ\n"
"NmZRdP2a61J5AgMBAAECggIOAIdoBQwVhqUDHn+2P2PI9q9LG4OvP2IiyKhJjkvd\n"
"8EMU6+nEM6eYmbaEyFTYWSNPjGEAiW+dQ9f7SPjocjRTMvEQ6V78ZK5+eJF9++0R\n"
"BM7Kvu1F2taYmJVv1+4VfrfeJu0MVg8+ftzTCeXhDjsLouu/9Khs/n0W4iMjWX0y\n"
"HbdXWzTM8g7nGywzasPNbh5ZdnhAxhsbpjqX7P3anu6CBJK7vwTyCTby4mYKc1Bg\n"
"2A9/JsibhI+PXNcPplbor+HpiixdJmJRWk5eoUCaOWCSlXiH/gkl7pqcr9V9j1nw\n"
"hU23BUUVZBmX/Vmza4B4TDPyXB6W4B/YY+orOEz1gGfTDnN3i5QiToshpnZ7BKJH\n"
"kI+NR/m47r+Dab7DFTpl0c9xEVDRtQj0vhKehzV3/FLJOVQtxMqXC+ZjW+rUfjuN\n"
"iNBWChGMMKOllJMvN6o+/lyA07RxSF1shgcfDNQQhnTCKnfz2SbKAxqnzLwVSrWZ\n"
"LldyoyTPKra5uYfldRSXCPM5Or0dSQYAaX8t9jxaYdHq7hT5w7C5lsJdlyeUFlO1\n"
"1+jl5VZ1MT7f6g7Poo+NMdOVsGT7N8D3ERUL/Kmspx1Gpdll5iRVWxtZDpg4ct3G\n"
"7NTxaJOe+gueNg49wo0I1k49gSQ+xnTOyKmzkOGaZ+Ncbapd4OIh+5Y/wuWA5FTi\n"
"msbBxaXyAg/snkOGaY8NEQKCAQcNatn6e2wuNDbvp0qtmG7ipU033acpGzpG0Zmc\n"
"l6mPO0uis8+7cSks0L3IL1Yh3qCR8CBd5gt9JyjVKL/USq7AO2v0Uhnbyn+qbh7f\n"
"476bifVq9iU31M001KccZ3B7Ev5wBsBCAT8GnP/SMxqdQHX6VxuDWc9/bdccidui\n"
"V3wBY1bDsxGNV80Gg5/n/p3gSlkFkdjv9g7Jl6ODTUP/1M8s7siF3Ah93846PBmI\n"
"CqfUgQhm42HEJQAi9dgA+Zhc1dyT1hwnhAhzPaNAaeWHQWCD8OM6WI+/24miAJMi\n"
"kNCDwIITr3H/tz8J5rA6yqXl30lDBKE7KSpUTqSegnSC2U2+29qMUtVlvwKCAQcM\n"
"wNzxi8PSFwLO/e9FBvcuhHCSYbAw3tHMgkNEItM+0wCUw9hpkIrq3XZLwh+GKopw\n"
"9Uqzo2xBq3LeZgiU0nokgnDoixvBTcawnXsR3Y7mQijJo0eG2Nukd+g5wJ7nRp7q\n"
"Rq0KHYzfER1UPcAPI2ZL4T1JU5sdmPqIZuZA0YGSIznQ5htBiQMB4zaJeNN7m4bK\n"
"7e/eEF8AbChzbKiNFzl4am1boPbIZK3xek5cS7pLv5G5vX4R4+t6UY1Na91XQG9l\n"
"YZzYb4cIxhmvy0/zVAjeJpZCJpAQjE67+IZdieEVe+xGNe7qC1TJN+pL1YNxJdi3\n"
"ZFAf9fCYH5Ir6Es+vXCRlyFhNBOFxwKCAQcBQehX30VONzqGz0jiaAzMVO2dtLo7\n"
"0f9uL6qT0Grlr4rxHqTzTjGrr4x5vGX4GqM1yileY3bkLc1X3M/Nl4o1HdyKMz+V\n"
"J687S8K8/N0aOp2zfooSZ3Er6FoZAWC7SBZsbVWLWg6MEh6vlnaCEk58PbmoX7xg\n"
"luy4EftxhX1rq+GvyZJ1iqr+V0ufNG+bW5xoNzj7lDXiksGSRqV+znT0IxTL5sks\n"
"8tKjBormAwmjksw0yE6bUVRn8l5iCQJMgQaBHGnbEjawhjBMkyAdwvTGqMbC6xXd\n"
"xzdo5WDktmm0T1Bhg+nNK2FPDj2p5OATYQ++pipuHveGmzA2YseEk9UDdBtRV1oE\n"
"hQKCAQcBZMpAc1+xA+bArCuLxZkZskuDE73neVJAITQsrAmd4f08RLLPxoYH6K/W\n"
"054SUW/TrFq/iup3ur7Q4yGo8d97Qe4I27rqww8lmfArIaVOMIi4kGluqSBHtvrf\n"
"5Nb4u1T+kT6zzkrozbwAysbEYL/7JuBFtSdMcr1eTrB3AO5CBCt7UspDvS9g822w\n"
"VE34QiTW5G3EPNHFAAzjoEpDMPiM2kSdMNgHSklgDFen6navdH3+aGDwn5HKSkNA\n"
"5LrJoDcMQ0CavoVpRgzkkzlnhBV8AYeGLySrSkoIbL5yVnEMogBOI/K6DQb0/nFS\n"
"XEEDCnnGgOXouD3Uwg59UeN3NcipgHSbZM+FXQKCAQcCswewWvkORgMxDYT68x6P\n"
"hZtkAuy7BxAQ2H7ToYxeiVy4SBELg3xFiSCLNwhdK8En5vmSo3WnjNuWOGZ4ywUe\n"
"KmbxNu7o+zMyOblNg/I6CQMSEuo6jHoLVc9QaODscGco3du8WjRwJ2DnA3HoBJ5F\n"
"L0XftzOCfSrBQfn0Fb2ej4nsaIw1z0wEaAnuDC18/VUHQ0rHl2K2QleX4FwBiyXK\n"
"qWzhAVuxskkfWe3Xgn58IT2MODSDnFhP8j6m0vq5lklwgfIi9c6+y0rmJbhSZI4N\n"
"b/o5HSpWAxfpaSnWzw5moN5JP6DmhGQzgnctW9YL2w4OfZ9jPHl+xWMlSGUd8TD2\n"
"QIpo2Qox/w==\n"
"-----END RSA PRIVATE KEY-----\n";
void check (
Fulfillment const& f,
Condition const& c,
Slice test,
Slice good)
{
BEAST_EXPECT (validate (f, c, test) ==
((test == good) && (f.condition() == c)));
}
void testKnown ()
{
testcase ("Known");
Slice m = makeSlice (knownMessage);
std::string test = "aaabc";
// Load and test string and binary and text
// serialization & deserialization
auto const f = loadFulfillment(knownFulfillment);
BEAST_EXPECT (f);
BEAST_EXPECT (to_string (*f) == knownFulfillment);
{
auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
// Verify the condition for this fulfillment and test
// binary and text serialization & deserialization
auto const c = f->condition();
BEAST_EXPECT (to_string(c) == knownCondition);
{
auto c1 = loadCondition (knownCondition);
BEAST_EXPECT (c1);
BEAST_EXPECT (c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (c == *c2);
}
// First check against incorrect conditions, using both
// correct, incorrect and empty buffers.
char const* const ccs[] =
{
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256",
"cc:1:25:XkflBmyISKuevH8-850LuMrzN-HT1Ds9zKUEzaZ2Wk0:103",
"cc:2:2b:d3O4epRCo_3rj17Bf3v8hp5ig7vq84ivPok07T9Rdl0:146",
"cc:3:11:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96"
};
for (auto cc : ccs)
{
auto nc = loadCondition (cc);
if (BEAST_EXPECT (nc && nc != c))
{
check (*f, nc.get(), makeSlice(test), m);
check (*f, nc.get(), m, m);
check (*f, nc.get(), { }, m);
}
}
// Now try against the correct condition with various
// buffers - most are incorrect, some are correct.
do
{
for (Slice t = makeSlice (test); !t.empty(); t += 1)
check (*f, c, t, m);
} while (std::next_permutation(test.begin(), test.end()));
// And with an empty buffer:
check (*f, c, Slice { }, m);
// Under the existing spec, multiple messages sharing
// the same key should generate the same fulfillment:
{
std::array<std::uint8_t, 3> aaa {{ 'a', 'a', 'a' }};
std::array<std::uint8_t, 3> bbb {{ 'b', 'b', 'b' }};
RsaSha256 f1;
BEAST_EXPECT (f1.sign (goodKey, makeSlice (aaa)));
RsaSha256 f2;
BEAST_EXPECT (f2.sign (goodKey, makeSlice (bbb)));
BEAST_EXPECT (f1.condition () == f2.condition ());
}
}
void testDynamic ()
{
testcase ("Dynamic");
Slice m = makeSlice (knownMessage);
std::string test = "aaabc";
RsaSha256 f;
BEAST_EXPECT (f.sign (goodKey, m));
{
auto const f2 = loadFulfillment(makeSlice(to_blob(f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (f == *f2);
}
// Generate and verify the condition for this fulfillment:
auto const c = f.condition();
{
auto c1 = loadCondition (to_string(c));
BEAST_EXPECT (c1);
BEAST_EXPECT (c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (c == *c2);
}
// First check against incorrect conditions, using both
// correct, incorrect and empty buffers.
char const* const ccs[] =
{
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256",
"cc:1:25:XkflBmyISKuevH8-850LuMrzN-HT1Ds9zKUEzaZ2Wk0:103",
"cc:2:2b:d3O4epRCo_3rj17Bf3v8hp5ig7vq84ivPok07T9Rdl0:146",
"cc:3:11:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96"
};
for (auto cc : ccs)
{
auto nc = loadCondition (cc);
if (BEAST_EXPECT (nc && nc != c))
{
check (f, nc.get(), makeSlice(test), m);
check (f, nc.get(), m, m);
check (f, nc.get(), { }, m);
}
}
// Now try against the correct condition with various
// buffers - most are incorrect, some are correct.
do
{
for (Slice t = makeSlice (test); !t.empty(); t += 1)
check (f, c, t, m);
} while (std::next_permutation(test.begin(), test.end()));
// And with an empty buffer:
check (f, c, Slice { }, m);
}
void testKeySize ()
{
testcase ("Key Sizes");
RsaSha256 f1;
BEAST_EXPECT (!f1.sign (longKey, makeSlice (knownMessage)));
RsaSha256 f2;
BEAST_EXPECT (!f2.sign (shortKey, makeSlice (knownMessage)));
}
void testMalformedCondition ()
{
testcase ("Malformed Condition");
// This is malformed and will not load because a
// feature suite of 0 is not supported.
auto c1 = loadCondition (
"cc:3:0:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518");
BEAST_EXPECT (!c1);
// The following will load but fail in different ways
auto c2 = loadCondition ( // only sha256
"cc:3:1:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518");
BEAST_EXPECT (c2 && !validate(*c2));
auto c3 = loadCondition ( // only preimage
"cc:3:2:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518");
BEAST_EXPECT (c3 && !validate(*c3));
auto c4 = loadCondition ( // sha256+preimage
"cc:4:3:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c4 && !validate(*c4));
auto c5 = loadCondition ( // Ed25519+sha256+preimage
"cc:1:23:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c5 && !validate(*c5));
auto c6 = loadCondition ( // rsa+sha256+threshold
"cc:1:19:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c6 && !validate(*c6));
auto c7 = loadCondition ( // rsa
"cc:1:10:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c7 && !validate(*c7));
}
void run ()
{
testKnown();
testDynamic();
testKeySize();
testMalformedCondition();
}
};
BEAST_DEFINE_TESTSUITE (RsaSha256, conditions, ripple);
}
}

View File

@@ -39,7 +39,7 @@
#include <test/app/SetAuth_test.cpp> #include <test/app/SetAuth_test.cpp>
#include <test/app/SetRegularKey_test.cpp> #include <test/app/SetRegularKey_test.cpp>
#include <test/app/SHAMapStore_test.cpp> #include <test/app/SHAMapStore_test.cpp>
#include <test/app/SusPay_test.cpp> #include <test/app/Escrow_test.cpp>
#include <test/app/Taker_test.cpp> #include <test/app/Taker_test.cpp>
#include <test/app/Transaction_ordering_test.cpp> #include <test/app/Transaction_ordering_test.cpp>
#include <test/app/TrustAndBalance_test.cpp> #include <test/app/TrustAndBalance_test.cpp>

View File

@@ -18,6 +18,3 @@
//============================================================================== //==============================================================================
#include <test/conditions/PreimageSha256_test.cpp> #include <test/conditions/PreimageSha256_test.cpp>
#include <test/conditions/PrefixSha256_test.cpp>
#include <test/conditions/RsaSha256_test.cpp>
#include <test/conditions/Ed25519_test.cpp>