mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
@@ -301,14 +301,28 @@ Json::Value RPCParser::parseRipplePathFind(const Json::Value& jvParams)
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
// submit any transaction to the network
|
||||
// sign/submit any transaction to the network
|
||||
//
|
||||
// sign private_key json
|
||||
// submit private_key json
|
||||
Json::Value RPCParser::parseSubmit(const Json::Value& jvParams)
|
||||
// submit tx_blob
|
||||
Json::Value RPCParser::parseSignSubmit(const Json::Value& jvParams)
|
||||
{
|
||||
Json::Value txJSON;
|
||||
Json::Reader reader;
|
||||
|
||||
if (reader.parse(jvParams[1u].asString(), txJSON))
|
||||
if (1 == jvParams.size())
|
||||
{
|
||||
// Submitting tx_blob
|
||||
|
||||
Json::Value jvRequest;
|
||||
|
||||
jvRequest["tx_blob"] = jvParams[0u].asString();
|
||||
|
||||
return jvRequest;
|
||||
}
|
||||
// Submitting tx_json.
|
||||
else if (reader.parse(jvParams[1u].asString(), txJSON))
|
||||
{
|
||||
Json::Value jvRequest;
|
||||
|
||||
@@ -475,7 +489,8 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
|
||||
// { "profile", &RPCParser::parseProfile, 1, 9 },
|
||||
{ "random", &RPCParser::parseAsIs, 0, 0 },
|
||||
{ "ripple_path_find", &RPCParser::parseRipplePathFind, 1, 1 },
|
||||
{ "submit", &RPCParser::parseSubmit, 2, 2 },
|
||||
{ "sign", &RPCParser::parseSignSubmit, 2, 2 },
|
||||
{ "submit", &RPCParser::parseSignSubmit, 1, 2 },
|
||||
{ "server_info", &RPCParser::parseAsIs, 0, 0 },
|
||||
{ "stop", &RPCParser::parseAsIs, 0, 0 },
|
||||
// { "transaction_entry", &RPCParser::parseTransactionEntry, -1, -1 },
|
||||
|
||||
@@ -31,7 +31,7 @@ protected:
|
||||
Json::Value parseOwnerInfo(const Json::Value& jvParams);
|
||||
Json::Value parseRandom(const Json::Value& jvParams);
|
||||
Json::Value parseRipplePathFind(const Json::Value& jvParams);
|
||||
Json::Value parseSubmit(const Json::Value& jvParams);
|
||||
Json::Value parseSignSubmit(const Json::Value& jvParams);
|
||||
Json::Value parseTx(const Json::Value& jvParams);
|
||||
Json::Value parseTxHistory(const Json::Value& jvParams);
|
||||
Json::Value parseUnlAdd(const Json::Value& jvParams);
|
||||
|
||||
@@ -165,7 +165,7 @@ void NetworkOPs::submitTransaction(Job&, SerializedTransaction::pointer iTrans,
|
||||
|
||||
// Sterilize transaction through serialization.
|
||||
// This is fully synchronous and deprecated
|
||||
Transaction::pointer NetworkOPs::submitTransactionSync(const Transaction::pointer& tpTrans)
|
||||
Transaction::pointer NetworkOPs::submitTransactionSync(const Transaction::pointer& tpTrans, bool bSubmit)
|
||||
{
|
||||
Serializer s;
|
||||
tpTrans->getSTransaction()->add(s);
|
||||
@@ -179,7 +179,8 @@ Transaction::pointer NetworkOPs::submitTransactionSync(const Transaction::pointe
|
||||
}
|
||||
else if (tpTransNew->getSTransaction()->isEquivalent(*tpTrans->getSTransaction()))
|
||||
{
|
||||
(void) NetworkOPs::processTransaction(tpTransNew);
|
||||
if (bSubmit)
|
||||
(void) NetworkOPs::processTransaction(tpTransNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1127,7 +1128,17 @@ Json::Value NetworkOPs::getServerInfo()
|
||||
if (mConsensus)
|
||||
info["consensus"] = mConsensus->getJson();
|
||||
|
||||
info["load"] = theApp->getJobQueue().getJson();
|
||||
info["load"] = theApp->getJobQueue().getJson();
|
||||
info["load_base"] = theApp->getFeeTrack().getLoadBase();
|
||||
info["load_fee"] = theApp->getFeeTrack().getLoadFactor();
|
||||
|
||||
Ledger::pointer lpClosed = getClosedLedger();
|
||||
|
||||
if (lpClosed)
|
||||
{
|
||||
info["reserve_base"] = Json::UInt(lpClosed->getReserve(0));
|
||||
info["reserve_inc"] = Json::UInt(lpClosed->getReserveInc());
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -1468,15 +1479,16 @@ void NetworkOPs::unsubAccountChanges(InfoSub* ispListener)
|
||||
// <-- bool: true=added, false=already there
|
||||
bool NetworkOPs::subLedger(InfoSub* ispListener, Json::Value& jvResult)
|
||||
{
|
||||
Ledger::pointer closedLgr = getClosedLedger();
|
||||
jvResult["ledger_index"] = closedLgr->getLedgerSeq();
|
||||
jvResult["ledger_hash"] = closedLgr->getHash().ToString();
|
||||
jvResult["ledger_time"] = Json::Value::UInt(utFromSeconds(closedLgr->getCloseTimeNC()));
|
||||
Ledger::pointer lpClosed = getClosedLedger();
|
||||
|
||||
jvResult["fee_ref"] = Json::UInt(closedLgr->getReferenceFeeUnits());
|
||||
jvResult["fee_base"] = Json::UInt(closedLgr->getBaseFee());
|
||||
jvResult["reserve_base"] = Json::UInt(closedLgr->getReserve(0));
|
||||
jvResult["reserve_inc"] = Json::UInt(closedLgr->getReserveInc());
|
||||
jvResult["ledger_index"] = lpClosed->getLedgerSeq();
|
||||
jvResult["ledger_hash"] = lpClosed->getHash().ToString();
|
||||
jvResult["ledger_time"] = Json::Value::UInt(utFromSeconds(lpClosed->getCloseTimeNC()));
|
||||
|
||||
jvResult["fee_ref"] = Json::UInt(lpClosed->getReferenceFeeUnits());
|
||||
jvResult["fee_base"] = Json::UInt(lpClosed->getBaseFee());
|
||||
jvResult["reserve_base"] = Json::UInt(lpClosed->getReserve(0));
|
||||
jvResult["reserve_inc"] = Json::UInt(lpClosed->getReserveInc());
|
||||
|
||||
return mSubLedger.insert(ispListener).second;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public:
|
||||
//
|
||||
typedef boost::function<void (Transaction::pointer, TER)> stCallback; // must complete immediately
|
||||
void submitTransaction(Job&, SerializedTransaction::pointer, stCallback callback = stCallback());
|
||||
Transaction::pointer submitTransactionSync(const Transaction::pointer& tpTrans);
|
||||
Transaction::pointer submitTransactionSync(const Transaction::pointer& tpTrans, bool bSubmit=true);
|
||||
|
||||
void runTransactionQueue();
|
||||
Transaction::pointer processTransaction(Transaction::pointer, stCallback);
|
||||
|
||||
@@ -18,6 +18,7 @@ Json::Value rpcError(int iError, Json::Value jvResult)
|
||||
{ rpcACT_EXISTS, "actExists", "Account already exists." },
|
||||
{ rpcACT_MALFORMED, "actMalformed", "Account malformed." },
|
||||
{ rpcACT_NOT_FOUND, "actNotFound", "Account not found." },
|
||||
{ rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." },
|
||||
{ rpcBAD_SEED, "badSeed", "Disallowed seed." },
|
||||
{ rpcBAD_SYNTAX, "badSyntax", "Syntax error." },
|
||||
{ rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." },
|
||||
|
||||
@@ -43,6 +43,7 @@ enum {
|
||||
// Bad parameter
|
||||
rpcACT_MALFORMED,
|
||||
rpcQUALITY_MALFORMED,
|
||||
rpcBAD_BLOB,
|
||||
rpcBAD_SEED,
|
||||
rpcDST_ACT_MALFORMED,
|
||||
rpcDST_ACT_MISSING,
|
||||
|
||||
@@ -37,6 +37,292 @@ RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub* infoSub)
|
||||
mInfoSub = infoSub;
|
||||
}
|
||||
|
||||
Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit)
|
||||
{
|
||||
Json::Value jvResult;
|
||||
RippleAddress naSeed;
|
||||
RippleAddress raSrcAddressID;
|
||||
|
||||
cLog(lsDEBUG)
|
||||
<< boost::str(boost::format("transactionSign: %s")
|
||||
% jvRequest);
|
||||
|
||||
if (!jvRequest.isMember("secret") || !jvRequest.isMember("tx_json"))
|
||||
{
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
Json::Value txJSON = jvRequest["tx_json"];
|
||||
|
||||
if (!txJSON.isObject())
|
||||
{
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
if (!naSeed.setSeedGeneric(jvRequest["secret"].asString()))
|
||||
{
|
||||
return rpcError(rpcBAD_SEED);
|
||||
}
|
||||
if (!txJSON.isMember("Account"))
|
||||
{
|
||||
return rpcError(rpcSRC_ACT_MISSING);
|
||||
}
|
||||
if (!raSrcAddressID.setAccountID(txJSON["Account"].asString()))
|
||||
{
|
||||
return rpcError(rpcSRC_ACT_MALFORMED);
|
||||
}
|
||||
if (!txJSON.isMember("TransactionType"))
|
||||
{
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
AccountState::pointer asSrc = mNetOps->getAccountState(mNetOps->getCurrentLedger(), raSrcAddressID);
|
||||
if (!asSrc)
|
||||
{
|
||||
cLog(lsDEBUG) << boost::str(boost::format("transactionSign: Failed to find source account in current ledger: %s")
|
||||
% raSrcAddressID.humanAccountID());
|
||||
|
||||
return rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ("Payment" == txJSON["TransactionType"].asString())
|
||||
{
|
||||
|
||||
RippleAddress dstAccountID;
|
||||
|
||||
if (!txJSON.isMember("Destination"))
|
||||
{
|
||||
return rpcError(rpcDST_ACT_MISSING);
|
||||
}
|
||||
if (!dstAccountID.setAccountID(txJSON["Destination"].asString()))
|
||||
{
|
||||
return rpcError(rpcDST_ACT_MALFORMED);
|
||||
}
|
||||
|
||||
if (!txJSON.isMember("Fee"))
|
||||
{
|
||||
txJSON["Fee"] = (int) theConfig.FEE_DEFAULT;
|
||||
}
|
||||
|
||||
if (txJSON.isMember("Paths") && jvRequest.isMember("build_path"))
|
||||
{
|
||||
// Asking to build a path when providing one is an error.
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
if (!txJSON.isMember("Paths") && txJSON.isMember("Amount") && jvRequest.isMember("build_path"))
|
||||
{
|
||||
// Need a ripple path.
|
||||
STPathSet spsPaths;
|
||||
uint160 uSrcCurrencyID;
|
||||
uint160 uSrcIssuerID;
|
||||
|
||||
STAmount saSendMax;
|
||||
STAmount saSend;
|
||||
|
||||
if (!txJSON.isMember("Amount") // Amount required.
|
||||
|| !saSend.bSetJson(txJSON["Amount"])) // Must be valid.
|
||||
return rpcError(rpcDST_AMT_MALFORMED);
|
||||
|
||||
if (txJSON.isMember("SendMax"))
|
||||
{
|
||||
if (!saSendMax.bSetJson(txJSON["SendMax"]))
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no SendMax, default to Amount with sender as issuer.
|
||||
saSendMax = saSend;
|
||||
saSendMax.setIssuer(raSrcAddressID.getAccountID());
|
||||
}
|
||||
|
||||
if (saSendMax.isNative() && saSend.isNative())
|
||||
{
|
||||
// Asking to build a path for XRP to XRP is an error.
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
Pathfinder pf(raSrcAddressID, dstAccountID, saSendMax.getCurrency(), saSendMax.getIssuer(), saSend);
|
||||
|
||||
if (!pf.findPaths(5, 3, spsPaths))
|
||||
{
|
||||
cLog(lsDEBUG) << "payment: build_path: No paths found.";
|
||||
|
||||
return rpcError(rpcNO_PATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsDEBUG) << "payment: build_path: " << spsPaths.getJson(0);
|
||||
}
|
||||
|
||||
if (!spsPaths.isEmpty())
|
||||
{
|
||||
txJSON["Paths"]=spsPaths.getJson(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!txJSON.isMember("Fee")
|
||||
&& (
|
||||
"AccountSet" == txJSON["TransactionType"].asString()
|
||||
|| "OfferCreate" == txJSON["TransactionType"].asString()
|
||||
|| "OfferCancel" == txJSON["TransactionType"].asString()
|
||||
|| "TrustSet" == txJSON["TransactionType"].asString()))
|
||||
{
|
||||
txJSON["Fee"] = (int) theConfig.FEE_DEFAULT;
|
||||
}
|
||||
|
||||
if (!txJSON.isMember("Sequence")) txJSON["Sequence"] = asSrc->getSeq();
|
||||
if (!txJSON.isMember("Flags")) txJSON["Flags"] = 0;
|
||||
|
||||
Ledger::pointer lpCurrent = mNetOps->getCurrentLedger();
|
||||
SLE::pointer sleAccountRoot = mNetOps->getSLE(lpCurrent, Ledger::getAccountRootIndex(raSrcAddressID.getAccountID()));
|
||||
|
||||
if (!sleAccountRoot)
|
||||
{
|
||||
// XXX Ignore transactions for accounts not created.
|
||||
return rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
|
||||
bool bHaveAuthKey = false;
|
||||
RippleAddress naAuthorizedPublic;
|
||||
|
||||
RippleAddress naSecret = RippleAddress::createSeedGeneric(jvRequest["secret"].asString());
|
||||
RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naSecret);
|
||||
|
||||
// Find the index of Account from the master generator, so we can generate the public and private keys.
|
||||
RippleAddress naMasterAccountPublic;
|
||||
unsigned int iIndex = 0;
|
||||
bool bFound = false;
|
||||
|
||||
// Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are
|
||||
// related.
|
||||
while (!bFound && iIndex != theConfig.ACCOUNT_PROBE_MAX)
|
||||
{
|
||||
naMasterAccountPublic.setAccountPublic(naMasterGenerator, iIndex);
|
||||
|
||||
cLog(lsWARNING) << "authorize: " << iIndex << " : " << naMasterAccountPublic.humanAccountID() << " : " << raSrcAddressID.humanAccountID();
|
||||
|
||||
bFound = raSrcAddressID.getAccountID() == naMasterAccountPublic.getAccountID();
|
||||
if (!bFound)
|
||||
++iIndex;
|
||||
}
|
||||
|
||||
if (!bFound)
|
||||
{
|
||||
return rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Use the generator to determine the associated public and private keys.
|
||||
RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSecret);
|
||||
RippleAddress naAccountPublic = RippleAddress::createAccountPublic(naGenerator, iIndex);
|
||||
RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate(naGenerator, naSecret, iIndex);
|
||||
|
||||
if (bHaveAuthKey
|
||||
// The generated pair must match authorized...
|
||||
&& naAuthorizedPublic.getAccountID() != naAccountPublic.getAccountID()
|
||||
// ... or the master key must have been used.
|
||||
&& raSrcAddressID.getAccountID() != naAccountPublic.getAccountID())
|
||||
{
|
||||
// std::cerr << "iIndex: " << iIndex << std::endl;
|
||||
// std::cerr << "sfAuthorizedKey: " << strHex(asSrc->getAuthorizedKey().getAccountID()) << std::endl;
|
||||
// std::cerr << "naAccountPublic: " << strHex(naAccountPublic.getAccountID()) << std::endl;
|
||||
|
||||
return rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
|
||||
std::auto_ptr<STObject> sopTrans;
|
||||
|
||||
try
|
||||
{
|
||||
sopTrans = STObject::parseJson(txJSON);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "malformedTransaction";
|
||||
jvResult["error_exception"] = e.what();
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
sopTrans->setFieldVL(sfSigningPubKey, naAccountPublic.getAccountPublic());
|
||||
|
||||
SerializedTransaction::pointer stpTrans;
|
||||
|
||||
try
|
||||
{
|
||||
stpTrans = boost::make_shared<SerializedTransaction>(*sopTrans);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "invalidTransaction";
|
||||
jvResult["error_exception"] = e.what();
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
// FIXME: For performance, transactions should not be signed in this code path.
|
||||
stpTrans->sign(naAccountPrivate);
|
||||
|
||||
Transaction::pointer tpTrans;
|
||||
|
||||
try
|
||||
{
|
||||
tpTrans = boost::make_shared<Transaction>(stpTrans, false);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "internalTransaction";
|
||||
jvResult["error_exception"] = e.what();
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
tpTrans = mNetOps->submitTransactionSync(tpTrans, bSubmit); // FIXME: For performance, should use asynch interface
|
||||
|
||||
if (!tpTrans) {
|
||||
jvResult["error"] = "invalidTransaction";
|
||||
jvResult["error_exception"] = "Unable to sterilize transaction.";
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "internalSubmit";
|
||||
jvResult["error_exception"] = e.what();
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
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& e)
|
||||
{
|
||||
jvResult["error"] = "internalJson";
|
||||
jvResult["error_exception"] = e.what();
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the master public generator for a regular seed so we may index source accounts ids.
|
||||
// --> naRegularSeed
|
||||
// <-- naMasterGenerator
|
||||
@@ -916,234 +1202,52 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest)
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
// {
|
||||
// tx_json: <object>,
|
||||
// secret: <secret>
|
||||
// }
|
||||
Json::Value RPCHandler::doSign(Json::Value jvRequest)
|
||||
{
|
||||
return transactionSign(jvRequest, false);
|
||||
}
|
||||
|
||||
// {
|
||||
// tx_json: <object>,
|
||||
// secret: <secret>
|
||||
// }
|
||||
Json::Value RPCHandler::doSubmit(Json::Value jvRequest)
|
||||
{
|
||||
Json::Value jvResult;
|
||||
RippleAddress naSeed;
|
||||
RippleAddress raSrcAddressID;
|
||||
if (!jvRequest.isMember("tx_blob"))
|
||||
{
|
||||
return transactionSign(jvRequest, true);
|
||||
}
|
||||
|
||||
cLog(lsDEBUG)
|
||||
<< boost::str(boost::format("doSubmit: %s")
|
||||
% jvRequest);
|
||||
Json::Value jvResult;
|
||||
|
||||
if (!jvRequest.isMember("secret") || !jvRequest.isMember("tx_json"))
|
||||
std::vector<unsigned char> vucBlob(strUnHex(jvRequest["tx_blob"].asString()));
|
||||
|
||||
if (!vucBlob.size())
|
||||
{
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
Json::Value txJSON = jvRequest["tx_json"];
|
||||
|
||||
if (!txJSON.isObject())
|
||||
{
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
if (!naSeed.setSeedGeneric(jvRequest["secret"].asString()))
|
||||
{
|
||||
return rpcError(rpcBAD_SEED);
|
||||
}
|
||||
if (!txJSON.isMember("Account"))
|
||||
{
|
||||
return rpcError(rpcSRC_ACT_MISSING);
|
||||
}
|
||||
if (!raSrcAddressID.setAccountID(txJSON["Account"].asString()))
|
||||
{
|
||||
return rpcError(rpcSRC_ACT_MALFORMED);
|
||||
}
|
||||
if (!txJSON.isMember("TransactionType"))
|
||||
{
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
AccountState::pointer asSrc = mNetOps->getAccountState(mNetOps->getCurrentLedger(), raSrcAddressID);
|
||||
if (!asSrc)
|
||||
{
|
||||
cLog(lsDEBUG) << boost::str(boost::format("doSubmit: Failed to find source account in current ledger: %s")
|
||||
% raSrcAddressID.humanAccountID());
|
||||
|
||||
return rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ("Payment" == txJSON["TransactionType"].asString())
|
||||
{
|
||||
|
||||
RippleAddress dstAccountID;
|
||||
|
||||
if (!txJSON.isMember("Destination"))
|
||||
{
|
||||
return rpcError(rpcDST_ACT_MISSING);
|
||||
}
|
||||
if (!dstAccountID.setAccountID(txJSON["Destination"].asString()))
|
||||
{
|
||||
return rpcError(rpcDST_ACT_MALFORMED);
|
||||
}
|
||||
|
||||
if (!txJSON.isMember("Fee"))
|
||||
{
|
||||
txJSON["Fee"] = (int) theConfig.FEE_DEFAULT;
|
||||
}
|
||||
|
||||
if (txJSON.isMember("Paths") && jvRequest.isMember("build_path"))
|
||||
{
|
||||
// Asking to build a path when providing one is an error.
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
if (!txJSON.isMember("Paths") && txJSON.isMember("Amount") && jvRequest.isMember("build_path"))
|
||||
{
|
||||
// Need a ripple path.
|
||||
STPathSet spsPaths;
|
||||
uint160 uSrcCurrencyID;
|
||||
uint160 uSrcIssuerID;
|
||||
|
||||
STAmount saSendMax;
|
||||
STAmount saSend;
|
||||
|
||||
if (!txJSON.isMember("Amount") // Amount required.
|
||||
|| !saSend.bSetJson(txJSON["Amount"])) // Must be valid.
|
||||
return rpcError(rpcDST_AMT_MALFORMED);
|
||||
|
||||
if (txJSON.isMember("SendMax"))
|
||||
{
|
||||
if (!saSendMax.bSetJson(txJSON["SendMax"]))
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no SendMax, default to Amount with sender as issuer.
|
||||
saSendMax = saSend;
|
||||
saSendMax.setIssuer(raSrcAddressID.getAccountID());
|
||||
}
|
||||
|
||||
if (saSendMax.isNative() && saSend.isNative())
|
||||
{
|
||||
// Asking to build a path for XRP to XRP is an error.
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
Pathfinder pf(raSrcAddressID, dstAccountID, saSendMax.getCurrency(), saSendMax.getIssuer(), saSend);
|
||||
|
||||
if (!pf.findPaths(5, 3, spsPaths))
|
||||
{
|
||||
cLog(lsDEBUG) << "payment: build_path: No paths found.";
|
||||
|
||||
return rpcError(rpcNO_PATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsDEBUG) << "payment: build_path: " << spsPaths.getJson(0);
|
||||
}
|
||||
|
||||
if (!spsPaths.isEmpty())
|
||||
{
|
||||
txJSON["Paths"]=spsPaths.getJson(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!txJSON.isMember("Fee")
|
||||
&& (
|
||||
"AccountSet" == txJSON["TransactionType"].asString()
|
||||
|| "OfferCreate" == txJSON["TransactionType"].asString()
|
||||
|| "OfferCancel" == txJSON["TransactionType"].asString()
|
||||
|| "TrustSet" == txJSON["TransactionType"].asString()))
|
||||
{
|
||||
txJSON["Fee"] = (int) theConfig.FEE_DEFAULT;
|
||||
}
|
||||
|
||||
if (!txJSON.isMember("Sequence")) txJSON["Sequence"] = asSrc->getSeq();
|
||||
if (!txJSON.isMember("Flags")) txJSON["Flags"] = 0;
|
||||
|
||||
Ledger::pointer lpCurrent = mNetOps->getCurrentLedger();
|
||||
SLE::pointer sleAccountRoot = mNetOps->getSLE(lpCurrent, Ledger::getAccountRootIndex(raSrcAddressID.getAccountID()));
|
||||
|
||||
if (!sleAccountRoot)
|
||||
{
|
||||
// XXX Ignore transactions for accounts not created.
|
||||
return rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
|
||||
bool bHaveAuthKey = false;
|
||||
RippleAddress naAuthorizedPublic;
|
||||
|
||||
RippleAddress naSecret = RippleAddress::createSeedGeneric(jvRequest["secret"].asString());
|
||||
RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naSecret);
|
||||
|
||||
// Find the index of Account from the master generator, so we can generate the public and private keys.
|
||||
RippleAddress naMasterAccountPublic;
|
||||
unsigned int iIndex = 0;
|
||||
bool bFound = false;
|
||||
|
||||
// Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are
|
||||
// related.
|
||||
while (!bFound && iIndex != theConfig.ACCOUNT_PROBE_MAX)
|
||||
{
|
||||
naMasterAccountPublic.setAccountPublic(naMasterGenerator, iIndex);
|
||||
|
||||
cLog(lsWARNING) << "authorize: " << iIndex << " : " << naMasterAccountPublic.humanAccountID() << " : " << raSrcAddressID.humanAccountID();
|
||||
|
||||
bFound = raSrcAddressID.getAccountID() == naMasterAccountPublic.getAccountID();
|
||||
if (!bFound)
|
||||
++iIndex;
|
||||
}
|
||||
|
||||
if (!bFound)
|
||||
{
|
||||
return rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Use the generator to determine the associated public and private keys.
|
||||
RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSecret);
|
||||
RippleAddress naAccountPublic = RippleAddress::createAccountPublic(naGenerator, iIndex);
|
||||
RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate(naGenerator, naSecret, iIndex);
|
||||
|
||||
if (bHaveAuthKey
|
||||
// The generated pair must match authorized...
|
||||
&& naAuthorizedPublic.getAccountID() != naAccountPublic.getAccountID()
|
||||
// ... or the master key must have been used.
|
||||
&& raSrcAddressID.getAccountID() != naAccountPublic.getAccountID())
|
||||
{
|
||||
// std::cerr << "iIndex: " << iIndex << std::endl;
|
||||
// std::cerr << "sfAuthorizedKey: " << strHex(asSrc->getAuthorizedKey().getAccountID()) << std::endl;
|
||||
// std::cerr << "naAccountPublic: " << strHex(naAccountPublic.getAccountID()) << std::endl;
|
||||
|
||||
return rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
|
||||
std::auto_ptr<STObject> sopTrans;
|
||||
|
||||
try
|
||||
{
|
||||
sopTrans = STObject::parseJson(txJSON);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "malformedTransaction";
|
||||
jvResult["error_exception"] = e.what();
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
sopTrans->setFieldVL(sfSigningPubKey, naAccountPublic.getAccountPublic());
|
||||
Serializer sTrans(vucBlob);
|
||||
SerializerIterator sitTrans(sTrans);
|
||||
|
||||
SerializedTransaction::pointer stpTrans;
|
||||
|
||||
try
|
||||
{
|
||||
stpTrans = boost::make_shared<SerializedTransaction>(*sopTrans);
|
||||
stpTrans = boost::make_shared<SerializedTransaction>(boost::ref(sitTrans));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "invalidTransaction";
|
||||
jvResult["error_exception"] = e.what();
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
// FIXME: Transactions should not be signed in this code path
|
||||
stpTrans->sign(naAccountPrivate);
|
||||
|
||||
Transaction::pointer tpTrans;
|
||||
|
||||
try
|
||||
@@ -1154,29 +1258,26 @@ Json::Value RPCHandler::doSubmit(Json::Value jvRequest)
|
||||
{
|
||||
jvResult["error"] = "internalTransaction";
|
||||
jvResult["error_exception"] = e.what();
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
tpTrans = mNetOps->submitTransactionSync(tpTrans); // FIXME: Should use asynch interface
|
||||
|
||||
if (!tpTrans) {
|
||||
jvResult["error"] = "invalidTransaction";
|
||||
jvResult["error_exception"] = "Unable to sterilize transaction.";
|
||||
return jvResult;
|
||||
}
|
||||
(void) mNetOps->processTransaction(tpTrans);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
jvResult["error"] = "internalSubmit";
|
||||
jvResult["error_exception"] = e.what();
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
jvResult["tx_json"] = tpTrans->getJson(0);
|
||||
jvResult["tx_blob"] = strHex(tpTrans->getSTransaction()->getSerializer().peekData());
|
||||
|
||||
if (temUNCERTAIN != tpTrans->getResult())
|
||||
{
|
||||
@@ -1195,6 +1296,7 @@ Json::Value RPCHandler::doSubmit(Json::Value jvRequest)
|
||||
{
|
||||
jvResult["error"] = "internalJson";
|
||||
jvResult["error_exception"] = e.what();
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
}
|
||||
@@ -2482,6 +2584,7 @@ Json::Value RPCHandler::doCommand(Json::Value& jvRequest, int iRole)
|
||||
// { "profile", &RPCHandler::doProfile, false, optCurrent },
|
||||
{ "random", &RPCHandler::doRandom, false, optNone },
|
||||
{ "ripple_path_find", &RPCHandler::doRipplePathFind, false, optCurrent },
|
||||
{ "sign", &RPCHandler::doSign, false, optCurrent },
|
||||
{ "submit", &RPCHandler::doSubmit, false, optCurrent },
|
||||
{ "server_info", &RPCHandler::doServerInfo, true, optNone },
|
||||
{ "stop", &RPCHandler::doStop, true, optNone },
|
||||
|
||||
@@ -30,6 +30,7 @@ class RPCHandler
|
||||
// Utilities
|
||||
void addSubmitPath(Json::Value& txJSON);
|
||||
boost::unordered_set<RippleAddress> parseAccountIds(const Json::Value& jvArray);
|
||||
Json::Value transactionSign(Json::Value jvRequest, bool bSubmit);
|
||||
|
||||
Json::Value lookupLedger(Json::Value jvRequest, Ledger::pointer& lpLedger);
|
||||
|
||||
@@ -69,6 +70,7 @@ class RPCHandler
|
||||
Json::Value doSessionClose(Json::Value params);
|
||||
Json::Value doSessionOpen(Json::Value params);
|
||||
Json::Value doStop(Json::Value params);
|
||||
Json::Value doSign(Json::Value params);
|
||||
Json::Value doSubmit(Json::Value params);
|
||||
Json::Value doTx(Json::Value params);
|
||||
Json::Value doTxHistory(Json::Value params);
|
||||
|
||||
@@ -75,14 +75,48 @@ int charUnHex(char cDigit)
|
||||
: -1;
|
||||
}
|
||||
|
||||
void strUnHex(std::string& strDst, const std::string& strSrc)
|
||||
int strUnHex(std::string& strDst, const std::string& strSrc)
|
||||
{
|
||||
int iBytes = strSrc.size()/2;
|
||||
int iBytes = (strSrc.size()+1)/2;
|
||||
|
||||
strDst.resize(iBytes);
|
||||
|
||||
for (int i=0; i != iBytes; i++)
|
||||
strDst[i] = (charUnHex(strSrc[i*2]) << 4) | charUnHex(strSrc[i*2+1]);
|
||||
const char* pSrc = &strSrc[0];
|
||||
char* pDst = &strDst[0];
|
||||
|
||||
if (strSrc.size() & 1)
|
||||
{
|
||||
int c = charUnHex(*pSrc++);
|
||||
|
||||
if (c < 0)
|
||||
{
|
||||
iBytes = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pDst++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; iBytes >= 0 && i != iBytes; i++)
|
||||
{
|
||||
int cHigh = charUnHex(*pSrc++);
|
||||
int cLow = charUnHex(*pSrc++);
|
||||
|
||||
if (cHigh < 0 || cLow < 0)
|
||||
{
|
||||
iBytes = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
strDst[i] = (cHigh << 4) | cLow;
|
||||
}
|
||||
}
|
||||
|
||||
if (iBytes < 0)
|
||||
strDst.clear();
|
||||
|
||||
return iBytes;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> strUnHex(const std::string& strSrc)
|
||||
|
||||
@@ -198,7 +198,7 @@ bool isZero(Iterator first, int iSize)
|
||||
}
|
||||
|
||||
int charUnHex(char cDigit);
|
||||
void strUnHex(std::string& strDst, const std::string& strSrc);
|
||||
int strUnHex(std::string& strDst, const std::string& strSrc);
|
||||
|
||||
uint64_t uintFromHex(const std::string& strSrc);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user