void msig::operator()(Env& env, JTx& jt) const { auto const mySigners = signers; jt.signer = [mySigners, &env](Env&, JTx& jt) { jt[sfSigningPubKey.getJsonName()] = ""; boost::optional<STObject> st; try { st = parse(jt.jv); } catch(parse_error const&) { env.test.log << pretty(jt.jv) << std::endl; Rethrow(); } auto& js = jt[sfSigners.getJsonName()]; js.resize(mySigners.size()); for(std::size_t i = 0; i < mySigners.size(); ++i) { auto const& e = mySigners[i]; auto& jo = js[i][sfSigner.getJsonName()]; jo[jss::Account] = e.acct.human(); jo[jss::SigningPubKey] = strHex(e.sig.pk().slice()); Serializer ss {buildMultiSigningData (*st, e.acct.id())}; auto const sig = ripple::sign ( *publicKeyType(e.sig.pk().slice()), e.sig.sk(), ss.slice()); jo[sfTxnSignature.getJsonName()] = strHex(Slice{ sig.data(), sig.size() }); } }; }
void msig::operator()(Env const& env, JTx& jt) const { // VFALCO Inefficient pre-C++14 auto accounts = accounts_; std::sort(accounts.begin(), accounts.end(), [](Account const& lhs, Account const& rhs) { return lhs.id() < rhs.id(); }); jt.signer = [accounts, &env](Env&, JTx& jt) { jt["SigningPubKey"] = ""; boost::optional<STObject> st; try { st = parse(jt.jv); } catch(parse_error const&) { env.test.log << pretty(jt.jv); throw; } auto const signFor = parseBase58<AccountID>( jt.jv[jss::Account].asString()); if (! signFor) { env.test.log << "invalid AccountID: '" << jt.jv[jss::Account].asString() << "'"; throw parse_error("msig: bad Account"); } auto& jv = jt["MultiSigners"][0u]["SigningFor"]; jv[jss::Account] = jt[jss::Account]; auto& js = jv["SigningAccounts"]; js.resize(accounts.size()); for(std::size_t i = 0; i < accounts.size(); ++i) { auto const& e = accounts[i]; auto& jo = js[i]["SigningAccount"]; jo[jss::Account] = e.human(); jo[jss::SigningPubKey] = strHex(e.pk().slice()); Serializer ss; ss.add32 (HashPrefix::txMultiSign); st->addWithoutSigningFields(ss); ss.add160(*signFor); ss.add160(e.id()); auto const sig = ripple::sign( *publicKeyType(e.pk().slice()), e.sk(), ss.slice()); jo["MultiSignature"] = strHex(Slice{ sig.data(), sig.size() }); } }; }
void memontype::operator()(Env const&, JTx& jt) const { auto& jv = jt.jv; auto& ma = jv["Memos"]; auto& mi = ma[ma.size()]; auto& m = mi["Memo"]; m["MemoData"] = strHex(data_); m["MemoFormat"] = strHex(format_); }
void msig2_t::operator()(Env const& env, JTx& jt) const { // VFALCO Inefficient pre-C++14 auto const sigs = sigs_; jt.signer = [sigs, &env](Env&, JTx& jt) { jt["SigningPubKey"] = ""; boost::optional<STObject> st; try { st = parse(jt.jv); } catch(parse_error const&) { env.test.log << pretty(jt.jv); throw; } auto& ja = jt["MultiSigners"]; ja.resize(sigs.size()); for (auto i = std::make_pair(0, sigs.begin()); i.first < sigs.size(); ++i.first, ++i.second) { auto const& sign_for = i.second->first; auto const& list = i.second->second; auto& ji = ja[i.first]["SigningFor"]; ji[jss::Account] = sign_for.human(); auto& js = ji["SigningAccounts"]; js.resize(list.size()); for (auto j = std::make_pair(0, list.begin()); j.first < list.size(); ++j.first, ++j.second) { auto& jj = js[j.first]["SigningAccount"]; jj[jss::Account] = j.second->human(); jj[jss::SigningPubKey] = strHex( j.second->pk().slice()); Serializer ss; ss.add32 (HashPrefix::txMultiSign); st->addWithoutSigningFields(ss); ss.add160(sign_for.id()); ss.add160(j.second->id()); auto const sig = ripple::sign( *publicKeyType(j.second->pk().slice()), j.second->sk(), ss.slice()); jj["MultiSignature"] = strHex(Slice{ sig.data(), sig.size() }); } } }; }
void sign (Json::Value& jv, Account const& account) { jv[jss::SigningPubKey] = strHex(account.pk().slice()); Serializer ss; ss.add32 (HashPrefix::txSign); parse(jv).add(ss); auto const sig = ripple::sign( *publicKeyType(account.pk().slice()), account.sk(), ss.slice()); jv[jss::TxnSignature] = strHex(Slice{ sig.data(), sig.size() }); }
// { // passphrase: <string> // } Json::Value doWalletPropose (RPC::Context& context) { context.lock_.unlock (); RippleAddress naSeed; RippleAddress naAccount; if (!context.params_.isMember ("passphrase")) naSeed.setSeedRandom (); else if (!naSeed.setSeedGeneric (context.params_["passphrase"].asString ())) return rpcError(rpcBAD_SEED); RippleAddress naGenerator = RippleAddress::createGeneratorPublic (naSeed); naAccount.setAccountPublic (naGenerator, 0); Json::Value obj (Json::objectValue); obj["master_seed"] = naSeed.humanSeed (); obj["master_seed_hex"] = to_string (naSeed.getSeed ()); obj["master_key"] = naSeed.humanSeed1751(); obj["account_id"] = naAccount.humanAccountID (); obj["public_key"] = naAccount.humanAccountPublic(); auto acct = naAccount.getAccountPublic(); obj["public_key_hex"] = strHex(acct.begin(), acct.size()); return obj; }
Json::Value STPath::getJson (int) const { Json::Value ret (Json::arrayValue); for (auto it: mPath) { Json::Value elem (Json::objectValue); int iType = it.getNodeType (); elem[jss::type] = iType; elem[jss::type_hex] = strHex (iType); if (iType & STPathElement::typeAccount) elem[jss::account] = to_string (it.getAccountID ()); if (iType & STPathElement::typeCurrency) elem[jss::currency] = to_string (it.getCurrency ()); if (iType & STPathElement::typeIssuer) elem[jss::issuer] = to_string (it.getIssuerID ()); ret.append (elem); } return ret; }
uint256 ParseHashV(const UniValue& v, std::string strName) { std::string strHex(v.get_str()); if (64 != strHex.length()) throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex)); if (!IsHex(strHex)) // Note: IsHex("") is false throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); return uint256S(strHex); }
void memotype::operator()(Env const&, JTx& jt) const { auto& jv = jt.jv; auto& ma = jv["Memos"]; auto& mi = ma[ma.size()]; auto& m = mi["Memo"]; m["MemoType"] = strHex(s_); }
// VFALCO TODO Make this a generic utility function of some container class // std::string AccountState::createGravatarUrl (uint128 uEmailHash) { Blob vucMD5 (uEmailHash.begin (), uEmailHash.end ()); std::string strMD5Lower = strHex (vucMD5); boost::to_lower (strMD5Lower); // VFALCO TODO Give a name and move this constant to a more visible location. // Also shouldn't this be https? return str (boost::format ("http://www.gravatar.com/avatar/%s") % strMD5Lower); }
Json::Value STTx::getJson (int options, bool binary) const { if (binary) { Json::Value ret; Serializer s = STObject::getSerializer (); ret[jss::tx] = strHex (s.peekData ()); ret[jss::hash] = to_string (getTransactionID ()); return ret; } return getJson(options); }
// { // secret_key: <signing_secret_key> // channel_id: 256-bit channel id // drops: 64-bit uint (as string) // } Json::Value doChannelAuthorize (RPC::Context& context) { auto const& params (context.params); for (auto const& p : {jss::secret, jss::channel_id, jss::amount}) if (!params.isMember (p)) return RPC::missing_field_error (p); Json::Value result; auto const keypair = RPC::keypairForSignature (params, result); if (RPC::contains_error (result)) return result; 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); } Serializer msg; serializePayChanAuthorization (msg, channelId, XRPAmount (drops)); try { auto const buf = sign (keypair.first, keypair.second, msg.slice ()); result[jss::signature] = strHex (buf); } catch (std::exception&) { result = RPC::make_error (rpcINTERNAL, "Exception occurred during signing."); } return result; }
std::string to_string(Currency const& currency) { static Currency const sIsoBits ("FFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFF"); // Characters we are willing to allow in the ASCII representation of a // three-letter currency code. static std::string const allowed_characters = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "<>(){}[]|?!@#$%^&*"; if (currency == zero) return systemCurrencyCode(); if (currency == vbcCurrency()) return systemCurrencyCodeVBC(); if (currency == noCurrency()) return "1"; if ((currency & sIsoBits).isZero ()) { // The offset of the 3 character ISO code in the currency descriptor int const isoOffset = 12; std::string const iso( currency.data () + isoOffset, currency.data () + isoOffset + 3); // Specifying the system currency code using ISO-style representation // is not allowed. if ((iso != systemCurrencyCode()) && (iso != systemCurrencyCodeVBC()) && (iso.find_first_not_of (allowed_characters) == std::string::npos)) { return iso; } } return strHex (currency.begin (), currency.size ()); }
// { // ledger_hash : <ledger> // ledger_index : <ledger_index> // } Json::Value doLedgerHeader (RPC::Context& context) { Ledger::pointer lpLedger; Json::Value jvResult = RPC::lookupLedger ( context.params_, lpLedger, context.netOps_); if (!lpLedger) return jvResult; Serializer s; lpLedger->addRaw (s); jvResult["ledger_data"] = strHex (s.peekData ()); // This information isn't verified: they should only use it if they trust // us. lpLedger->addJson (jvResult, 0); return jvResult; }
void addChannel (Json::Value& jsonLines, SLE const& line) { Json::Value& jDst (jsonLines.append (Json::objectValue)); jDst[jss::channel_id] = to_string (line.key ()); jDst[jss::account] = to_string (line[sfAccount]); jDst[jss::destination_account] = to_string (line[sfDestination]); jDst[jss::amount] = line[sfAmount].getText (); jDst[jss::balance] = line[sfBalance].getText (); PublicKey const pk (line[sfPublicKey]); jDst[jss::public_key] = toBase58 (TokenType::TOKEN_ACCOUNT_PUBLIC, pk); jDst[jss::public_key_hex] = strHex (pk); jDst[jss::settle_delay] = line[sfSettleDelay]; if (auto const& v = line[~sfExpiration]) jDst[jss::expiration] = *v; if (auto const& v = line[~sfCancelAfter]) jDst[jss::cancel_after] = *v; if (auto const& v = line[~sfSourceTag]) jDst[jss::source_tag] = *v; if (auto const& v = line[~sfDestinationTag]) jDst[jss::destination_tag] = *v; }
void AcceptedLedgerTx::buildJson () { mJson = Json::objectValue; mJson[jss::transaction] = mTxn->getJson (0); if (mMeta) { mJson[jss::meta] = mMeta->getJson (0); mJson[jss::raw_meta] = strHex (mRawMeta); } mJson[jss::result] = transHuman (mResult); if (!mAffected.empty ()) { Json::Value& affected = (mJson[jss::affected] = Json::arrayValue); for (auto const& ra : mAffected) { affected.append (ra.humanAccountID ()); } } if (mTxn->getTxnType () == ttOFFER_CREATE) { auto const account (mTxn->getSourceAccount ().getAccountID ()); auto const amount (mTxn->getFieldAmount (sfTakerGets)); // If the offer create is not self funded then add the owner balance if (account != amount.issue ().account) { LedgerEntrySet les (mLedger, tapNONE, true); auto const ownerFunds (funds( les, account, amount, fhIGNORE_FREEZE)); mJson[jss::transaction][jss::owner_funds] = ownerFunds.getText (); } } }
Json::Value STPath::getJson (int) const { Json::Value ret (Json::arrayValue); BOOST_FOREACH (std::vector<STPathElement>::const_iterator::value_type it, mPath) { Json::Value elem (Json::objectValue); int iType = it.getNodeType (); elem["type"] = iType; elem["type_hex"] = strHex (iType); if (iType & STPathElement::typeAccount) elem["account"] = RippleAddress::createHumanAccountID (it.getAccountID ()); if (iType & STPathElement::typeCurrency) elem["currency"] = STAmount::createHumanCurrency (it.getCurrency ()); if (iType & STPathElement::typeIssuer) elem["issuer"] = RippleAddress::createHumanAccountID (it.getIssuerID ()); ret.append (elem); }
std::string STVariableLength::getText() const { return strHex(value); }
SHAMapTreeNode::SHAMapTreeNode (const SHAMapNode& id, Blob const& rawNode, uint32 seq, SHANodeFormat format, uint256 const& hash, bool hashValid) : SHAMapNode (id), mSeq (seq), mType (tnERROR), mIsBranch (0), mFullBelow (false) { if (format == snfWIRE) { Serializer s (rawNode); int type = s.removeLastByte (); int len = s.getLength (); if ((type < 0) || (type > 4)) { #ifdef BEAST_DEBUG Log::out() << "Invalid wire format node"; Log::out() << strHex (rawNode); assert (false); #endif throw std::runtime_error ("invalid node AW type"); } if (type == 0) { // transaction mItem = boost::make_shared<SHAMapItem> (s.getPrefixHash (HashPrefix::transactionID), s.peekData ()); mType = tnTRANSACTION_NM; } else if (type == 1) { // account state if (len < (256 / 8)) throw std::runtime_error ("short AS node"); uint256 u; s.get256 (u, len - (256 / 8)); s.chop (256 / 8); if (u.isZero ()) throw std::runtime_error ("invalid AS node"); mItem = boost::make_shared<SHAMapItem> (u, s.peekData ()); mType = tnACCOUNT_STATE; } else if (type == 2) { // full inner if (len != 512) throw std::runtime_error ("invalid FI node"); for (int i = 0; i < 16; ++i) { s.get256 (mHashes[i], i * 32); if (mHashes[i].isNonZero ()) mIsBranch |= (1 << i); } mType = tnINNER; } else if (type == 3) { // compressed inner for (int i = 0; i < (len / 33); ++i) { int pos; s.get8 (pos, 32 + (i * 33)); if ((pos < 0) || (pos >= 16)) throw std::runtime_error ("invalid CI node"); s.get256 (mHashes[pos], i * 33); if (mHashes[pos].isNonZero ()) mIsBranch |= (1 << pos); } mType = tnINNER; } else if (type == 4) { // transaction with metadata if (len < (256 / 8)) throw std::runtime_error ("short TM node"); uint256 u; s.get256 (u, len - (256 / 8)); s.chop (256 / 8); if (u.isZero ()) throw std::runtime_error ("invalid TM node"); mItem = boost::make_shared<SHAMapItem> (u, s.peekData ()); mType = tnTRANSACTION_MD; } } else if (format == snfPREFIX) { if (rawNode.size () < 4) { WriteLog (lsINFO, SHAMapNode) << "size < 4"; throw std::runtime_error ("invalid P node"); } uint32 prefix = rawNode[0]; prefix <<= 8; prefix |= rawNode[1]; prefix <<= 8; prefix |= rawNode[2]; prefix <<= 8; prefix |= rawNode[3]; Serializer s (rawNode.begin () + 4, rawNode.end ()); if (prefix == HashPrefix::transactionID) { mItem = boost::make_shared<SHAMapItem> (Serializer::getSHA512Half (rawNode), s.peekData ()); mType = tnTRANSACTION_NM; } else if (prefix == HashPrefix::leafNode) { if (s.getLength () < 32) throw std::runtime_error ("short PLN node"); uint256 u; s.get256 (u, s.getLength () - 32); s.chop (32); if (u.isZero ()) { WriteLog (lsINFO, SHAMapNode) << "invalid PLN node"; throw std::runtime_error ("invalid PLN node"); } mItem = boost::make_shared<SHAMapItem> (u, s.peekData ()); mType = tnACCOUNT_STATE; } else if (prefix == HashPrefix::innerNode) { if (s.getLength () != 512) throw std::runtime_error ("invalid PIN node"); for (int i = 0; i < 16; ++i) { s.get256 (mHashes[i], i * 32); if (mHashes[i].isNonZero ()) mIsBranch |= (1 << i); } mType = tnINNER; } else if (prefix == HashPrefix::txNode) { // transaction with metadata if (s.getLength () < 32) throw std::runtime_error ("short TXN node"); uint256 txID; s.get256 (txID, s.getLength () - 32); s.chop (32); mItem = boost::make_shared<SHAMapItem> (txID, s.peekData ()); mType = tnTRANSACTION_MD; } else { WriteLog (lsINFO, SHAMapNode) << "Unknown node prefix " << std::hex << prefix << std::dec; throw std::runtime_error ("invalid node prefix"); } } else { assert (false); throw std::runtime_error ("Unknown format"); } if (hashValid) { mHash = hash; #if RIPPLE_VERIFY_NODEOBJECT_KEYS updateHash (); assert (mHash == hash); #endif } else updateHash (); }
Json::Value ValidatorList::getJson() const { Json::Value res(Json::objectValue); boost::shared_lock<boost::shared_mutex> read_lock{mutex_}; res[jss::validation_quorum] = static_cast<Json::UInt>(quorum()); if (auto when = expires()) { if (*when == TimeKeeper::time_point::max()) res[jss::validator_list_expires] = "never"; else res[jss::validator_list_expires] = to_string(*when); } else res[jss::validator_list_expires] = "unknown"; // Local static keys PublicKey local; Json::Value& jLocalStaticKeys = (res[jss::local_static_keys] = Json::arrayValue); auto it = publisherLists_.find(local); if (it != publisherLists_.end()) { for (auto const& key : it->second.list) jLocalStaticKeys.append( toBase58(TokenType::TOKEN_NODE_PUBLIC, key)); } // Publisher lists Json::Value& jPublisherLists = (res[jss::publisher_lists] = Json::arrayValue); for (auto const& p : publisherLists_) { if(local == p.first) continue; Json::Value& curr = jPublisherLists.append(Json::objectValue); curr[jss::pubkey_publisher] = strHex(p.first); curr[jss::available] = p.second.available; if(p.second.expiration != TimeKeeper::time_point{}) { curr[jss::seq] = static_cast<Json::UInt>(p.second.sequence); curr[jss::expiration] = to_string(p.second.expiration); curr[jss::version] = requiredListVersion; } Json::Value& keys = (curr[jss::list] = Json::arrayValue); for (auto const& key : p.second.list) { keys.append(toBase58(TokenType::TOKEN_NODE_PUBLIC, key)); } } // Trusted validator keys Json::Value& jValidatorKeys = (res[jss::trusted_validator_keys] = Json::arrayValue); for (auto const& k : trustedKeys_) { jValidatorKeys.append(toBase58(TokenType::TOKEN_NODE_PUBLIC, k)); } // signing keys Json::Value& jSigningKeys = (res[jss::signing_keys] = Json::objectValue); validatorManifests_.for_each_manifest( [&jSigningKeys, this](Manifest const& manifest) { auto it = keyListings_.find(manifest.masterKey); if (it != keyListings_.end()) { jSigningKeys[toBase58( TokenType::TOKEN_NODE_PUBLIC, manifest.masterKey)] = toBase58(TokenType::TOKEN_NODE_PUBLIC, manifest.signingKey); } }); return res; }
// VFALCO TODO This function should take a reference to the params, modify it // as needed, and then there should be a separate function to // submit the transaction. // Json::Value transactionSign ( Json::Value params, bool bSubmit, bool bFailHard, RPCDetail::LedgerFacade& ledgerFacade, Role role) { Json::Value jvResult; WriteLog (lsDEBUG, RPCHandler) << "transactionSign: " << params; if (! params.isMember ("secret")) return RPC::missing_field_error ("secret"); if (! params.isMember ("tx_json")) return RPC::missing_field_error ("tx_json"); RippleAddress naSeed; if (! naSeed.setSeedGeneric (params["secret"].asString ())) return RPC::make_error (rpcBAD_SEED, RPC::invalid_field_message ("secret")); Json::Value& tx_json (params ["tx_json"]); if (! tx_json.isObject ()) return RPC::object_field_error ("tx_json"); if (! tx_json.isMember ("TransactionType")) return RPC::missing_field_error ("tx_json.TransactionType"); std::string const sType = tx_json ["TransactionType"].asString (); if (! tx_json.isMember ("Account")) return RPC::make_error (rpcSRC_ACT_MISSING, RPC::missing_field_message ("tx_json.Account")); RippleAddress raSrcAddressID; if (! raSrcAddressID.setAccountID (tx_json["Account"].asString ())) return RPC::make_error (rpcSRC_ACT_MALFORMED, RPC::invalid_field_message ("tx_json.Account")); bool const verify = !(params.isMember ("offline") && params["offline"].asBool ()); if (!tx_json.isMember ("Sequence") && !verify) return RPC::missing_field_error ("tx_json.Sequence"); // Check for current ledger. if (verify && !getConfig ().RUN_STANDALONE && (ledgerFacade.getValidatedLedgerAge () > 120)) return rpcError (rpcNO_CURRENT); // Check for load. if (ledgerFacade.isLoadedCluster () && (role != Role::ADMIN)) return rpcError (rpcTOO_BUSY); ledgerFacade.snapshotAccountState (raSrcAddressID); if (verify) { if (!ledgerFacade.isValidAccount ()) { // If not offline and did not find account, error. WriteLog (lsDEBUG, RPCHandler) << "transactionSign: Failed to find source account " << "in current ledger: " << raSrcAddressID.humanAccountID (); return rpcError (rpcSRC_ACT_NOT_FOUND); } } autofill_fee (params, ledgerFacade, jvResult, role == Role::ADMIN); if (RPC::contains_error (jvResult)) return jvResult; if ("Payment" == sType) { auto e = signPayment( params, tx_json, raSrcAddressID, ledgerFacade, role); if (contains_error(e)) return e; } if (!tx_json.isMember ("Sequence")) tx_json["Sequence"] = ledgerFacade.getSeq (); if (!tx_json.isMember ("Flags")) tx_json["Flags"] = tfFullyCanonicalSig; if (verify) { if (!ledgerFacade.hasAccountRoot ()) // XXX Ignore transactions for accounts not created. return rpcError (rpcSRC_ACT_NOT_FOUND); } RippleAddress secret = RippleAddress::createSeedGeneric ( params["secret"].asString ()); RippleAddress masterGenerator = RippleAddress::createGeneratorPublic ( secret); RippleAddress masterAccountPublic = RippleAddress::createAccountPublic ( masterGenerator, 0); if (verify) { WriteLog (lsTRACE, RPCHandler) << "verify: " << masterAccountPublic.humanAccountID () << " : " << raSrcAddressID.humanAccountID (); auto const secretAccountID = masterAccountPublic.getAccountID(); if (raSrcAddressID.getAccountID () == secretAccountID) { if (ledgerFacade.accountMasterDisabled ()) return rpcError (rpcMASTER_DISABLED); } else if (!ledgerFacade.accountMatchesRegularKey (secretAccountID)) { return rpcError (rpcBAD_SECRET); } } STParsedJSONObject parsed ("tx_json", tx_json); if (!parsed.object.get()) { jvResult ["error"] = parsed.error ["error"]; jvResult ["error_code"] = parsed.error ["error_code"]; jvResult ["error_message"] = parsed.error ["error_message"]; return jvResult; } std::unique_ptr<STObject> sopTrans = std::move(parsed.object); sopTrans->setFieldVL ( sfSigningPubKey, masterAccountPublic.getAccountPublic ()); STTx::pointer stpTrans; try { stpTrans = std::make_shared<STTx> (*sopTrans); //WriteLog(lsINFO, RPCHandler) << "radar: before sign " << stpTrans->getFieldAmount(sfAmount); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } std::string reason; if (!passesLocalChecks (*stpTrans, reason)) return RPC::make_error (rpcINVALID_PARAMS, reason); if (params.isMember ("debug_signing")) { jvResult["tx_unsigned"] = strHex ( stpTrans->getSerializer ().peekData ()); jvResult["tx_signing_hash"] = to_string (stpTrans->getSigningHash ()); } // FIXME: For performance, transactions should not be signed in this code // path. RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate ( masterGenerator, secret, 0); stpTrans->sign (naAccountPrivate); Transaction::pointer tpTrans; try { //WriteLog(lsINFO, RPCHandler) << "radar: after sign " << stpTrans->getFieldAmount(sfAmount); tpTrans = std::make_shared<Transaction>(stpTrans, Validate::NO); //WriteLog(lsINFO, RPCHandler) << "radar: after copy" << tpTrans->getSTransaction()->getFieldAmount(sfAmount); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } try { // FIXME: For performance, should use asynch interface. tpTrans = ledgerFacade.submitTransactionSync (tpTrans, role == Role::ADMIN, true, bFailHard, bSubmit); if (!tpTrans) { return RPC::make_error (rpcINTERNAL, "Unable to sterilize transaction."); } } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction submission."); } try { jvResult["tx_json"] = tpTrans->getJson (0); jvResult["tx_blob"] = strHex ( tpTrans->getSTransaction ()->getSerializer ().peekData ()); if (temUNCERTAIN != tpTrans->getResult ()) { std::string sToken; std::string sHuman; transResultInfo (tpTrans->getResult (), sToken, sHuman); jvResult["engine_result"] = sToken; jvResult["engine_result_code"] = tpTrans->getResult (); jvResult["engine_result_message"] = sHuman; } return jvResult; } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during JSON handling."); } }
// Get state nodes from a ledger // Inputs: // limit: integer, maximum number of entries // marker: opaque, resume point // binary: boolean, format // Outputs: // ledger_hash: chosen ledger's hash // ledger_index: chosen ledger's index // state: array of state nodes // marker: resume point, if any Json::Value doLedgerData (RPC::Context& context) { context.lock_.unlock (); int const BINARY_PAGE_LENGTH = 256; int const JSON_PAGE_LENGTH = 2048; Ledger::pointer lpLedger; Json::Value jvResult = RPC::lookupLedger (context.params_, lpLedger, context.netOps_); if (!lpLedger) return jvResult; uint256 resumePoint; if (context.params_.isMember ("marker")) { Json::Value const& jMarker = context.params_["marker"]; if (!jMarker.isString ()) return RPC::expected_field_error ("marker", "valid"); if (!resumePoint.SetHex (jMarker.asString ())) return RPC::expected_field_error ("marker", "valid"); } bool isBinary = false; if (context.params_.isMember ("binary")) { Json::Value const& jBinary = context.params_["binary"]; if (!jBinary.isBool ()) return RPC::expected_field_error ("binary", "bool"); isBinary = jBinary.asBool (); } int limit = -1; int maxLimit = isBinary ? BINARY_PAGE_LENGTH : JSON_PAGE_LENGTH; if (context.params_.isMember ("limit")) { Json::Value const& jLimit = context.params_["limit"]; if (!jLimit.isIntegral ()) return RPC::expected_field_error ("limit", "integer"); limit = jLimit.asInt (); } if ((limit < 0) || ((limit > maxLimit) && (context.role_ != Config::ADMIN))) limit = maxLimit; Json::Value jvReply = Json::objectValue; jvReply["ledger_hash"] = to_string (lpLedger->getHash()); jvReply["ledger_index"] = beast::lexicalCastThrow <std::string> (lpLedger->getLedgerSeq ()); Json::Value& nodes = (jvReply["state"] = Json::arrayValue); SHAMap& map = *(lpLedger->peekAccountStateMap ()); for (;;) { SHAMapItem::pointer item = map.peekNextItem (resumePoint); if (!item) break; resumePoint = item->getTag(); if (limit-- <= 0) { --resumePoint; jvReply["marker"] = to_string (resumePoint); break; } if (isBinary) { Json::Value& entry = nodes.append (Json::objectValue); entry["data"] = strHex (item->peekData().begin(), item->peekData().size()); entry["index"] = to_string (item->getTag ()); } else { SLE sle (item->peekSerializer(), item->getTag ()); Json::Value& entry = nodes.append (sle.getJson (0)); entry["index"] = to_string (item->getTag ()); } } return jvReply; }
// VFALCO TODO This function should take a reference to the params, modify it // as needed, and then there should be a separate function to // submit the transaction // Json::Value transactionSign ( Json::Value params, bool bSubmit, bool bFailHard, Application::ScopedLockType& mlh, NetworkOPs& netOps, int role) { Json::Value jvResult; WriteLog (lsDEBUG, RPCHandler) << "transactionSign: " << params; if (! params.isMember ("secret")) return RPC::missing_field_error ("secret"); if (! params.isMember ("tx_json")) return RPC::missing_field_error ("tx_json"); RippleAddress naSeed; if (! naSeed.setSeedGeneric (params["secret"].asString ())) return RPC::make_error (rpcBAD_SEED, RPC::invalid_field_message ("secret")); Json::Value& tx_json (params ["tx_json"]); if (! tx_json.isObject ()) return RPC::object_field_error ("tx_json"); if (! tx_json.isMember ("TransactionType")) return RPC::missing_field_error ("tx_json.TransactionType"); std::string const sType = tx_json ["TransactionType"].asString (); if (! tx_json.isMember ("Account")) return RPC::make_error (rpcSRC_ACT_MISSING, RPC::missing_field_message ("tx_json.Account")); RippleAddress raSrcAddressID; if (! raSrcAddressID.setAccountID (tx_json["Account"].asString ())) return RPC::make_error (rpcSRC_ACT_MALFORMED, RPC::invalid_field_message ("tx_json.Account")); bool const verify = !(params.isMember ("offline") && params["offline"].asBool ()); if (!tx_json.isMember ("Sequence") && !verify) return RPC::missing_field_error ("tx_json.Sequence"); // Check for current ledger if (verify && !getConfig ().RUN_STANDALONE && (getApp().getLedgerMaster().getValidatedLedgerAge() > 120)) return rpcError (rpcNO_CURRENT); // Check for load if (getApp().getFeeTrack().isLoadedCluster() && (role != Config::ADMIN)) return rpcError(rpcTOO_BUSY); Ledger::pointer lSnapshot = netOps.getCurrentLedger (); AccountState::pointer asSrc; if (verify) { asSrc = netOps.getAccountState (lSnapshot, raSrcAddressID); if (!asSrc) { // If not offline and did not find account, error. WriteLog (lsDEBUG, RPCHandler) << "transactionSign: Failed to find source account in current ledger: " << raSrcAddressID.humanAccountID (); return rpcError (rpcSRC_ACT_NOT_FOUND); } } autofill_fee (params, lSnapshot, jvResult, role == Config::ADMIN); if (RPC::contains_error (jvResult)) return jvResult; if ("Payment" == sType) { auto e = signPayment(params, tx_json, raSrcAddressID, lSnapshot, role); if (contains_error(e)) return e; } if ("Genesis" == sType) { auto e = signGenesis(params, tx_json, raSrcAddressID, lSnapshot, role); if (contains_error(e)) return e; } if ("Transfer" == sType) { auto e = signTransfer(params, tx_json, raSrcAddressID, lSnapshot, role); if (contains_error(e)) return e; } if ("AccountCreate" == sType) { auto e = signAccountCreate(params, tx_json, raSrcAddressID, lSnapshot, role); if (contains_error(e)) return e; } if (!tx_json.isMember ("Fee")) { auto const& transactionType = tx_json["TransactionType"].asString (); if ("AccountSet" == transactionType || "OfferCreate" == transactionType || "OfferCancel" == transactionType || "TrustSet" == transactionType) { tx_json["Fee"] = (int) getConfig ().FEE_DEFAULT; } } if (!tx_json.isMember ("Sequence")) tx_json["Sequence"] = asSrc->getSeq (); if (!tx_json.isMember ("Flags")) tx_json["Flags"] = tfFullyCanonicalSig; if (verify) { SLE::pointer sleAccountRoot = netOps.getSLEi (lSnapshot, Ledger::getAccountRootIndex (raSrcAddressID.getAccountID ())); if (!sleAccountRoot) // XXX Ignore transactions for accounts not created. return rpcError (rpcSRC_ACT_NOT_FOUND); } RippleAddress naSecret = RippleAddress::createSeedGeneric (params["secret"].asString ()); RippleAddress masterAccountPublic = RippleAddress::createAccountPublic(naSecret); if (verify) { auto account = masterAccountPublic.getAccountID(); auto const& sle = asSrc->peekSLE(); WriteLog (lsWARNING, RPCHandler) << "verify: " << masterAccountPublic.humanAccountID () << " : " << raSrcAddressID.humanAccountID (); if (raSrcAddressID.getAccountID () == account) { if (sle.isFlag(lsfDisableMaster) && "Inflation" != sType) return rpcError (rpcMASTER_DISABLED); } else if (!sle.isFieldPresent(sfRegularKey) || account != sle.getFieldAccount160 (sfRegularKey)) { return rpcError (rpcBAD_SECRET); } } STParsedJSON parsed ("tx_json", tx_json); if (!parsed.object.get()) { jvResult ["error"] = parsed.error ["error"]; jvResult ["error_code"] = parsed.error ["error_code"]; jvResult ["error_message"] = parsed.error ["error_message"]; return jvResult; } std::unique_ptr<STObject> sopTrans = std::move(parsed.object); sopTrans->setFieldVL (sfSigningPubKey, masterAccountPublic.getAccountPublic ()); SerializedTransaction::pointer stpTrans; try { stpTrans = boost::make_shared<SerializedTransaction> (*sopTrans); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } std::string reason; if (!passesLocalChecks (*stpTrans, reason)) return RPC::make_error (rpcINVALID_PARAMS, reason); if (params.isMember ("debug_signing")) { jvResult["tx_unsigned"] = strHex ( stpTrans->getSerializer ().peekData ()); jvResult["tx_signing_hash"] = to_string (stpTrans->getSigningHash ()); } // FIXME: For performance, transactions should not be signed in this code path. RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate (naSecret); stpTrans->sign (naAccountPrivate); Transaction::pointer tpTrans; tpTrans = getApp().getMasterTransaction().fetch(stpTrans->getTransactionID(), false); if (tpTrans) { TER res = tpTrans->getResult(); if (!(isTelLocal(res) || isTemMalformed(res) || isTefFailure(res))) { tpTrans = Transaction::pointer(); } } if (!tpTrans) { try { tpTrans = boost::make_shared<Transaction> (stpTrans, false); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } try { // FIXME: For performance, should use asynch interface tpTrans = netOps.submitTransactionSync (tpTrans, role == Config::ADMIN, true, bFailHard, bSubmit); if (!tpTrans) { return RPC::make_error (rpcINTERNAL, "Unable to sterilize transaction."); } } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction submission."); } } try { jvResult["tx_json"] = tpTrans->getJson (0); jvResult["tx_blob"] = strHex ( tpTrans->getSTransaction ()->getSerializer ().peekData ()); if (temUNCERTAIN != tpTrans->getResult ()) { std::string sToken; std::string sHuman; transResultInfo (tpTrans->getResult (), sToken, sHuman); jvResult["engine_result"] = sToken; jvResult["engine_result_code"] = tpTrans->getResult (); jvResult["engine_result_message"] = sHuman; } return jvResult; } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during JSON handling."); } }
// { // ledger_hash : <ledger> // ledger_index : <ledger_index> // ... // } Json::Value RPCHandler::doLedgerEntry (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder) { masterLockHolder.unlock (); Ledger::pointer lpLedger; Json::Value jvResult = RPC::lookupLedger (params, lpLedger, *mNetOps); if (!lpLedger) return jvResult; uint256 uNodeIndex; bool bNodeBinary = false; if (params.isMember ("index")) { // XXX Needs to provide proof. uNodeIndex.SetHex (params["index"].asString ()); bNodeBinary = true; } else if (params.isMember ("account_root")) { RippleAddress naAccount; if (!naAccount.setAccountID (params["account_root"].asString ()) || !naAccount.getAccountID ()) { jvResult["error"] = "malformedAddress"; } else { uNodeIndex = Ledger::getAccountRootIndex (naAccount.getAccountID ()); } } else if (params.isMember ("directory")) { if (!params["directory"].isObject ()) { uNodeIndex.SetHex (params["directory"].asString ()); } else if (params["directory"].isMember ("sub_index") && !params["directory"]["sub_index"].isIntegral ()) { jvResult["error"] = "malformedRequest"; } else { std::uint64_t uSubIndex = params["directory"].isMember ("sub_index") ? params["directory"]["sub_index"].asUInt () : 0; if (params["directory"].isMember ("dir_root")) { uint256 uDirRoot; uDirRoot.SetHex (params["dir_root"].asString ()); uNodeIndex = Ledger::getDirNodeIndex (uDirRoot, uSubIndex); } else if (params["directory"].isMember ("owner")) { RippleAddress naOwnerID; if (!naOwnerID.setAccountID (params["directory"]["owner"].asString ())) { jvResult["error"] = "malformedAddress"; } else { uint256 uDirRoot = Ledger::getOwnerDirIndex (naOwnerID.getAccountID ()); uNodeIndex = Ledger::getDirNodeIndex (uDirRoot, uSubIndex); } } else { jvResult["error"] = "malformedRequest"; } } } else if (params.isMember ("generator")) { RippleAddress naGeneratorID; if (!params["generator"].isObject ()) { uNodeIndex.SetHex (params["generator"].asString ()); } else if (!params["generator"].isMember ("regular_seed")) { jvResult["error"] = "malformedRequest"; } else if (!naGeneratorID.setSeedGeneric (params["generator"]["regular_seed"].asString ())) { jvResult["error"] = "malformedAddress"; } else { RippleAddress na0Public; // To find the generator's index. RippleAddress naGenerator = RippleAddress::createGeneratorPublic (naGeneratorID); na0Public.setAccountPublic (naGenerator, 0); uNodeIndex = Ledger::getGeneratorIndex (na0Public.getAccountID ()); } } else if (params.isMember ("offer")) { RippleAddress naAccountID; if (!params["offer"].isObject ()) { uNodeIndex.SetHex (params["offer"].asString ()); } else if (!params["offer"].isMember ("account") || !params["offer"].isMember ("seq") || !params["offer"]["seq"].isIntegral ()) { jvResult["error"] = "malformedRequest"; } else if (!naAccountID.setAccountID (params["offer"]["account"].asString ())) { jvResult["error"] = "malformedAddress"; } else { std::uint32_t uSequence = params["offer"]["seq"].asUInt (); uNodeIndex = Ledger::getOfferIndex (naAccountID.getAccountID (), uSequence); } } else if (params.isMember ("ripple_state")) { RippleAddress naA; RippleAddress naB; uint160 uCurrency; Json::Value jvRippleState = params["ripple_state"]; if (!jvRippleState.isObject () || !jvRippleState.isMember ("currency") || !jvRippleState.isMember ("accounts") || !jvRippleState["accounts"].isArray () || 2 != jvRippleState["accounts"].size () || !jvRippleState["accounts"][0u].isString () || !jvRippleState["accounts"][1u].isString () || jvRippleState["accounts"][0u].asString () == jvRippleState["accounts"][1u].asString () ) { jvResult["error"] = "malformedRequest"; } else if (!naA.setAccountID (jvRippleState["accounts"][0u].asString ()) || !naB.setAccountID (jvRippleState["accounts"][1u].asString ())) { jvResult["error"] = "malformedAddress"; } else if (!STAmount::currencyFromString (uCurrency, jvRippleState["currency"].asString ())) { jvResult["error"] = "malformedCurrency"; } else { uNodeIndex = Ledger::getRippleStateIndex (naA, naB, uCurrency); } } else { jvResult["error"] = "unknownOption"; } if (uNodeIndex.isNonZero ()) { SLE::pointer sleNode = mNetOps->getSLEi (lpLedger, uNodeIndex); if (params.isMember("binary")) bNodeBinary = params["binary"].asBool(); if (!sleNode) { // Not found. // XXX Should also provide proof. jvResult["error"] = "entryNotFound"; } else if (bNodeBinary) { // XXX Should also provide proof. Serializer s; sleNode->add (s); jvResult["node_binary"] = strHex (s.peekData ()); jvResult["index"] = uNodeIndex.ToString (); } else { jvResult["node"] = sleNode->getJson (0); jvResult["index"] = uNodeIndex.ToString (); } } return jvResult; }
Json::Value walletPropose (Json::Value const& params) { boost::optional<KeyType> keyType; boost::optional<Seed> seed; bool rippleLibSeed = false; if (params.isMember (jss::key_type)) { if (! params[jss::key_type].isString()) { return RPC::expected_field_error ( jss::key_type, "string"); } keyType = keyTypeFromString ( params[jss::key_type].asString()); if (!keyType) return rpcError(rpcINVALID_PARAMS); } // ripple-lib encodes seed used to generate an Ed25519 wallet in a // non-standard way. While we never encode seeds that way, we try // to detect such keys to avoid user confusion. { if (params.isMember(jss::passphrase)) seed = RPC::parseRippleLibSeed(params[jss::passphrase]); else if (params.isMember(jss::seed)) seed = RPC::parseRippleLibSeed(params[jss::seed]); if(seed) { rippleLibSeed = true; // If the user *explicitly* requests a key type other than // Ed25519 we return an error. if (keyType.value_or(KeyType::ed25519) != KeyType::ed25519) return rpcError(rpcBAD_SEED); keyType = KeyType::ed25519; } } if (!seed) { if (params.isMember(jss::passphrase) || params.isMember(jss::seed) || params.isMember(jss::seed_hex)) { Json::Value err; seed = RPC::getSeedFromRPC(params, err); if (!seed) return err; } else { seed = randomSeed(); } } if (!keyType) keyType = KeyType::secp256k1; auto const publicKey = generateKeyPair (*keyType, *seed).first; Json::Value obj (Json::objectValue); auto const seed1751 = seedAs1751 (*seed); auto const seedHex = strHex (*seed); auto const seedBase58 = toBase58 (*seed); obj[jss::master_seed] = seedBase58; obj[jss::master_seed_hex] = seedHex; obj[jss::master_key] = seed1751; obj[jss::account_id] = toBase58(calcAccountID(publicKey)); obj[jss::public_key] = toBase58(TokenType::AccountPublic, publicKey); obj[jss::key_type] = to_string (*keyType); obj[jss::public_key_hex] = strHex (publicKey); // If a passphrase was specified, and it was hashed and used as a seed // run a quick entropy check and add an appropriate warning, because // "brain wallets" can be easily attacked. if (!rippleLibSeed && params.isMember (jss::passphrase)) { auto const passphrase = params[jss::passphrase].asString(); if (passphrase != seed1751 && passphrase != seedBase58 && passphrase != seedHex) { // 80 bits of entropy isn't bad, but it's better to // err on the side of caution and be conservative. if (estimate_entropy (passphrase) < 80.0) obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase that has low entropy and is vulnerable " "to brute-force attacks."; else obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase. It may be vulnerable to brute-force " "attacks."; } } return obj; }
Json::Value walletPropose (Json::Value const& params) { boost::optional<Seed> seed; KeyType keyType = KeyType::secp256k1; if (params.isMember (jss::key_type)) { if (! params[jss::key_type].isString()) { return RPC::expected_field_error ( jss::key_type, "string"); } keyType = keyTypeFromString ( params[jss::key_type].asString()); if (keyType == KeyType::invalid) return rpcError(rpcINVALID_PARAMS); } if (params.isMember (jss::passphrase) || params.isMember (jss::seed) || params.isMember (jss::seed_hex)) { Json::Value err; seed = RPC::getSeedFromRPC (params, err); if (!seed) return err; } else { seed = randomSeed (); } auto const publicKey = generateKeyPair (keyType, *seed).first; Json::Value obj (Json::objectValue); obj[jss::master_seed] = toBase58 (*seed); obj[jss::master_seed_hex] = strHex (seed->data(), seed->size()); obj[jss::master_key] = seedAs1751 (*seed); obj[jss::account_id] = toBase58(calcAccountID(publicKey)); obj[jss::public_key] = toBase58(TOKEN_ACCOUNT_PUBLIC, publicKey); obj[jss::key_type] = to_string (keyType); obj[jss::public_key_hex] = strHex (publicKey.data(), publicKey.size()); if (params.isMember (jss::passphrase)) { auto const entropy = estimate_entropy ( params[jss::passphrase].asString()); // 80 bits of entropy isn't bad, but it's better to // err on the side of caution and be conservative. if (entropy < 80.0) obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase that has low entropy and is vulnerable " "to brute-force attacks."; else obj[jss::warning] = "This wallet was generated using a user-supplied " "passphrase. It may be vulnerable to brute-force " "attacks."; } return obj; }
Json::Value STUInt64::getJson (int) const { return strHex (value_); }
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; }
// { // 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; } }
Json::Value walletPropose (Json::Value const& params) { DivvyAddress naSeed; DivvyAddress naAccount; KeyType type = KeyType::secp256k1; bool const has_key_type = params.isMember (jss::key_type); bool const has_passphrase = params.isMember (jss::passphrase); if (has_key_type) { // `key_type` must be valid if present. type = keyTypeFromString (params[jss::key_type].asString()); if (type == KeyType::invalid) { return rpcError (rpcBAD_SEED); } naSeed = getSeedFromRPC (params); } else if (has_passphrase) { naSeed.setSeedGeneric (params[jss::passphrase].asString()); } else { naSeed.setSeedRandom(); } if (!naSeed.isSet()) { return rpcError(rpcBAD_SEED); } if (type == KeyType::secp256k1) { DivvyAddress naGenerator = DivvyAddress::createGeneratorPublic (naSeed); naAccount.setAccountPublic (naGenerator, 0); } else if (type == KeyType::ed25519) { uint256 secretkey = keyFromSeed (naSeed.getSeed()); Blob publickey (33); publickey[0] = 0xED; ed25519_publickey (secretkey.data(), &publickey[1]); secretkey.zero(); // security erase naAccount.setAccountPublic (publickey); } else { assert (false); // not reached } Json::Value obj (Json::objectValue); obj[jss::master_seed] = naSeed.humanSeed (); obj[jss::master_seed_hex] = to_string (naSeed.getSeed ()); obj[jss::master_key] = naSeed.humanSeed1751(); obj[jss::account_id] = naAccount.humanAccountID (); obj[jss::public_key] = naAccount.humanAccountPublic(); obj[jss::key_type] = to_string (type); auto acct = naAccount.getAccountPublic(); obj[jss::public_key_hex] = strHex(acct.begin(), acct.size()); return obj; }