std::pair<Blob, bool> strUnHex (std::string const& strSrc) { std::string strTmp; if (strUnHex (strTmp, strSrc) == -1) return std::make_pair (Blob (), false); return std::make_pair(strCopy (strTmp), true); }
void testUnHexFailure (std::string strIn) { std::string strOut; expect (strUnHex (strOut, strIn) == -1, "strUnHex: parsing incorrect input succeeded"); expect (strOut.empty (), "strUnHex: parsing incorrect input returned data"); }
void testUnHexSuccess (std::string strIn, std::string strExpected) { std::string strOut; expect (strUnHex (strOut, strIn) == strExpected.length (), "strUnHex: parsing correct input failed"); expect (strOut == strExpected, "strUnHex: parsing doesn't produce expected result"); }
/** * Decode a JSON value representing a ledger (as found in a dump file) into * a 3-tuple of: #1 a Ledger object, #2 a SHAMap holding a set of * transactions, and #3 a vector of transaction IDs indicating the order * that the transactions in the SHAMap were applied in the Ledger. */ static std::tuple<Ledger::pointer, SHAMap::pointer, std::vector<uint256> > loadLedgerAndTransactionsFromJSON(Application& app, Json::Value const& j) { require (j.isMember ("raw") && j["raw"].isString (), "JSON ledger \"raw\" entry missing or non-string"); std::pair<Blob, bool> ledgerBlob = strUnHex (j["raw"].asString ()); require (ledgerBlob.second, "Error decoding ledger \"raw\" field "); Ledger::pointer ledger = boost::make_shared<Ledger> (ledgerBlob.first, true); require (j.isMember ("txs") && j["txs"].isArray(), "JSON ledger \"txs\" entry missing or non-array"); // Fill a SHAMap full of the current transactions. SHAMap::pointer txSet = boost::make_shared<SHAMap> (smtTRANSACTION, app.getFullBelowCache()); std::vector<uint256> txOrder; for (auto const& jtx : j["txs"]) { std::pair<Blob, bool> txBlob = strUnHex (jtx.asString ()); require (txBlob.second, "Error decoding tx"); Serializer ser (txBlob.first); SerializerIterator sit (ser); SerializedTransaction stx (sit); auto txID = stx.getTransactionID(); require (txSet->addItem (SHAMapItem (txID, ser), true, true), "Error adding transaction"); txOrder.push_back (txID); } return std::make_tuple (ledger, txSet, txOrder); }
boost::optional<ValidatorToken> ValidatorToken::make_ValidatorToken(std::vector<std::string> const& tokenBlob) { try { std::string tokenStr; tokenStr.reserve ( std::accumulate (tokenBlob.cbegin(), tokenBlob.cend(), std::size_t(0), [] (std::size_t init, std::string const& s) { return init + s.size(); })); for (auto const& line : tokenBlob) tokenStr += beast::rfc2616::trim(line); tokenStr = beast::detail::base64_decode(tokenStr); Json::Reader r; Json::Value token; if (! r.parse (tokenStr, token)) return boost::none; if (token.isMember("manifest") && token["manifest"].isString() && token.isMember("validation_secret_key") && token["validation_secret_key"].isString()) { auto const ret = strUnHex (token["validation_secret_key"].asString()); if (! ret.second || ! ret.first.size ()) return boost::none; return ValidatorToken( token["manifest"].asString(), SecretKey(Slice{ret.first.data(), ret.first.size()})); } else { return boost::none; } } catch (std::exception const&) { return boost::none; } }
// { // public_key: <public_key> // channel_id: 256-bit channel id // drops: 64-bit uint (as string) // signature: signature to verify // } Json::Value doChannelVerify (RPC::Context& context) { auto const& params (context.params); for (auto const& p : {jss::public_key, jss::channel_id, jss::amount, jss::signature}) if (!params.isMember (p)) return RPC::missing_field_error (p); std::string const strPk = params[jss::public_key].asString (); auto const pk = parseBase58<PublicKey> (TokenType::TOKEN_ACCOUNT_PUBLIC, strPk); if (!pk) return rpcError (rpcPUBLIC_MALFORMED); uint256 channelId; if (!channelId.SetHexExact (params[jss::channel_id].asString ())) return rpcError (rpcCHANNEL_MALFORMED); std::uint64_t drops = 0; try { drops = std::stoul (params[jss::amount].asString ()); } catch (std::exception const&) { return rpcError (rpcCHANNEL_AMT_MALFORMED); } std::pair<Blob, bool> sig(strUnHex (params[jss::signature].asString ())); if (!sig.second || !sig.first.size ()) return rpcError (rpcINVALID_PARAMS); Serializer msg; serializePayChanAuthorization (msg, channelId, XRPAmount (drops)); Json::Value result; result[jss::signature_verified] = verify (*pk, msg.slice (), makeSlice (sig.first), /*canonical*/ true); return result; }
static bool isMemoOkay (STObject const& st, std::string& reason) { if (!st.isFieldPresent (sfMemos)) return true; auto const& memos = st.getFieldArray (sfMemos); // The number 2048 is a preallocation hint, not a hard limit // to avoid allocate/copy/free's Serializer s (2048); memos.add (s); // FIXME move the memo limit into a config tunable if (s.getDataLength () > 1024) { reason = "The memo exceeds the maximum allowed size."; return false; } for (auto const& memo : memos) { auto memoObj = dynamic_cast <STObject const*> (&memo); if (!memoObj || (memoObj->getFName() != sfMemo)) { reason = "A memo array may contain only Memo objects."; return false; } for (auto const& memoElement : *memoObj) { auto const& name = memoElement.getFName(); if (name != sfMemoType && name != sfMemoData && name != sfMemoFormat) { reason = "A memo may contain only MemoType, MemoData or " "MemoFormat fields."; return false; } // The raw data is stored as hex-octets, which we want to decode. auto data = strUnHex (memoElement.getText ()); if (!data.second) { reason = "The MemoType, MemoData and MemoFormat fields may " "only contain hex-encoded data."; return false; } if (name == sfMemoData) continue; // The only allowed characters for MemoType and MemoFormat are the // characters allowed in URLs per RFC 3986: alphanumerics and the // following symbols: -._~:/?#[]@!$&'()*+,;=% static std::array<char, 256> const allowedSymbols = [] { std::array<char, 256> a; a.fill(0); std::string symbols ( "0123456789" "-._~:/?#[]@!$&'()*+,;=%" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"); for(char c : symbols) a[c] = 1; return a; }(); for (auto c : data.first) { if (!allowedSymbols[c]) { reason = "The MemoType and MemoFormat fields may only " "contain characters that are allowed in URLs " "under RFC 3986."; return false; } } } } return true; }
bool STParsedJSON::parse (std::string const& json_name, Json::Value const& json, SField::ref inName, int depth, std::unique_ptr <STObject>& sub_object) { if (! json.isObject ()) { error = not_an_object (json_name); return false; } SField::ptr name (&inName); boost::ptr_vector<SerializedType> data; Json::Value::Members members (json.getMemberNames ()); for (Json::Value::Members::iterator it (members.begin ()); it != members.end (); ++it) { std::string const& fieldName = *it; Json::Value const& value = json [fieldName]; SField::ref field = SField::getField (fieldName); if (field == sfInvalid) { error = unknown_field (json_name, fieldName); return false; } switch (field.fieldType) { case STI_UINT8: try { if (value.isString ()) { // VFALCO TODO wtf? } else if (value.isInt ()) { if (value.asInt () < 0 || value.asInt () > 255) { error = out_of_range (json_name, fieldName); return false; } data.push_back (new STUInt8 (field, range_check_cast <unsigned char> ( value.asInt (), 0, 255))); } else if (value.isUInt ()) { if (value.asUInt () > 255) { error = out_of_range (json_name, fieldName); return false; } data.push_back (new STUInt8 (field, range_check_cast <unsigned char> ( value.asUInt (), 0, 255))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_UINT16: try { if (value.isString ()) { std::string strValue = value.asString (); if (! strValue.empty () && ((strValue[0] < '0') || (strValue[0] > '9'))) { if (field == sfTransactionType) { TxType const txType (TxFormats::getInstance()-> findTypeByName (strValue)); data.push_back (new STUInt16 (field, static_cast <std::uint16_t> (txType))); if (*name == sfGeneric) name = &sfTransaction; } else if (field == sfLedgerEntryType) { LedgerEntryType const type (LedgerFormats::getInstance()-> findTypeByName (strValue)); data.push_back (new STUInt16 (field, static_cast <std::uint16_t> (type))); if (*name == sfGeneric) name = &sfLedgerEntry; } else { error = invalid_data (json_name, fieldName); return false; } } else { data.push_back (new STUInt16 (field, beast::lexicalCastThrow <std::uint16_t> (strValue))); } } else if (value.isInt ()) { data.push_back (new STUInt16 (field, range_check_cast <std::uint16_t> ( value.asInt (), 0, 65535))); } else if (value.isUInt ()) { data.push_back (new STUInt16 (field, range_check_cast <std::uint16_t> ( value.asUInt (), 0, 65535))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_UINT32: try { if (value.isString ()) { data.push_back (new STUInt32 (field, beast::lexicalCastThrow <std::uint32_t> (value.asString ()))); } else if (value.isInt ()) { data.push_back (new STUInt32 (field, range_check_cast <std::uint32_t> (value.asInt (), 0u, 4294967295u))); } else if (value.isUInt ()) { data.push_back (new STUInt32 (field, static_cast <std::uint32_t> (value.asUInt ()))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_UINT64: try { if (value.isString ()) { data.push_back (new STUInt64 (field, uintFromHex (value.asString ()))); } else if (value.isInt ()) { data.push_back (new STUInt64 (field, range_check_cast<std::uint64_t> ( value.asInt (), 0, 18446744073709551615ull))); } else if (value.isUInt ()) { data.push_back (new STUInt64 (field, static_cast <std::uint64_t> (value.asUInt ()))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_HASH128: try { if (value.isString ()) { data.push_back (new STHash128 (field, value.asString ())); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_HASH160: try { if (value.isString ()) { data.push_back (new STHash160 (field, value.asString ())); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_HASH256: try { if (value.isString ()) { data.push_back (new STHash256 (field, value.asString ())); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_VL: if (! value.isString ()) { error = bad_type (json_name, fieldName); return false; } try { std::pair<Blob, bool> ret(strUnHex (value.asString ())); if (!ret.second) throw std::invalid_argument ("invalid data"); data.push_back (new STVariableLength (field, ret.first)); } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_AMOUNT: try { data.push_back (new STAmount (field, value)); } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_VECTOR256: if (! value.isArray ()) { error = array_expected (json_name, fieldName); return false; } try { data.push_back (new STVector256 (field)); STVector256* tail (dynamic_cast <STVector256*> (&data.back ())); assert (tail); for (Json::UInt i = 0; !json.isValidIndex (i); ++i) { uint256 s; s.SetHex (json[i].asString ()); tail->addValue (s); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_PATHSET: if (!value.isArray ()) { error = array_expected (json_name, fieldName); return false; } try { data.push_back (new STPathSet (field)); STPathSet* tail = dynamic_cast <STPathSet*> (&data.back ()); assert (tail); for (Json::UInt i = 0; value.isValidIndex (i); ++i) { STPath p; if (!value[i].isArray ()) { std::stringstream ss; ss << fieldName << "[" << i << "]"; error = array_expected (json_name, ss.str ()); return false; } for (Json::UInt j = 0; value[i].isValidIndex (j); ++j) { std::stringstream ss; ss << fieldName << "[" << i << "][" << j << "]"; std::string const element_name ( json_name + "." + ss.str()); // each element in this path has some combination of account, // currency, or issuer Json::Value pathEl = value[i][j]; if (!pathEl.isObject ()) { error = not_an_object (element_name); return false; } const Json::Value& account = pathEl["account"]; const Json::Value& currency = pathEl["currency"]; const Json::Value& issuer = pathEl["issuer"]; bool hasCurrency = false; uint160 uAccount, uCurrency, uIssuer; if (! account.isNull ()) { // human account id if (! account.isString ()) { error = string_expected (element_name, "account"); return false; } std::string const strValue (account.asString ()); if (value.size () == 40) // 160-bit hex account value uAccount.SetHex (strValue); { RippleAddress a; if (! a.setAccountID (strValue)) { error = invalid_data (element_name, "account"); return false; } uAccount = a.getAccountID (); } } if (!currency.isNull ()) { // human currency if (!currency.isString ()) { error = string_expected (element_name, "currency"); return false; } hasCurrency = true; if (currency.asString ().size () == 40) { uCurrency.SetHex (currency.asString ()); } else if (!STAmount::currencyFromString ( uCurrency, currency.asString ())) { error = invalid_data (element_name, "currency"); return false; } } if (!issuer.isNull ()) { // human account id if (!issuer.isString ()) { error = string_expected (element_name, "issuer"); return false; } if (issuer.asString ().size () == 40) { uIssuer.SetHex (issuer.asString ()); } else { RippleAddress a; if (!a.setAccountID (issuer.asString ())) { error = invalid_data (element_name, "issuer"); return false; } uIssuer = a.getAccountID (); } } p.addElement (STPathElement (uAccount, uCurrency, uIssuer, hasCurrency)); } tail->addPath (p); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_ACCOUNT: { if (! value.isString ()) { error = bad_type (json_name, fieldName); return false; } std::string strValue = value.asString (); try { if (value.size () == 40) // 160-bit hex account value { uint160 v; v.SetHex (strValue); data.push_back (new STAccount (field, v)); } else { // ripple address RippleAddress a; if (!a.setAccountID (strValue)) { error = invalid_data (json_name, fieldName); return false; } data.push_back (new STAccount (field, a.getAccountID ())); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } } break; case STI_OBJECT: case STI_TRANSACTION: case STI_LEDGERENTRY: case STI_VALIDATION: if (! value.isObject ()) { error = not_an_object (json_name, fieldName); return false; } if (depth > 64) { error = too_deep (json_name, fieldName); return false; } try { std::unique_ptr <STObject> sub_object_; bool const success (parse (json_name + "." + fieldName, value, field, depth + 1, sub_object_)); if (! success) return false; data.push_back (sub_object_.release ()); } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_ARRAY: if (! value.isArray ()) { error = array_expected (json_name, fieldName); return false; } try { data.push_back (new STArray (field)); STArray* tail = dynamic_cast<STArray*> (&data.back ()); assert (tail); for (Json::UInt i = 0; value.isValidIndex (i); ++i) { bool const isObject (value[i].isObject()); bool const singleKey (isObject ? value [i].size() == 1 : true); if (!isObject || !singleKey) { std::stringstream ss; ss << json_name << "." << fieldName << "[" << i << "]"; error = singleton_expected (ss.str ()); return false; } // TODO: There doesn't seem to be a nice way to get just the // first/only key in an object without copying all keys into // a vector std::string const objectName (value[i].getMemberNames()[0]);; SField::ref nameField (SField::getField(objectName)); if (nameField == sfInvalid) { error = unknown_field (json_name, objectName); return false; } Json::Value const objectFields (value[i][objectName]); std::unique_ptr <STObject> sub_object_; { std::stringstream ss; ss << json_name << "." << fieldName << "[" << i << "]." << objectName; bool const success (parse (ss.str (), objectFields, nameField, depth + 1, sub_object_)); if (! success) return false; } tail->push_back (*sub_object_); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; default: error = bad_type (json_name, fieldName); return false; } } sub_object.reset (new STObject (*name, data)); return true; }
bool ValidatorList::load ( PublicKey const& localSigningKey, std::vector<std::string> const& configKeys, std::vector<std::string> const& publisherKeys) { static boost::regex const re ( "[[:space:]]*" // skip leading whitespace "([[:alnum:]]+)" // node identity "(?:" // begin optional comment block "[[:space:]]+" // (skip all leading whitespace) "(?:" // begin optional comment "(.*[^[:space:]]+)" // the comment "[[:space:]]*" // (skip all trailing whitespace) ")?" // end optional comment ")?" // end optional comment block ); boost::unique_lock<boost::shared_mutex> read_lock{mutex_}; JLOG (j_.debug()) << "Loading configured trusted validator list publisher keys"; std::size_t count = 0; for (auto key : publisherKeys) { JLOG (j_.trace()) << "Processing '" << key << "'"; auto const ret = strUnHex (key); if (! ret.second || ! ret.first.size ()) { JLOG (j_.error()) << "Invalid validator list publisher key: " << key; return false; } auto id = PublicKey(Slice{ ret.first.data (), ret.first.size() }); if (validatorManifests_.revoked (id)) { JLOG (j_.warn()) << "Configured validator list publisher key is revoked: " << key; continue; } if (publisherLists_.count(id)) { JLOG (j_.warn()) << "Duplicate validator list publisher key: " << key; continue; } publisherLists_[id].available = false; ++count; } JLOG (j_.debug()) << "Loaded " << count << " keys"; localPubKey_ = validatorManifests_.getMasterKey (localSigningKey); // Treat local validator key as though it was listed in the config if (localPubKey_.size()) keyListings_.insert ({ localPubKey_, 1 }); JLOG (j_.debug()) << "Loading configured validator keys"; count = 0; PublicKey local; for (auto const& n : configKeys) { JLOG (j_.trace()) << "Processing '" << n << "'"; boost::smatch match; if (!boost::regex_match (n, match, re)) { JLOG (j_.error()) << "Malformed entry: '" << n << "'"; return false; } auto const id = parseBase58<PublicKey>( TokenType::TOKEN_NODE_PUBLIC, match[1]); if (!id) { JLOG (j_.error()) << "Invalid node identity: " << match[1]; return false; } // Skip local key which was already added if (*id == localPubKey_ || *id == localSigningKey) continue; auto ret = keyListings_.insert ({*id, 1}); if (! ret.second) { JLOG (j_.warn()) << "Duplicate node identity: " << match[1]; continue; } auto it = publisherLists_.emplace( std::piecewise_construct, std::forward_as_tuple(local), std::forward_as_tuple()); // Config listed keys never expire if (it.second) it.first->second.expiration = TimeKeeper::time_point::max(); it.first->second.list.emplace_back(std::move(*id)); it.first->second.available = true; ++count; } JLOG (j_.debug()) << "Loaded " << count << " entries"; return true; }
ListDisposition ValidatorList::verify ( Json::Value& list, PublicKey& pubKey, std::string const& manifest, std::string const& blob, std::string const& signature) { auto m = Manifest::make_Manifest (beast::detail::base64_decode(manifest)); if (! m || ! publisherLists_.count (m->masterKey)) return ListDisposition::untrusted; pubKey = m->masterKey; auto const revoked = m->revoked(); auto const result = publisherManifests_.applyManifest ( std::move(*m)); if (revoked && result == ManifestDisposition::accepted) { removePublisherList (pubKey); publisherLists_.erase (pubKey); } if (revoked || result == ManifestDisposition::invalid) return ListDisposition::untrusted; auto const sig = strUnHex(signature); auto const data = beast::detail::base64_decode (blob); if (! sig.second || ! ripple::verify ( publisherManifests_.getSigningKey(pubKey), makeSlice(data), makeSlice(sig.first))) return ListDisposition::invalid; Json::Reader r; if (! r.parse (data, list)) return ListDisposition::invalid; if (list.isMember("sequence") && list["sequence"].isInt() && list.isMember("expiration") && list["expiration"].isInt() && list.isMember("validators") && list["validators"].isArray()) { auto const sequence = list["sequence"].asUInt(); auto const expiration = TimeKeeper::time_point{ TimeKeeper::duration{list["expiration"].asUInt()}}; if (sequence < publisherLists_[pubKey].sequence || expiration <= timeKeeper_.now()) return ListDisposition::stale; else if (sequence == publisherLists_[pubKey].sequence) return ListDisposition::same_sequence; } else { return ListDisposition::invalid; } return ListDisposition::accepted; }
ListDisposition ValidatorList::applyList ( std::string const& manifest, std::string const& blob, std::string const& signature, std::uint32_t version) { if (version != requiredListVersion) return ListDisposition::unsupported_version; boost::unique_lock<boost::shared_mutex> lock{mutex_}; Json::Value list; PublicKey pubKey; auto const result = verify (list, pubKey, manifest, blob, signature); if (result != ListDisposition::accepted) return result; // Update publisher's list Json::Value const& newList = list["validators"]; publisherLists_[pubKey].available = true; publisherLists_[pubKey].sequence = list["sequence"].asUInt (); publisherLists_[pubKey].expiration = TimeKeeper::time_point{ TimeKeeper::duration{list["expiration"].asUInt()}}; std::vector<PublicKey>& publisherList = publisherLists_[pubKey].list; std::vector<PublicKey> oldList = publisherList; publisherList.clear (); publisherList.reserve (newList.size ()); std::vector<std::string> manifests; for (auto const& val : newList) { if (val.isObject () && val.isMember ("validation_public_key") && val["validation_public_key"].isString ()) { std::pair<Blob, bool> ret (strUnHex ( val["validation_public_key"].asString ())); if (! ret.second || ! ret.first.size ()) { JLOG (j_.error()) << "Invalid node identity: " << val["validation_public_key"].asString (); } else { publisherList.push_back ( PublicKey(Slice{ ret.first.data (), ret.first.size() })); } if (val.isMember ("manifest") && val["manifest"].isString ()) manifests.push_back(val["manifest"].asString ()); } } // Update keyListings_ for added and removed keys std::sort ( publisherList.begin (), publisherList.end ()); auto iNew = publisherList.begin (); auto iOld = oldList.begin (); while (iNew != publisherList.end () || iOld != oldList.end ()) { if (iOld == oldList.end () || (iNew != publisherList.end () && *iNew < *iOld)) { // Increment list count for added keys ++keyListings_[*iNew]; ++iNew; } else if (iNew == publisherList.end () || (iOld != oldList.end () && *iOld < *iNew)) { // Decrement list count for removed keys if (keyListings_[*iOld] <= 1) keyListings_.erase (*iOld); else --keyListings_[*iOld]; ++iOld; } else { ++iNew; ++iOld; } } if (publisherList.empty()) { JLOG (j_.warn()) << "No validator keys included in valid list"; } for (auto const& valManifest : manifests) { auto m = Manifest::make_Manifest ( beast::detail::base64_decode(valManifest)); if (! m || ! keyListings_.count (m->masterKey)) { JLOG (j_.warn()) << "List for " << strHex(pubKey) << " contained untrusted validator manifest"; continue; } auto const result = validatorManifests_.applyManifest (std::move(*m)); if (result == ManifestDisposition::invalid) { JLOG (j_.warn()) << "List for " << strHex(pubKey) << " contained invalid validator manifest"; } } return ListDisposition::accepted; }
void testUnHexFailure (std::string const& strIn) { auto rv = strUnHex (strIn); BEAST_EXPECT(! rv.second); BEAST_EXPECT(rv.first.empty()); }
void testUnHexSuccess (std::string const& strIn, std::string const& strExpected) { auto rv = strUnHex (strIn); BEAST_EXPECT(rv.second); BEAST_EXPECT(makeSlice(rv.first) == makeSlice(strExpected)); }
// { // tx_json: <object>, // secret: <secret> // } Json::Value doSubmit (RPC::Context& context) { context.loadType = Resource::feeMediumBurdenRPC; if (!context.params.isMember (jss::tx_blob)) { auto const failType = getFailHard (context); if (context.role != Role::ADMIN && !context.app.config().canSign()) return RPC::make_error (rpcNOT_SUPPORTED, "Signing is not supported by this server."); auto ret = RPC::transactionSubmit ( context.params, failType, context.role, context.ledgerMaster.getValidatedLedgerAge(), context.app, RPC::getProcessTxnFn (context.netOps)); ret[jss::deprecated] = "Signing support in the 'submit' command has been " "deprecated and will be removed in a future version " "of the server. Please migrate to a standalone " "signing tool."; return ret; } Json::Value jvResult; std::pair<Blob, bool> ret(strUnHex (context.params[jss::tx_blob].asString ())); if (!ret.second || !ret.first.size ()) return rpcError (rpcINVALID_PARAMS); SerialIter sitTrans (makeSlice(ret.first)); std::shared_ptr<STTx const> stpTrans; try { stpTrans = std::make_shared<STTx const> (std::ref (sitTrans)); } catch (std::exception& e) { jvResult[jss::error] = "invalidTransaction"; jvResult[jss::error_exception] = e.what (); return jvResult; } { if (!context.app.checkSigs()) forceValidity(context.app.getHashRouter(), stpTrans->getTransactionID(), Validity::SigGoodOnly); auto validity = checkValidity(context.app.getHashRouter(), *stpTrans, context.ledgerMaster.getCurrentLedger()->rules(), context.app.config()); if (validity.first != Validity::Valid) { jvResult[jss::error] = "invalidTransaction"; jvResult[jss::error_exception] = "fails local checks: " + validity.second; return jvResult; } } std::string reason; auto tpTrans = std::make_shared<Transaction> ( stpTrans, reason, context.app); if (tpTrans->getStatus() != NEW) { jvResult[jss::error] = "invalidTransaction"; jvResult[jss::error_exception] = "fails local checks: " + reason; return jvResult; } try { auto const failType = getFailHard (context); context.netOps.processTransaction ( tpTrans, isUnlimited (context.role), true, failType); } catch (std::exception& e) { jvResult[jss::error] = "internalSubmit"; jvResult[jss::error_exception] = e.what (); return jvResult; } try { jvResult[jss::tx_json] = tpTrans->getJson (0); jvResult[jss::tx_blob] = strHex ( tpTrans->getSTransaction ()->getSerializer ().peekData ()); if (temUNCERTAIN != tpTrans->getResult ()) { std::string sToken; std::string sHuman; transResultInfo (tpTrans->getResult (), sToken, sHuman); jvResult[jss::engine_result] = sToken; jvResult[jss::engine_result_code] = tpTrans->getResult (); jvResult[jss::engine_result_message] = sHuman; } return jvResult; } catch (std::exception& e) { jvResult[jss::error] = "internalJson"; jvResult[jss::error_exception] = e.what (); return jvResult; } }