diff --git a/src/ripple/json/json_value.h b/src/ripple/json/json_value.h index fd9ca6dc23..22ccc6cf25 100644 --- a/src/ripple/json/json_value.h +++ b/src/ripple/json/json_value.h @@ -71,17 +71,17 @@ enum CommentPlacement class StaticString { public: - explicit StaticString ( const char* czstring ) + constexpr explicit StaticString ( const char* czstring ) : str_ ( czstring ) { } - operator const char* () const + constexpr operator const char* () const { return str_; } - const char* c_str () const + constexpr const char* c_str () const { return str_; } diff --git a/src/ripple/protocol/JsonFields.h b/src/ripple/protocol/JsonFields.h index 00942c00ce..50d16fc19d 100644 --- a/src/ripple/protocol/JsonFields.h +++ b/src/ripple/protocol/JsonFields.h @@ -27,7 +27,7 @@ namespace jss { // JSON static strings -#define JSS(x) const ::Json::StaticString x ( #x ) +#define JSS(x) constexpr ::Json::StaticString x ( #x ) /* The "StaticString" field names are used instead of string literals to optimize the performance of accessing members of Json::Value objects. diff --git a/src/ripple/rpc/impl/KeypairForSignature.cpp b/src/ripple/rpc/impl/KeypairForSignature.cpp index 77a1916d5e..562c86cb35 100644 --- a/src/ripple/rpc/impl/KeypairForSignature.cpp +++ b/src/ripple/rpc/impl/KeypairForSignature.cpp @@ -65,31 +65,43 @@ std::pair keypairForSignature (Json::Value const& params, Json::Value& error) { bool const has_key_type = params.isMember (jss::key_type); - bool const hasPassphrase = params.isMember (jss::passphrase); - bool const hasSecret = params.isMember (jss::secret); - bool const hasSeed = params.isMember (jss::seed); - bool const hasSeedHex = params.isMember (jss::seed_hex); - int const n_secrets = - (hasPassphrase ? 1 : 0) + - (hasSecret ? 1 : 0) + - (hasSeed ? 1 : 0) + - (hasSeedHex ? 1 : 0); + // All of the secret types we allow, but only one at a time. + // The array should be constexpr, but that makes Visual Studio unhappy. + static char const* const secretTypes[] + { + jss::passphrase.c_str(), + jss::secret.c_str(), + jss::seed.c_str(), + jss::seed_hex.c_str() + }; - if (n_secrets == 0) + // Identify which secret type is in use. + char const* secretType = nullptr; + int secretCount = 0; + for (auto t : secretTypes) + { + if (params.isMember (t)) + { + ++secretCount; + secretType = t; + } + } + + if (secretCount == 0 || secretType == nullptr) { error = RPC::missing_field_error (jss::secret); return { }; } - if (n_secrets > 1) + if (secretCount > 1) { // `passphrase`, `secret`, `seed`, and `seed_hex` are mutually exclusive. error = rpcError (rpcBAD_SECRET); return { }; } - if (has_key_type && hasSecret) + if (has_key_type && (secretType == jss::secret.c_str())) { // `secret` is deprecated. error = rpcError (rpcBAD_SECRET); @@ -121,7 +133,7 @@ keypairForSignature (Json::Value const& params, Json::Value& error) if (!seed) { error = RPC::make_error (rpcBAD_SEED, - RPC::invalid_field_message (jss::secret)); + RPC::invalid_field_message (secretType)); return { }; } diff --git a/src/ripple/rpc/tests/JSONRPC.test.cpp b/src/ripple/rpc/tests/JSONRPC.test.cpp index ed4a30e4e8..05456e7007 100644 --- a/src/ripple/rpc/tests/JSONRPC.test.cpp +++ b/src/ripple/rpc/tests/JSONRPC.test.cpp @@ -35,6 +35,15 @@ struct TxnTestData { char const* const description; char const* const json; + // The JSON is applied to four different interfaces: + // 1. sign, + // 2. submit, + // 3. sign_for, and + // 4. submit_multisigned. + // The JSON is not valid for all of these interfaces, but it should + // crash none of them, and should provide reliable error messages. + // + // The expMsg array contains the expected error string for the above cases. char const* const expMsg[4]; // Default and copy ctors should be deleted, but that displeases gcc 4.6.3. @@ -533,6 +542,44 @@ R"({ "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}, +{ "Use 'seed' instead of 'secret'.", +R"({ + "command": "doesnt_matter", + "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi", + "key_type": "ed25519", + "seed": "sh1yJfwoi98zCygwijUzuHmJDeVKd", + "tx_json": { + "Account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "TransactionType": "Payment" + } +})", +{ +"", +"", +"Missing field 'tx_json.Sequence'.", +"Missing field 'tx_json.Sequence'."}}, + +{ "Malformed 'seed'.", +R"({ + "command": "doesnt_matter", + "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi", + "key_type": "ed25519", + "seed": "not a seed", + "tx_json": { + "Account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "TransactionType": "Payment" + } +})", +{ +"Invalid field 'seed'.", +"Invalid field 'seed'.", +"Missing field 'tx_json.Sequence'.", +"Missing field 'tx_json.Sequence'."}}, + { "'tx_json' must be present.", R"({ "command": "doesnt_matter", @@ -841,6 +888,52 @@ R"({ "", "Missing field 'tx_json.Signers'."}}, +{ "Offline sign_for using 'seed' instead of 'secret'.", +R"({ + "command": "doesnt_matter", + "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi", + "key_type": "ed25519", + "seed": "sh1yJfwoi98zCygwijUzuHmJDeVKd", + "offline": 1, + "tx_json": { + "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Amount": "1000000000", + "Destination": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"", +"", +"", +"Missing field 'tx_json.Signers'."}}, + +{ "Malformed seed in sign_for.", +R"({ + "command": "doesnt_matter", + "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi", + "key_type": "ed25519", + "seed": "sh1yJfwoi98zCygwjUzuHmJDeVKd", + "offline": 1, + "tx_json": { + "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Amount": "1000000000", + "Destination": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Invalid field 'seed'.", +"Invalid field 'seed'.", +"Invalid field 'seed'.", +"Missing field 'tx_json.Signers'."}}, + { "Missing 'Account' in sign_for.", R"({ "command": "doesnt_matter", @@ -1840,12 +1933,16 @@ public: test::jtx::Account const a {"a"}; // rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA test::jtx::Account const g {"g"}; // rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4 auto const USD = g["USD"]; + + // Account: rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi + // seed: sh1yJfwoi98zCygwijUzuHmJDeVKd + test::jtx::Account const ed {"ed", KeyType::ed25519}; // master is rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh. // "b" (not in the ledger) is rDg53Haik2475DJx8bjMDSDPj4VX7htaMd. // "c" (phantom signer) is rPcNzota6B8YBokhYtcTNqQVCngtbnWfux. test::jtx::Env env(*this, test::jtx::features(featureMultiSign)); - env.fund(test::jtx::XRP(100000), a, g); + env.fund(test::jtx::XRP(100000), a, ed, g); env.close(); env(trust(a, USD(1000)));