mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-26 14:05:51 +00:00
Add SuspendedPayment feature (RIPD-992):
The code is enabled in jtx::Env, and enabled in production ledgers only if the SuspendedPayment amendment is voted into a ledger.
This commit is contained in:
committed by
Edward Hennis
parent
d49f9ea109
commit
3f0eacf5e7
@@ -1709,6 +1709,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\tests\SusPay_test.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\tx\apply.h">
|
<ClInclude Include="..\..\src\ripple\app\tx\apply.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClCompile Include="..\..\src\ripple\app\tx\impl\apply.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tx\impl\apply.cpp">
|
||||||
@@ -1809,6 +1813,12 @@
|
|||||||
</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>
|
||||||
@@ -3168,7 +3178,7 @@
|
|||||||
<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\rpc\handlers\Feature.cpp">
|
<ClCompile Include="..\..\src\ripple\rpc\handlers\Feature1.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>
|
||||||
@@ -3696,6 +3706,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\test\jtx\impl\tag.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\test\jtx\impl\ticket.cpp">
|
<ClCompile Include="..\..\src\ripple\test\jtx\impl\ticket.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>
|
||||||
@@ -3746,6 +3760,8 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\test\jtx\sig.h">
|
<ClInclude Include="..\..\src\ripple\test\jtx\sig.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\test\jtx\tag.h">
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\test\jtx\tags.h">
|
<ClInclude Include="..\..\src\ripple\test\jtx\tags.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\test\jtx\ter.h">
|
<ClInclude Include="..\..\src\ripple\test\jtx\ter.h">
|
||||||
|
|||||||
@@ -2457,6 +2457,9 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\app\tests\Regression_test.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tests\Regression_test.cpp">
|
||||||
<Filter>ripple\app\tests</Filter>
|
<Filter>ripple\app\tests</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\tests\SusPay_test.cpp">
|
||||||
|
<Filter>ripple\app\tests</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\tx\apply.h">
|
<ClInclude Include="..\..\src\ripple\app\tx\apply.h">
|
||||||
<Filter>ripple\app\tx</Filter>
|
<Filter>ripple\app\tx</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -2556,6 +2559,12 @@
|
|||||||
<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>
|
||||||
@@ -3870,7 +3879,7 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\ConsensusInfo.cpp">
|
<ClCompile Include="..\..\src\ripple\rpc\handlers\ConsensusInfo.cpp">
|
||||||
<Filter>ripple\rpc\handlers</Filter>
|
<Filter>ripple\rpc\handlers</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\Feature.cpp">
|
<ClCompile Include="..\..\src\ripple\rpc\handlers\Feature1.cpp">
|
||||||
<Filter>ripple\rpc\handlers</Filter>
|
<Filter>ripple\rpc\handlers</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\FetchInfo.cpp">
|
<ClCompile Include="..\..\src\ripple\rpc\handlers\FetchInfo.cpp">
|
||||||
@@ -4362,6 +4371,9 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\test\jtx\impl\sig.cpp">
|
<ClCompile Include="..\..\src\ripple\test\jtx\impl\sig.cpp">
|
||||||
<Filter>ripple\test\jtx\impl</Filter>
|
<Filter>ripple\test\jtx\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\test\jtx\impl\tag.cpp">
|
||||||
|
<Filter>ripple\test\jtx\impl</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\test\jtx\impl\ticket.cpp">
|
<ClCompile Include="..\..\src\ripple\test\jtx\impl\ticket.cpp">
|
||||||
<Filter>ripple\test\jtx\impl</Filter>
|
<Filter>ripple\test\jtx\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -4425,6 +4437,9 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\test\jtx\sig.h">
|
<ClInclude Include="..\..\src\ripple\test\jtx\sig.h">
|
||||||
<Filter>ripple\test\jtx</Filter>
|
<Filter>ripple\test\jtx</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\test\jtx\tag.h">
|
||||||
|
<Filter>ripple\test\jtx</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\test\jtx\tags.h">
|
<ClInclude Include="..\..\src\ripple\test\jtx\tags.h">
|
||||||
<Filter>ripple\test\jtx</Filter>
|
<Filter>ripple\test\jtx</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|||||||
322
src/ripple/app/tests/SusPay_test.cpp
Normal file
322
src/ripple/app/tests/SusPay_test.cpp
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <BeastConfig.h>
|
||||||
|
#include <ripple/test/jtx.h>
|
||||||
|
#include <ripple/protocol/digest.h>
|
||||||
|
#include <ripple/protocol/Indexes.h>
|
||||||
|
#include <ripple/protocol/JsonFields.h>
|
||||||
|
#include <ripple/protocol/TxFlags.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
struct SusPay_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
template <class... Args>
|
||||||
|
static
|
||||||
|
uint256
|
||||||
|
digest (Args&&... args)
|
||||||
|
{
|
||||||
|
sha256_hasher h;
|
||||||
|
using beast::hash_append;
|
||||||
|
hash_append(h, args...);
|
||||||
|
auto const d = static_cast<
|
||||||
|
sha256_hasher::result_type>(h);
|
||||||
|
uint256 result;
|
||||||
|
std::memcpy(result.data(), d.data(), d.size());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create condition
|
||||||
|
// First is digest, second is pre-image
|
||||||
|
static
|
||||||
|
std::pair<uint256, uint256>
|
||||||
|
cond (std::string const& receipt)
|
||||||
|
{
|
||||||
|
std::pair<uint256, uint256> result;
|
||||||
|
result.second = digest(receipt);
|
||||||
|
result.first = digest(result.second);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Json::Value
|
||||||
|
condpay (jtx::Account const& account, jtx::Account const& to,
|
||||||
|
STAmount const& amount, uint256 const& digest,
|
||||||
|
NetClock::time_point const& expiry)
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = "SuspendedPaymentCreate";
|
||||||
|
jv[jss::Flags] = tfUniversal;
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv[jss::Destination] = to.human();
|
||||||
|
jv[jss::Amount] = amount.getJson(0);
|
||||||
|
jv["CancelAfter"] =
|
||||||
|
expiry.time_since_epoch().count();
|
||||||
|
jv["Digest"] = to_string(digest);
|
||||||
|
return jv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Json::Value
|
||||||
|
lockup (jtx::Account const& account, jtx::Account const& to,
|
||||||
|
STAmount const& amount, NetClock::time_point const& expiry)
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = "SuspendedPaymentCreate";
|
||||||
|
jv[jss::Flags] = tfUniversal;
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv[jss::Destination] = to.human();
|
||||||
|
jv[jss::Amount] = amount.getJson(0);
|
||||||
|
jv["FinishAfter"] =
|
||||||
|
expiry.time_since_epoch().count();
|
||||||
|
return jv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Json::Value
|
||||||
|
finish (jtx::Account const& account,
|
||||||
|
jtx::Account const& from, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = "SuspendedPaymentFinish";
|
||||||
|
jv[jss::Flags] = tfUniversal;
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv["Owner"] = from.human();
|
||||||
|
jv["OfferSequence"] = seq;
|
||||||
|
return jv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Json::Value
|
||||||
|
finish (jtx::Account const& account,
|
||||||
|
jtx::Account const& from, std::uint32_t seq,
|
||||||
|
uint256 const& digest, uint256 const& preimage)
|
||||||
|
{
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = "SuspendedPaymentFinish";
|
||||||
|
jv[jss::Flags] = tfUniversal;
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv["Owner"] = from.human();
|
||||||
|
jv["OfferSequence"] = seq;
|
||||||
|
jv["Method"] = 1;
|
||||||
|
jv["Digest"] = to_string(digest);
|
||||||
|
jv["Proof"] = to_string(preimage);
|
||||||
|
return jv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Json::Value
|
||||||
|
cancel (jtx::Account const& account,
|
||||||
|
jtx::Account const& from, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = "SuspendedPaymentCancel";
|
||||||
|
jv[jss::Flags] = tfUniversal;
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv["Owner"] = from.human();
|
||||||
|
jv["OfferSequence"] = seq;
|
||||||
|
return jv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testEnablement()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using S = seconds;
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
auto const T = [&env](NetClock::duration const& d)
|
||||||
|
{ return env.clock.now() + d; };
|
||||||
|
env.fund(XRP(5000), "alice", "bob");
|
||||||
|
auto const c = cond("receipt");
|
||||||
|
// syntax
|
||||||
|
env(condpay("alice", "bob", XRP(1000), c.first, T(S{1})));
|
||||||
|
env.disable_testing();
|
||||||
|
// disabled in production
|
||||||
|
env(condpay("alice", "bob", XRP(1000), c.first, T(S{1})), ter(temDISABLED));
|
||||||
|
env(finish("bob", "alice", 1), ter(temDISABLED));
|
||||||
|
env(cancel("bob", "alice", 1), ter(temDISABLED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testTags()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using S = seconds;
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
auto const alice = Account("alice");
|
||||||
|
auto const T = [&env](NetClock::duration const& d)
|
||||||
|
{ return env.clock.now() + d; };
|
||||||
|
env.fund(XRP(5000), alice, "bob");
|
||||||
|
auto const c = cond("receipt");
|
||||||
|
auto const seq = env.seq(alice);
|
||||||
|
// set source and dest tags
|
||||||
|
env(condpay(alice, "bob", XRP(1000), c.first, T(S{1})), stag(1), dtag(2));
|
||||||
|
auto const sle = env.le(keylet::susPay(alice.id(), seq));
|
||||||
|
expect((*sle)[sfSourceTag] == 1);
|
||||||
|
expect((*sle)[sfDestinationTag] == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testFails()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using S = seconds;
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
auto const T = [&env](NetClock::duration const& d)
|
||||||
|
{ return env.clock.now() + d; };
|
||||||
|
env.fund(XRP(5000), "alice", "bob");
|
||||||
|
auto const c = cond("receipt");
|
||||||
|
// VFALCO Should we enforce this?
|
||||||
|
// expiration in the past
|
||||||
|
//env(condpay("alice", "bob", XRP(1000), c.first, T(S{-1})), ter(tecNO_PERMISSION));
|
||||||
|
// expiration beyond the limit
|
||||||
|
env(condpay("alice", "bob", XRP(1000), c.first, T(days(7+1))), ter(tecNO_PERMISSION));
|
||||||
|
// no destination account
|
||||||
|
env(condpay("alice", "carol", XRP(1000), c.first, T(S{1})), ter(tecNO_DST));
|
||||||
|
env.fund(XRP(5000), "carol");
|
||||||
|
env(condpay("alice", "carol",
|
||||||
|
XRP(1000), c.first, T(S{1})), stag(2));
|
||||||
|
env(condpay("alice", "carol",
|
||||||
|
XRP(1000), c.first, T(S{1})), stag(3), dtag(4));
|
||||||
|
env(fset("carol", asfRequireDest));
|
||||||
|
// missing destination tag
|
||||||
|
env(condpay("alice", "carol", XRP(1000), c.first, T(S{1})), ter(tecDST_TAG_NEEDED));
|
||||||
|
env(condpay("alice", "carol",
|
||||||
|
XRP(1000), c.first, T(S{1})), dtag(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testLockup()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using S = seconds;
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
auto const T = [&env](NetClock::duration const& d)
|
||||||
|
{ return env.clock.now() + d; };
|
||||||
|
env.fund(XRP(5000), "alice", "bob");
|
||||||
|
auto const seq = env.seq("alice");
|
||||||
|
env(lockup("alice", "alice", XRP(1000), T(S{1})));
|
||||||
|
env.require(balance("alice", XRP(4000) - drops(10)));
|
||||||
|
env(cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
|
||||||
|
env(finish("bob", "alice", seq), ter(tecNO_PERMISSION));
|
||||||
|
env.close();
|
||||||
|
env(cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
|
||||||
|
env(finish("bob", "alice", seq));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testCondPay()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using S = seconds;
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
auto const T = [&env](NetClock::duration const& d)
|
||||||
|
{ return env.clock.now() + d; };
|
||||||
|
env.fund(XRP(5000), "alice", "bob", "carol");
|
||||||
|
auto const c = cond("receipt");
|
||||||
|
auto const seq = env.seq("alice");
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 0);
|
||||||
|
env(condpay("alice", "carol", XRP(1000), c.first, T(S{1})));
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 1);
|
||||||
|
env.require(balance("alice", XRP(4000) - drops(10)));
|
||||||
|
env.require(balance("carol", XRP(5000)));
|
||||||
|
env(cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 1);
|
||||||
|
env(finish("bob", "alice", seq, c.first, c.first), ter(temBAD_SIGNATURE));
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 1);
|
||||||
|
env(finish("bob", "alice", seq, c.first, c.second));
|
||||||
|
// SLE removed on finish
|
||||||
|
expect(! env.le(keylet::susPay(Account("alice").id(), seq)));
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 0);
|
||||||
|
env.require(balance("carol", XRP(6000)));
|
||||||
|
env(cancel("bob", "alice", seq), ter(tecNO_TARGET));
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 0);
|
||||||
|
env(cancel("bob", "carol", 1), ter(tecNO_TARGET));
|
||||||
|
env.close();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
auto const T = [&env](NetClock::duration const& d)
|
||||||
|
{ return env.clock.now() + d; };
|
||||||
|
env.fund(XRP(5000), "alice", "bob", "carol");
|
||||||
|
auto const c = cond("receipt");
|
||||||
|
auto const seq = env.seq("alice");
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 0);
|
||||||
|
env(condpay("alice", "carol", XRP(1000), c.first, T(S{1})));
|
||||||
|
env.close();
|
||||||
|
env.require(balance("alice", XRP(4000) - drops(10)));
|
||||||
|
// balance restored on cancel
|
||||||
|
env(cancel("bob", "alice", seq));
|
||||||
|
env.require(balance("alice", XRP(5000) - drops(10)));
|
||||||
|
// SLE removed on cancel
|
||||||
|
expect(! env.le(keylet::susPay(Account("alice").id(), seq)));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Env env(*this);
|
||||||
|
auto const T = [&env](NetClock::duration const& d)
|
||||||
|
{ return env.clock.now() + d; };
|
||||||
|
env.fund(XRP(5000), "alice", "bob", "carol");
|
||||||
|
env.close();
|
||||||
|
auto const c = cond("receipt");
|
||||||
|
auto const seq = env.seq("alice");
|
||||||
|
env(condpay("alice", "carol", XRP(1000), c.first, T(S{1})));
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 1);
|
||||||
|
// cancel fails before expiration
|
||||||
|
env(cancel("bob", "alice", seq), ter(tecNO_PERMISSION));
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 1);
|
||||||
|
env.close();
|
||||||
|
// finish fails after expiration
|
||||||
|
env(finish("bob", "alice", seq, c.first, c.second), ter(tecNO_PERMISSION));
|
||||||
|
expect((*env.le("alice"))[sfOwnerCount] == 1);
|
||||||
|
env.require(balance("carol", XRP(5000)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
testEnablement();
|
||||||
|
testTags();
|
||||||
|
testFails();
|
||||||
|
testLockup();
|
||||||
|
testCondPay();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(SusPay,app,ripple);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // ripple
|
||||||
410
src/ripple/app/tx/impl/SusPay.cpp
Normal file
410
src/ripple/app/tx/impl/SusPay.cpp
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <BeastConfig.h>
|
||||||
|
#include <ripple/app/tx/impl/SusPay.h>
|
||||||
|
#include <ripple/basics/chrono.h>
|
||||||
|
#include <ripple/basics/Log.h>
|
||||||
|
#include <ripple/protocol/digest.h>
|
||||||
|
#include <ripple/protocol/st.h>
|
||||||
|
#include <ripple/protocol/Feature.h>
|
||||||
|
#include <ripple/protocol/Indexes.h>
|
||||||
|
#include <ripple/protocol/TxFlags.h>
|
||||||
|
#include <ripple/protocol/XRPAmount.h>
|
||||||
|
#include <ripple/ledger/View.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
/*
|
||||||
|
SuspendedPayment
|
||||||
|
|
||||||
|
A suspended payment ("SusPay") sequesters XRP in its
|
||||||
|
own ledger entry until a SusPayFinish or a SusPayCancel
|
||||||
|
transaction mentioning the ledger entry is successfully
|
||||||
|
applied to the ledger. If the SusPayFinish succeeds,
|
||||||
|
the destination account (which must exist) receives the
|
||||||
|
XRP. If the SusPayCancel succeeds, the account which
|
||||||
|
created the SusPay is credited the XRP.
|
||||||
|
|
||||||
|
SusPayCreate
|
||||||
|
|
||||||
|
When the SusPay is created, an optional condition may
|
||||||
|
be attached. The condition is specified by providing
|
||||||
|
the cryptographic digest of the condition to be met.
|
||||||
|
|
||||||
|
At the time of creation, one or both of the fields
|
||||||
|
sfCancelAfter and sfFinishAfter may be provided. If
|
||||||
|
neither field is specified, the transaction is
|
||||||
|
malformed.
|
||||||
|
|
||||||
|
Since the SusPay eventually becomes a payment, an
|
||||||
|
optional DestinationTag and an optional SourceTag
|
||||||
|
is supported in the SusPayCreate transaction.
|
||||||
|
|
||||||
|
Validation rules:
|
||||||
|
|
||||||
|
sfDigest
|
||||||
|
If present, proof is required on a SusPayFinish.
|
||||||
|
|
||||||
|
sfCancelAfter
|
||||||
|
If present, SusPay may be canceled after the
|
||||||
|
specified time (seconds after the Ripple epoch).
|
||||||
|
|
||||||
|
sfFinishAfter
|
||||||
|
If present, must be prior to sfCancelAfter.
|
||||||
|
A SusPayFinish succeeds only in ledgers after
|
||||||
|
sfFinishAfter but before sfCancelAfter.
|
||||||
|
|
||||||
|
If absent, same as parentCloseTime
|
||||||
|
|
||||||
|
Malformed if both sfCancelAfter, sfFinishAfter
|
||||||
|
are absent.
|
||||||
|
|
||||||
|
Malformed if both sfFinishAfter, sfCancelAfter
|
||||||
|
specified and sfCancelAfter <= sfFinishAfter
|
||||||
|
|
||||||
|
SusPayFinish
|
||||||
|
|
||||||
|
Any account may submit a SusPayFinish. If the SusPay
|
||||||
|
ledger entry specifies a condition, the SusPayFinish
|
||||||
|
must provide the sfMethod, original sfDigest, and
|
||||||
|
sfProof fields. Depending on the method, a
|
||||||
|
cryptographic operation will be performed on sfProof
|
||||||
|
and the result must match the sfDigest or else the
|
||||||
|
SusPayFinish is considered as having an invalid
|
||||||
|
signature.
|
||||||
|
|
||||||
|
Only sfMethod==1 is supported, where sfProof must be a
|
||||||
|
256-bit unsigned big-endian integer which when hashed
|
||||||
|
using SHA256 produces digest == sfDigest.
|
||||||
|
|
||||||
|
If the SusPay ledger entry specifies sfFinishAfter, the
|
||||||
|
transaction will fail if parentCloseTime <= sfFinishAfter.
|
||||||
|
|
||||||
|
SusPayFinish transactions must be submitted before a
|
||||||
|
SusPay's sfCancelAfter if present.
|
||||||
|
|
||||||
|
If the SusPay ledger entry specifies sfCancelAfter, the
|
||||||
|
transaction will fail if sfCancelAfter <= parentCloseTime.
|
||||||
|
|
||||||
|
NOTE: It must always be possible to verify the condition
|
||||||
|
without retrieving the SusPay ledger entry.
|
||||||
|
|
||||||
|
SusPayCancel
|
||||||
|
|
||||||
|
Any account may submit a SusPayCancel transaction.
|
||||||
|
|
||||||
|
If the SusPay ledger entry does not specify a
|
||||||
|
sfCancelAfter, the cancel transaction will fail.
|
||||||
|
|
||||||
|
If parentCloseTime <= sfCancelAfter, the transaction
|
||||||
|
will fail.
|
||||||
|
|
||||||
|
When a SusPay is canceled, the funds are returned to
|
||||||
|
the source account.
|
||||||
|
|
||||||
|
By careful selection of fields in each transaction,
|
||||||
|
these operations may be achieved:
|
||||||
|
|
||||||
|
* Lock up XRP for a time period
|
||||||
|
* Execute a payment conditionally
|
||||||
|
*/
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TER
|
||||||
|
SusPayCreate::preflight (PreflightContext const& ctx)
|
||||||
|
{
|
||||||
|
if (! (ctx.flags & tapENABLE_TESTING) &&
|
||||||
|
! ctx.rules.enabled(featureSusPay,
|
||||||
|
ctx.config.features))
|
||||||
|
return temDISABLED;
|
||||||
|
|
||||||
|
if (! isXRP(ctx.tx[sfAmount]))
|
||||||
|
return temBAD_AMOUNT;
|
||||||
|
|
||||||
|
if (ctx.tx[sfAmount] <= beast::zero)
|
||||||
|
return temBAD_AMOUNT;
|
||||||
|
|
||||||
|
if (! ctx.tx[~sfCancelAfter] &&
|
||||||
|
! ctx.tx[~sfFinishAfter])
|
||||||
|
return temBAD_EXPIRATION;
|
||||||
|
|
||||||
|
if (ctx.tx[~sfCancelAfter] && ctx.tx[~sfFinishAfter] &&
|
||||||
|
ctx.tx[sfCancelAfter] <= ctx.tx[sfFinishAfter])
|
||||||
|
return temBAD_EXPIRATION;
|
||||||
|
|
||||||
|
return Transactor::preflight(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
SusPayCreate::doApply()
|
||||||
|
{
|
||||||
|
// For now, require that all operations can be
|
||||||
|
// canceled, or finished without proof, within a
|
||||||
|
// reasonable period of time for the first release.
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto const maxExpire =
|
||||||
|
ctx_.view().info().parentCloseTime +
|
||||||
|
seconds{days(7)}.count();
|
||||||
|
if (ctx_.tx[~sfDigest])
|
||||||
|
{
|
||||||
|
if (! ctx_.tx[~sfCancelAfter] ||
|
||||||
|
maxExpire <= ctx_.tx[sfCancelAfter])
|
||||||
|
return tecNO_PERMISSION;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ctx_.tx[~sfCancelAfter] &&
|
||||||
|
maxExpire <= ctx_.tx[sfCancelAfter])
|
||||||
|
return tecNO_PERMISSION;
|
||||||
|
if (ctx_.tx[~sfFinishAfter] &&
|
||||||
|
maxExpire <= ctx_.tx[sfFinishAfter])
|
||||||
|
return tecNO_PERMISSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
XRPAmount const amount =
|
||||||
|
STAmount(ctx_.tx[sfAmount]).mantissa();
|
||||||
|
|
||||||
|
auto const account = ctx_.tx[sfAccount];
|
||||||
|
|
||||||
|
auto const sle = ctx_.view().peek(
|
||||||
|
keylet::account(account));
|
||||||
|
|
||||||
|
if (XRPAmount(STAmount((*sle)[sfBalance]).mantissa()) <
|
||||||
|
XRPAmount(ctx_.view().fees().accountReserve(
|
||||||
|
(*sle)[sfOwnerCount] + 1)))
|
||||||
|
return tecINSUFFICIENT_RESERVE;
|
||||||
|
|
||||||
|
if (XRPAmount(STAmount((*sle)[sfBalance]).mantissa()) <
|
||||||
|
amount + XRPAmount(ctx_.view().fees().accountReserve(
|
||||||
|
(*sle)[sfOwnerCount] + 1)))
|
||||||
|
return tecUNFUNDED;
|
||||||
|
|
||||||
|
// Check destination account
|
||||||
|
{
|
||||||
|
auto const sled = ctx_.view().read(
|
||||||
|
keylet::account(ctx_.tx[sfDestination]));
|
||||||
|
if (! sled)
|
||||||
|
return tecNO_DST;
|
||||||
|
if (((*sled)[sfFlags] & lsfRequireDestTag) &&
|
||||||
|
! ctx_.tx[~sfDestinationTag])
|
||||||
|
return tecDST_TAG_NEEDED;
|
||||||
|
if ((*sled)[sfFlags] & lsfDisallowXRP)
|
||||||
|
return tecNO_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create SusPay in ledger
|
||||||
|
auto const slep = std::make_shared<SLE>(
|
||||||
|
keylet::susPay(account, (*sle)[sfSequence] - 1));
|
||||||
|
(*slep)[sfAmount] = ctx_.tx[sfAmount];
|
||||||
|
(*slep)[sfAccount] = account;
|
||||||
|
(*slep)[~sfDigest] = ctx_.tx[~sfDigest];
|
||||||
|
(*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
|
||||||
|
(*slep)[sfDestination] = ctx_.tx[sfDestination];
|
||||||
|
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
|
||||||
|
(*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
|
||||||
|
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
|
||||||
|
|
||||||
|
ctx_.view().insert(slep);
|
||||||
|
|
||||||
|
// Add SusPay to owner directory
|
||||||
|
{
|
||||||
|
uint64_t page;
|
||||||
|
TER ter = dirAdd(ctx_.view(), page,
|
||||||
|
keylet::ownerDir(account).key,
|
||||||
|
slep->key(), describeOwnerDir(account));
|
||||||
|
if (! isTesSuccess(ter))
|
||||||
|
return ter;
|
||||||
|
(*slep)[sfOwnerNode] = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduct owner's balance, increment owner count
|
||||||
|
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||||
|
(*sle)[sfOwnerCount] = (*sle)[sfOwnerCount] + 1;
|
||||||
|
ctx_.view().update(sle);
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TER
|
||||||
|
SusPayFinish::preflight (PreflightContext const& ctx)
|
||||||
|
{
|
||||||
|
if (! (ctx.flags & tapENABLE_TESTING) &&
|
||||||
|
! ctx.rules.enabled(featureSusPay,
|
||||||
|
ctx.config.features))
|
||||||
|
return temDISABLED;
|
||||||
|
|
||||||
|
if (ctx.tx[~sfMethod])
|
||||||
|
{
|
||||||
|
// Condition
|
||||||
|
switch(ctx.tx[sfMethod])
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
if (! ctx.tx[~sfDigest])
|
||||||
|
return temMALFORMED;
|
||||||
|
if (! ctx.tx[~sfProof])
|
||||||
|
return temMALFORMED;
|
||||||
|
sha256_hasher h;
|
||||||
|
using beast::hash_append;
|
||||||
|
hash_append(h, ctx.tx[sfProof]);
|
||||||
|
uint256 digest;
|
||||||
|
{
|
||||||
|
auto const result = static_cast<
|
||||||
|
sha256_hasher::result_type>(h);
|
||||||
|
std::memcpy(digest.data(),
|
||||||
|
result.data(), result.size());
|
||||||
|
}
|
||||||
|
if (digest != ctx.tx[sfDigest])
|
||||||
|
return temBAD_SIGNATURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No Condition
|
||||||
|
if (ctx.tx[~sfDigest])
|
||||||
|
return temMALFORMED;
|
||||||
|
if (ctx.tx[~sfProof])
|
||||||
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Transactor::preflight(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
SusPayFinish::doApply()
|
||||||
|
{
|
||||||
|
// peek SusPay SLE
|
||||||
|
auto const k = keylet::susPay(
|
||||||
|
ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
|
||||||
|
auto const slep = ctx_.view().peek(k);
|
||||||
|
if (! slep)
|
||||||
|
return tecNO_TARGET;
|
||||||
|
|
||||||
|
// Too soon?
|
||||||
|
if ((*slep)[~sfFinishAfter] &&
|
||||||
|
ctx_.view().info().parentCloseTime <=
|
||||||
|
(*slep)[sfFinishAfter])
|
||||||
|
return tecNO_PERMISSION;
|
||||||
|
|
||||||
|
// Too late?
|
||||||
|
if ((*slep)[~sfCancelAfter] &&
|
||||||
|
(*slep)[sfCancelAfter] <=
|
||||||
|
ctx_.view().info().parentCloseTime)
|
||||||
|
return tecNO_PERMISSION;
|
||||||
|
|
||||||
|
AccountID const account = (*slep)[sfAccount];
|
||||||
|
|
||||||
|
// Remove SusPay from owner directory
|
||||||
|
{
|
||||||
|
auto const page = (*slep)[sfOwnerNode];
|
||||||
|
TER const ter = dirDelete(ctx_.view(), true,
|
||||||
|
page, keylet::ownerDir(account).key,
|
||||||
|
k.key, false, page == 0);
|
||||||
|
if (! isTesSuccess(ter))
|
||||||
|
return ter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: These payments cannot be used to fund accounts
|
||||||
|
|
||||||
|
// Fetch Destination SLE
|
||||||
|
auto const sled = ctx_.view().peek(
|
||||||
|
keylet::account((*slep)[sfDestination]));
|
||||||
|
if (! sled)
|
||||||
|
return tecNO_DST;
|
||||||
|
|
||||||
|
// Transfer amount to destination
|
||||||
|
(*sled)[sfBalance] = (*sled)[sfBalance] + (*slep)[sfAmount];
|
||||||
|
ctx_.view().update(sled);
|
||||||
|
|
||||||
|
// Adjust source owner count
|
||||||
|
auto const sle = ctx_.view().peek(
|
||||||
|
keylet::account(account));
|
||||||
|
(*sle)[sfOwnerCount] = (*sle)[sfOwnerCount] - 1;
|
||||||
|
ctx_.view().update(sle);
|
||||||
|
|
||||||
|
// Remove SusPay from ledger
|
||||||
|
ctx_.view().erase(slep);
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TER
|
||||||
|
SusPayCancel::preflight (PreflightContext const& ctx)
|
||||||
|
{
|
||||||
|
if (! (ctx.flags & tapENABLE_TESTING) &&
|
||||||
|
! ctx.rules.enabled(featureSusPay,
|
||||||
|
ctx.config.features))
|
||||||
|
return temDISABLED;
|
||||||
|
|
||||||
|
return Transactor::preflight(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
SusPayCancel::doApply()
|
||||||
|
{
|
||||||
|
// peek SusPay SLE
|
||||||
|
auto const k = keylet::susPay(
|
||||||
|
ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
|
||||||
|
auto const slep = ctx_.view().peek(k);
|
||||||
|
if (! slep)
|
||||||
|
return tecNO_TARGET;
|
||||||
|
|
||||||
|
// Too soon?
|
||||||
|
if (! (*slep)[~sfCancelAfter] ||
|
||||||
|
ctx_.view().info().parentCloseTime <=
|
||||||
|
(*slep)[sfCancelAfter])
|
||||||
|
return tecNO_PERMISSION;
|
||||||
|
|
||||||
|
AccountID const account = (*slep)[sfAccount];
|
||||||
|
|
||||||
|
// Remove SusPay from owner directory
|
||||||
|
{
|
||||||
|
auto const page = (*slep)[sfOwnerNode];
|
||||||
|
TER const ter = dirDelete(ctx_.view(), true,
|
||||||
|
page, keylet::ownerDir(account).key,
|
||||||
|
k.key, false, page == 0);
|
||||||
|
if (! isTesSuccess(ter))
|
||||||
|
return ter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer amount back to owner, decrement owner count
|
||||||
|
auto const sle = ctx_.view().peek(
|
||||||
|
keylet::account(account));
|
||||||
|
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount];
|
||||||
|
(*sle)[sfOwnerCount] = (*sle)[sfOwnerCount] - 1;
|
||||||
|
ctx_.view().update(sle);
|
||||||
|
|
||||||
|
// Remove SusPay from ledger
|
||||||
|
ctx_.view().erase(slep);
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // ripple
|
||||||
|
|
||||||
87
src/ripple/app/tx/impl/SusPay.h
Normal file
87
src/ripple/app/tx/impl/SusPay.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef RIPPLE_TX_SUSPAY_H_INCLUDED
|
||||||
|
#define RIPPLE_TX_SUSPAY_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/app/tx/impl/Transactor.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
class SusPayCreate
|
||||||
|
: public Transactor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
SusPayCreate (ApplyContext& ctx)
|
||||||
|
: Transactor(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
TER
|
||||||
|
preflight (PreflightContext const& ctx);
|
||||||
|
|
||||||
|
TER
|
||||||
|
doApply() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class SusPayFinish
|
||||||
|
: public Transactor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
SusPayFinish (ApplyContext& ctx)
|
||||||
|
: Transactor(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
TER
|
||||||
|
preflight (PreflightContext const& ctx);
|
||||||
|
|
||||||
|
TER
|
||||||
|
doApply() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class SusPayCancel
|
||||||
|
: public Transactor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
SusPayCancel (ApplyContext& ctx)
|
||||||
|
: Transactor(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
TER
|
||||||
|
preflight (PreflightContext const& ctx);
|
||||||
|
|
||||||
|
TER
|
||||||
|
doApply() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
#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>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -43,6 +44,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 ttSUSPAY_FINISH: return SusPayFinish ::preflight(ctx);
|
||||||
|
case ttSUSPAY_CANCEL: return SusPayCancel ::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);
|
||||||
@@ -65,6 +69,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 ttSUSPAY_FINISH: { SusPayFinish p(ctx); return p(); }
|
||||||
|
case ttSUSPAY_CANCEL: { SusPayCancel 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(); }
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ uint256
|
|||||||
feature (const char* name);
|
feature (const char* name);
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
extern uint256 const featureSusPay;
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <ripple/protocol/Keylet.h>
|
#include <ripple/protocol/Keylet.h>
|
||||||
#include <ripple/protocol/LedgerFormats.h>
|
#include <ripple/protocol/LedgerFormats.h>
|
||||||
#include <ripple/protocol/Protocol.h>
|
#include <ripple/protocol/Protocol.h>
|
||||||
|
#include <ripple/protocol/PublicKey.h>
|
||||||
#include <ripple/protocol/RippleAddress.h>
|
#include <ripple/protocol/RippleAddress.h>
|
||||||
#include <ripple/protocol/Serializer.h>
|
#include <ripple/protocol/Serializer.h>
|
||||||
#include <ripple/protocol/UintTypes.h>
|
#include <ripple/protocol/UintTypes.h>
|
||||||
@@ -234,6 +235,10 @@ Keylet page (uint256 const& key)
|
|||||||
return { ltDIR_NODE, key };
|
return { ltDIR_NODE, key };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A SuspendedPayment */
|
||||||
|
Keylet
|
||||||
|
susPay (AccountID const& source, std::uint32_t seq);
|
||||||
|
|
||||||
} // keylet
|
} // keylet
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,15 +64,12 @@ enum LedgerEntryType
|
|||||||
*/
|
*/
|
||||||
ltDIR_NODE = 'd',
|
ltDIR_NODE = 'd',
|
||||||
|
|
||||||
/** Describes a trust line.
|
|
||||||
*/
|
|
||||||
ltRIPPLE_STATE = 'r',
|
ltRIPPLE_STATE = 'r',
|
||||||
|
|
||||||
ltTICKET = 'T',
|
ltTICKET = 'T',
|
||||||
|
|
||||||
ltSIGNER_LIST = 'S',
|
ltSIGNER_LIST = 'S',
|
||||||
|
|
||||||
/* Deprecated. */
|
|
||||||
ltOFFER = 'o',
|
ltOFFER = 'o',
|
||||||
|
|
||||||
ltLEDGER_HASHES = 'h',
|
ltLEDGER_HASHES = 'h',
|
||||||
@@ -81,6 +78,8 @@ enum LedgerEntryType
|
|||||||
|
|
||||||
ltFEE_SETTINGS = 's',
|
ltFEE_SETTINGS = 's',
|
||||||
|
|
||||||
|
ltSUSPAY = 'u',
|
||||||
|
|
||||||
// No longer used or supported. Left here to prevent accidental
|
// No longer used or supported. Left here to prevent accidental
|
||||||
// reassignment of the ledger type.
|
// reassignment of the ledger type.
|
||||||
ltNICKNAME = 'n',
|
ltNICKNAME = 'n',
|
||||||
@@ -103,6 +102,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',
|
||||||
spaceAmendment = 'f',
|
spaceAmendment = 'f',
|
||||||
spaceFee = 'e',
|
spaceFee = 'e',
|
||||||
spaceTicket = 'T',
|
spaceTicket = 'T',
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ extern SField const sfMetadata;
|
|||||||
|
|
||||||
// 8-bit integers
|
// 8-bit integers
|
||||||
extern SF_U8 const sfCloseResolution;
|
extern SF_U8 const sfCloseResolution;
|
||||||
extern SF_U8 const sfTemplateEntryType;
|
extern SF_U8 const sfMethod;
|
||||||
extern SF_U8 const sfTransactionResult;
|
extern SF_U8 const sfTransactionResult;
|
||||||
|
|
||||||
// 16-bit integers
|
// 16-bit integers
|
||||||
@@ -388,6 +388,8 @@ extern SF_U32 const sfReserveIncrement;
|
|||||||
extern SF_U32 const sfSetFlag;
|
extern SF_U32 const sfSetFlag;
|
||||||
extern SF_U32 const sfClearFlag;
|
extern SF_U32 const sfClearFlag;
|
||||||
extern SF_U32 const sfSignerQuorum;
|
extern SF_U32 const sfSignerQuorum;
|
||||||
|
extern SF_U32 const sfCancelAfter;
|
||||||
|
extern SF_U32 const sfFinishAfter;
|
||||||
|
|
||||||
// 64-bit integers
|
// 64-bit integers
|
||||||
extern SF_U64 const sfIndexNext;
|
extern SF_U64 const sfIndexNext;
|
||||||
@@ -461,7 +463,7 @@ extern SF_Blob const sfMemoFormat;
|
|||||||
|
|
||||||
// variable length (uncommon)
|
// variable length (uncommon)
|
||||||
extern SF_Blob const sfMultiSignature;
|
extern SF_Blob const sfMultiSignature;
|
||||||
extern SF_Blob const sfInnerSig;
|
extern SF_Blob const sfProof;
|
||||||
|
|
||||||
// account
|
// account
|
||||||
extern SF_Account const sfAccount;
|
extern SF_Account const sfAccount;
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ enum TxType
|
|||||||
ttINVALID = -1,
|
ttINVALID = -1,
|
||||||
|
|
||||||
ttPAYMENT = 0,
|
ttPAYMENT = 0,
|
||||||
ttCLAIM = 1, // open
|
ttSUSPAY_CREATE = 1,
|
||||||
ttWALLET_ADD = 2, // unused
|
ttSUSPAY_FINISH = 2,
|
||||||
ttACCOUNT_SET = 3,
|
ttACCOUNT_SET = 3,
|
||||||
ttPASSWORD_FUND = 4, // open
|
ttSUSPAY_CANCEL = 4,
|
||||||
ttREGULAR_KEY_SET = 5,
|
ttREGULAR_KEY_SET = 5,
|
||||||
ttNICKNAME_SET = 6, // open
|
ttNICKNAME_SET = 6, // open
|
||||||
ttOFFER_CREATE = 7,
|
ttOFFER_CREATE = 7,
|
||||||
|
|||||||
@@ -45,4 +45,6 @@ feature (const char* name)
|
|||||||
return feature(name, std::strlen(name));
|
return feature(name, std::strlen(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint256 const featureSusPay = feature("SusPay");
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -308,6 +308,17 @@ Keylet page(Keylet const& root,
|
|||||||
return page(root.key, index);
|
return page(root.key, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keylet
|
||||||
|
susPay (AccountID const& source, std::uint32_t seq)
|
||||||
|
{
|
||||||
|
sha512_half_hasher h;
|
||||||
|
using beast::hash_append;
|
||||||
|
hash_append(h, spaceSusPay);
|
||||||
|
hash_append(h, source);
|
||||||
|
hash_append(h, seq);
|
||||||
|
return { ltSUSPAY, static_cast<uint256>(h) };
|
||||||
|
}
|
||||||
|
|
||||||
} // keylet
|
} // keylet
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -81,6 +81,19 @@ LedgerFormats::LedgerFormats ()
|
|||||||
<< SOElement (sfHighQualityOut, SOE_OPTIONAL)
|
<< SOElement (sfHighQualityOut, SOE_OPTIONAL)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
add ("SusPay", ltSUSPAY) <<
|
||||||
|
SOElement (sfAccount, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfDestination, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfAmount, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfDigest, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfCancelAfter, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfFinishAfter, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfSourceTag, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfDestinationTag, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfOwnerNode, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfPreviousTxnID, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfPreviousTxnLgrSeq, SOE_REQUIRED);
|
||||||
|
|
||||||
add ("LedgerHashes", ltLEDGER_HASHES)
|
add ("LedgerHashes", ltLEDGER_HASHES)
|
||||||
<< SOElement (sfFirstLedgerSequence, SOE_OPTIONAL) // Remove if we do a ledger restart
|
<< SOElement (sfFirstLedgerSequence, SOE_OPTIONAL) // Remove if we do a ledger restart
|
||||||
<< SOElement (sfLastLedgerSequence, SOE_OPTIONAL)
|
<< SOElement (sfLastLedgerSequence, SOE_OPTIONAL)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ SField const sfIndex = make::one(&sfIndex, STI_HASH256, 258, "in
|
|||||||
|
|
||||||
// 8-bit integers
|
// 8-bit integers
|
||||||
SF_U8 const sfCloseResolution = make::one<SF_U8::type>(&sfCloseResolution, STI_UINT8, 1, "CloseResolution");
|
SF_U8 const sfCloseResolution = make::one<SF_U8::type>(&sfCloseResolution, STI_UINT8, 1, "CloseResolution");
|
||||||
SF_U8 const sfTemplateEntryType = make::one<SF_U8::type>(&sfTemplateEntryType, STI_UINT8, 2, "TemplateEntryType");
|
SF_U8 const sfMethod = make::one<SF_U8::type>(&sfMethod, STI_UINT8, 2, "Method");
|
||||||
SF_U8 const sfTransactionResult = make::one<SF_U8::type>(&sfTransactionResult, STI_UINT8, 3, "TransactionResult");
|
SF_U8 const sfTransactionResult = make::one<SF_U8::type>(&sfTransactionResult, STI_UINT8, 3, "TransactionResult");
|
||||||
|
|
||||||
// 16-bit integers
|
// 16-bit integers
|
||||||
@@ -121,6 +121,8 @@ SF_U32 const sfReserveIncrement = make::one<SF_U32::type>(&sfReserveIncrement
|
|||||||
SF_U32 const sfSetFlag = make::one<SF_U32::type>(&sfSetFlag, STI_UINT32, 33, "SetFlag");
|
SF_U32 const sfSetFlag = make::one<SF_U32::type>(&sfSetFlag, STI_UINT32, 33, "SetFlag");
|
||||||
SF_U32 const sfClearFlag = make::one<SF_U32::type>(&sfClearFlag, STI_UINT32, 34, "ClearFlag");
|
SF_U32 const sfClearFlag = make::one<SF_U32::type>(&sfClearFlag, STI_UINT32, 34, "ClearFlag");
|
||||||
SF_U32 const sfSignerQuorum = make::one<SF_U32::type>(&sfSignerQuorum, STI_UINT32, 35, "SignerQuorum");
|
SF_U32 const sfSignerQuorum = make::one<SF_U32::type>(&sfSignerQuorum, STI_UINT32, 35, "SignerQuorum");
|
||||||
|
SF_U32 const sfCancelAfter = make::one<SF_U32::type>(&sfCancelAfter, STI_UINT32, 36, "CancelAfter");
|
||||||
|
SF_U32 const sfFinishAfter = make::one<SF_U32::type>(&sfFinishAfter, STI_UINT32, 37, "FinishAfter");
|
||||||
|
|
||||||
// 64-bit integers
|
// 64-bit integers
|
||||||
SF_U64 const sfIndexNext = make::one<SF_U64::type>(&sfIndexNext, STI_UINT64, 1, "IndexNext");
|
SF_U64 const sfIndexNext = make::one<SF_U64::type>(&sfIndexNext, STI_UINT64, 1, "IndexNext");
|
||||||
@@ -194,7 +196,7 @@ SF_Blob const sfMemoFormat = make::one<SF_Blob::type>(&sfMemoFormat, STI
|
|||||||
|
|
||||||
// variable length (uncommon)
|
// variable length (uncommon)
|
||||||
SF_Blob const sfMultiSignature = make::one<SF_Blob::type>(&sfMultiSignature, STI_VL, 16, "MultiSignature");
|
SF_Blob const sfMultiSignature = make::one<SF_Blob::type>(&sfMultiSignature, STI_VL, 16, "MultiSignature");
|
||||||
SF_Blob const sfInnerSig = make::one<SF_Blob::type>(&sfInnerSig, STI_VL, 17, "InnerSig");
|
SF_Blob const sfProof = make::one<SF_Blob::type>(&sfProof, STI_VL, 17, "Proof");
|
||||||
|
|
||||||
// account
|
// account
|
||||||
SF_Account const sfAccount = make::one<SF_Account::type>(&sfAccount, STI_ACCOUNT, 1, "Account");
|
SF_Account const sfAccount = make::one<SF_Account::type>(&sfAccount, STI_ACCOUNT, 1, "Account");
|
||||||
|
|||||||
@@ -67,6 +67,25 @@ TxFormats::TxFormats ()
|
|||||||
<< SOElement (sfDeliverMin, SOE_OPTIONAL)
|
<< SOElement (sfDeliverMin, SOE_OPTIONAL)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
add ("SuspendedPaymentCreate", ttSUSPAY_CREATE) <<
|
||||||
|
SOElement (sfDestination, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfAmount, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfDigest, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfCancelAfter, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfFinishAfter, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfDestinationTag, SOE_OPTIONAL);
|
||||||
|
|
||||||
|
add ("SuspendedPaymentFinish", ttSUSPAY_FINISH) <<
|
||||||
|
SOElement (sfOwner, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfOfferSequence, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfMethod, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfDigest, SOE_OPTIONAL) <<
|
||||||
|
SOElement (sfProof, SOE_OPTIONAL);
|
||||||
|
|
||||||
|
add ("SuspendedPaymentCancel", ttSUSPAY_CANCEL) <<
|
||||||
|
SOElement (sfOwner, SOE_REQUIRED) <<
|
||||||
|
SOElement (sfOfferSequence, SOE_REQUIRED);
|
||||||
|
|
||||||
add ("EnableAmendment", ttAMENDMENT)
|
add ("EnableAmendment", ttAMENDMENT)
|
||||||
<< SOElement (sfLedgerSequence, SOE_OPTIONAL)
|
<< SOElement (sfLedgerSequence, SOE_OPTIONAL)
|
||||||
<< SOElement (sfAmendment, SOE_REQUIRED)
|
<< SOElement (sfAmendment, SOE_REQUIRED)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
// Convenience header that includes everything
|
// Convenience header that includes everything
|
||||||
|
|
||||||
|
#include <ripple/json/to_string.h>
|
||||||
#include <ripple/test/jtx/Account.h>
|
#include <ripple/test/jtx/Account.h>
|
||||||
#include <ripple/test/jtx/amount.h>
|
#include <ripple/test/jtx/amount.h>
|
||||||
#include <ripple/test/jtx/balance.h>
|
#include <ripple/test/jtx/balance.h>
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
#include <ripple/test/jtx/sendmax.h>
|
#include <ripple/test/jtx/sendmax.h>
|
||||||
#include <ripple/test/jtx/seq.h>
|
#include <ripple/test/jtx/seq.h>
|
||||||
#include <ripple/test/jtx/sig.h>
|
#include <ripple/test/jtx/sig.h>
|
||||||
|
#include <ripple/test/jtx/tag.h>
|
||||||
#include <ripple/test/jtx/tags.h>
|
#include <ripple/test/jtx/tags.h>
|
||||||
#include <ripple/test/jtx/ter.h>
|
#include <ripple/test/jtx/ter.h>
|
||||||
#include <ripple/test/jtx/ticket.h>
|
#include <ripple/test/jtx/ticket.h>
|
||||||
|
|||||||
42
src/ripple/test/jtx/impl/tag.cpp
Normal file
42
src/ripple/test/jtx/impl/tag.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <BeastConfig.h>
|
||||||
|
#include <ripple/test/jtx/tag.h>
|
||||||
|
#include <ripple/protocol/JsonFields.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
namespace jtx {
|
||||||
|
|
||||||
|
void
|
||||||
|
dtag::operator()(Env const&, JTx& jt) const
|
||||||
|
{
|
||||||
|
jt.jv["DestinationTag"] = value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
stag::operator()(Env const&, JTx& jt) const
|
||||||
|
{
|
||||||
|
jt.jv["SourceTag"] = value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // jtx
|
||||||
|
} // test
|
||||||
|
} // ripple
|
||||||
@@ -31,8 +31,7 @@ namespace jtx {
|
|||||||
/** Create a payment. */
|
/** Create a payment. */
|
||||||
Json::Value
|
Json::Value
|
||||||
pay (Account const& account,
|
pay (Account const& account,
|
||||||
Account const& to,
|
Account const& to, AnyAmount amount);
|
||||||
AnyAmount amount);
|
|
||||||
|
|
||||||
} // jtx
|
} // jtx
|
||||||
} // test
|
} // test
|
||||||
|
|||||||
69
src/ripple/test/jtx/tag.h
Normal file
69
src/ripple/test/jtx/tag.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef RIPPLE_TEST_JTX_TAG_H_INCLUDED
|
||||||
|
#define RIPPLE_TEST_JTX_TAG_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/test/jtx/Env.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
namespace jtx {
|
||||||
|
|
||||||
|
/** Set the destination tag on a JTx*/
|
||||||
|
struct dtag
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::uint32_t value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
dtag (std::uint32_t value)
|
||||||
|
: value_ (value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(Env const&, JTx& jt) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Set the source tag on a JTx*/
|
||||||
|
struct stag
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::uint32_t value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
stag (std::uint32_t value)
|
||||||
|
: value_ (value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(Env const&, JTx& jt) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // jtx
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -19,4 +19,5 @@
|
|||||||
|
|
||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
|
|
||||||
|
#include <ripple/app/tests/SusPay_test.cpp>
|
||||||
#include <ripple/app/tests/Regression_test.cpp>
|
#include <ripple/app/tests/Regression_test.cpp>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#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/TransactionMaster.cpp>
|
#include <ripple/app/tx/impl/TransactionMaster.cpp>
|
||||||
#include <ripple/app/tx/impl/Transaction.cpp>
|
#include <ripple/app/tx/impl/Transaction.cpp>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
#include <ripple/rpc/handlers/CanDelete.cpp>
|
#include <ripple/rpc/handlers/CanDelete.cpp>
|
||||||
#include <ripple/rpc/handlers/Connect.cpp>
|
#include <ripple/rpc/handlers/Connect.cpp>
|
||||||
#include <ripple/rpc/handlers/ConsensusInfo.cpp>
|
#include <ripple/rpc/handlers/ConsensusInfo.cpp>
|
||||||
#include <ripple/rpc/handlers/Feature.cpp>
|
#include <ripple/rpc/handlers/Feature1.cpp>
|
||||||
#include <ripple/rpc/handlers/FetchInfo.cpp>
|
#include <ripple/rpc/handlers/FetchInfo.cpp>
|
||||||
#include <ripple/rpc/handlers/GatewayBalances.cpp>
|
#include <ripple/rpc/handlers/GatewayBalances.cpp>
|
||||||
#include <ripple/rpc/handlers/GetCounts.cpp>
|
#include <ripple/rpc/handlers/GetCounts.cpp>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include <ripple/test/jtx/impl/sendmax.cpp>
|
#include <ripple/test/jtx/impl/sendmax.cpp>
|
||||||
#include <ripple/test/jtx/impl/seq.cpp>
|
#include <ripple/test/jtx/impl/seq.cpp>
|
||||||
#include <ripple/test/jtx/impl/sig.cpp>
|
#include <ripple/test/jtx/impl/sig.cpp>
|
||||||
|
#include <ripple/test/jtx/impl/tag.cpp>
|
||||||
#include <ripple/test/jtx/impl/ticket.cpp>
|
#include <ripple/test/jtx/impl/ticket.cpp>
|
||||||
#include <ripple/test/jtx/impl/trust.cpp>
|
#include <ripple/test/jtx/impl/trust.cpp>
|
||||||
#include <ripple/test/jtx/impl/txflags.cpp>
|
#include <ripple/test/jtx/impl/txflags.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user