diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj index c2acc96da..68a039a45 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj +++ b/Builds/VisualStudio2015/RippleD.vcxproj @@ -3377,6 +3377,10 @@ True True + + True + True + True True diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters index cb10cbe3e..a51dcbd59 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters @@ -3837,6 +3837,9 @@ ripple\rpc\tests + + ripple\rpc\tests + ripple\rpc\tests diff --git a/src/ripple/rpc/tests/AccountSet.test.cpp b/src/ripple/rpc/tests/AccountSet.test.cpp new file mode 100644 index 000000000..5dc9b4a73 --- /dev/null +++ b/src/ripple/rpc/tests/AccountSet.test.cpp @@ -0,0 +1,317 @@ +//------------------------------------------------------------------------------ +/* + 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 +#include +#include +#include + +namespace ripple { + +class AccountSet_test : public beast::unit_test::suite +{ +public: + + void testNullAccountSet() + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), noripple(alice)); + //ask for the ledger entry - account root, to check its flags + auto const jrr = env.le(alice); + BEAST_EXPECT((*env.le(alice))[ sfFlags ] == 0u); + } + + void testSetAndReset(unsigned int flag_val, std::string const& label) + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), noripple(alice)); + env.memoize("eric"); + env(regkey(alice, "eric")); + + unsigned int orig_flags = (*env.le(alice))[ sfFlags ]; + + env.require(nflags(alice, flag_val)); + env(fset(alice, flag_val), sig(alice)); + env.require(flags(alice, flag_val)); + env(fclear(alice, flag_val)); + env.require(nflags(alice, flag_val)); + uint32 now_flags = (*env.le(alice))[ sfFlags ]; + BEAST_EXPECT(now_flags == orig_flags); + } + + void testSetAndResetAccountTxnID() + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), noripple(alice)); + + unsigned int orig_flags = (*env.le(alice))[ sfFlags ]; + + // asfAccountTxnID is special and not actually set as a flag, + // so we check the field presence instead + BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfAccountTxnID)); + env(fset(alice, asfAccountTxnID), sig(alice)); + BEAST_EXPECT(env.le(alice)->isFieldPresent(sfAccountTxnID)); + env(fclear(alice, asfAccountTxnID)); + BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfAccountTxnID)); + uint32 now_flags = (*env.le(alice))[ sfFlags ]; + BEAST_EXPECT(now_flags == orig_flags); + } + + void testSetNoFreeze() + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), noripple(alice)); + env.memoize("eric"); + env(regkey(alice, "eric")); + + env.require(nflags(alice, asfNoFreeze)); + env(fset(alice, asfNoFreeze), sig("eric"), ter(tecNEED_MASTER_KEY)); + env(fset(alice, asfNoFreeze), sig(alice)); + env.require(flags(alice, asfNoFreeze)); + env(fclear(alice, asfNoFreeze), sig(alice)); + // verify flag is still set (clear does not clear in this case) + env.require(flags(alice, asfNoFreeze)); + } + + void testDomain() + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), alice); + auto jt = noop(alice); + // The Domain field is represented as the hex string of the lowercase + // ASCII of the domain. For example, the domain example.com would be + // represented as "6578616d706c652e636f6d". + // + // To remove the Domain field from an account, send an AccountSet with + // the Domain set to an empty string. + std::string const domain = "example.com"; + jt[sfDomain.fieldName] = strHex(domain); + env(jt); + BEAST_EXPECT((*env.le(alice))[ sfDomain ] == makeSlice(domain)); + + jt[sfDomain.fieldName] = ""; + env(jt); + BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfDomain)); + + // The upper limit on the length is 256 bytes + // (defined as DOMAIN_BYTES_MAX in SetAccount) + // test the edge cases: 255, 256, 257. + std::size_t const maxLength = 256; + for (std::size_t len = maxLength - 1; len <= maxLength + 1; ++len) + { + std::string domain2 = + std::string(len - domain.length() - 1, 'a') + "." + domain; + + BEAST_EXPECT (domain2.length() == len); + + jt[sfDomain.fieldName] = strHex(domain2); + + if (len <= maxLength) + { + env(jt); + BEAST_EXPECT((*env.le(alice))[ sfDomain ] == makeSlice(domain2)); + } + else + { + env(jt, ter(telBAD_DOMAIN)); + } + } + } + + void testMessageKey() + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), alice); + auto jt = noop(alice); + + auto const rkp = randomKeyPair(KeyType::ed25519); + jt[sfMessageKey.fieldName] = strHex(rkp.first.slice()); + env(jt); + BEAST_EXPECT(strHex((*env.le(alice))[ sfMessageKey ]) == strHex(rkp.first.slice())); + + jt[sfMessageKey.fieldName] = ""; + env(jt); + BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfMessageKey)); + + jt[sfMessageKey.fieldName] = strHex("NOT_REALLY_A_PUBKEY"); + env(jt, ter(telBAD_PUBLIC_KEY)); + } + + void testWalletID() + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), alice); + auto jt = noop(alice); + + uint256 somehash = from_hex_text("9633ec8af54f16b5286db1d7b519ef49eefc050c0c8ac4384f1d88acd1bfdf05"); + jt[sfWalletLocator.fieldName] = to_string(somehash); + env(jt); + BEAST_EXPECT((*env.le(alice))[ sfWalletLocator ] == somehash); + + jt[sfWalletLocator.fieldName] = ""; + env(jt); + BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfWalletLocator)); + } + + void testEmailHash() + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), alice); + auto jt = noop(alice); + + uint128 somehash = from_hex_text("fff680681c2f5e6095324e2e08838f221a72ab4f"); + jt[sfEmailHash.fieldName] = to_string(somehash); + env(jt); + BEAST_EXPECT((*env.le(alice))[ sfEmailHash ] == somehash); + + jt[sfEmailHash.fieldName] = ""; + env(jt); + BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfEmailHash)); + } + + void testTransferRate() + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), alice); + auto jt = noop(alice); + + uint32 xfer_rate = 2000000000; + jt[sfTransferRate.fieldName] = xfer_rate; + env(jt); + BEAST_EXPECT((*env.le(alice))[ sfTransferRate ] == xfer_rate); + + jt[sfTransferRate.fieldName] = 0u; + env(jt); + BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfTransferRate)); + + // set a bad value (< QUALITY_ONE) + jt[sfTransferRate.fieldName] = 10u; + env(jt, ter(temBAD_TRANSFER_RATE)); + } + + void testBadInputs() + { + using namespace test::jtx; + Env env(*this); + Account const alice ("alice"); + env.fund(XRP(10000), alice); + + auto jt = fset(alice, asfDisallowXRP); + jt[jss::ClearFlag] = asfDisallowXRP; + env(jt, ter(temINVALID_FLAG)); + + jt = fset(alice, asfRequireAuth); + jt[jss::ClearFlag] = asfRequireAuth; + env(jt, ter(temINVALID_FLAG)); + + jt = fset(alice, asfRequireDest); + jt[jss::ClearFlag] = asfRequireDest; + env(jt, ter(temINVALID_FLAG)); + + jt = fset(alice, asfDisallowXRP); + jt[sfFlags.fieldName] = tfAllowXRP; + env(jt, ter(temINVALID_FLAG)); + + jt = fset(alice, asfRequireAuth); + jt[sfFlags.fieldName] = tfOptionalAuth; + env(jt, ter(temINVALID_FLAG)); + + jt = fset(alice, asfRequireDest); + jt[sfFlags.fieldName] = tfOptionalDestTag; + env(jt, ter(temINVALID_FLAG)); + + jt = fset(alice, asfRequireDest); + jt[sfFlags.fieldName] = tfAccountSetMask; + env(jt, ter(temINVALID_FLAG)); + + env(fset (alice, asfDisableMaster), + sig(alice), ter(tecNO_REGULAR_KEY)); + } + + void testRequireAuthWithDir() + { + using namespace test::jtx; + Env env(*this, features(featureMultiSign)); + Account const alice ("alice"); + Account const bob ("bob"); + + env.fund(XRP(10000), alice); + env.close(); + + // alice should have an empty directory. + BEAST_EXPECT(dirIsEmpty (*env.closed(), keylet::ownerDir(alice))); + + // Give alice a signer list, then there will be stuff in the directory. + env(signers(alice, 1, { { bob, 1} })); + env.close(); + BEAST_EXPECT(! dirIsEmpty (*env.closed(), keylet::ownerDir(alice))); + + env(fset (alice, asfRequireAuth), ter(tecOWNERS)); + } + + void run() + { + testNullAccountSet(); + for(auto const& flag_set : std::vector>({ + {asfRequireDest, "RequireDestTag"}, + {asfRequireAuth, "RequireAuth"}, + {asfDisallowXRP, "DisallowXRP"}, + {asfGlobalFreeze, "GlobalFreeze"}, + {asfDisableMaster, "DisableMaster"}, + {asfDefaultRipple, "DefaultRipple"} + })) + { + testSetAndReset(flag_set.first, flag_set.second); + } + testSetAndResetAccountTxnID(); + testSetNoFreeze(); + testDomain(); + testMessageKey(); + testWalletID(); + testEmailHash(); + testBadInputs(); + testRequireAuthWithDir(); + testTransferRate(); + } + + +}; + +BEAST_DEFINE_TESTSUITE(AccountSet,app,ripple); + +} + diff --git a/src/ripple/unity/rpcx.cpp b/src/ripple/unity/rpcx.cpp index acd466910..2c922acb6 100644 --- a/src/ripple/unity/rpcx.cpp +++ b/src/ripple/unity/rpcx.cpp @@ -97,9 +97,10 @@ #include #include -#include #include +#include #include +#include #include #include #include