Merge branch 'master' of github.com:jedmccaleb/NewCoin

This commit is contained in:
JoelKatz
2013-01-14 18:17:02 -08:00
10 changed files with 400 additions and 232 deletions

View File

@@ -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 },

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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." },

View File

@@ -43,6 +43,7 @@ enum {
// Bad parameter
rpcACT_MALFORMED,
rpcQUALITY_MALFORMED,
rpcBAD_BLOB,
rpcBAD_SEED,
rpcDST_ACT_MALFORMED,
rpcDST_ACT_MISSING,

View File

@@ -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 },

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);