From 2b70d3f750bdcf99abc0681dd0f391856fd01d4f Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 14 Jan 2013 14:28:23 -0800 Subject: [PATCH 1/5] Output fee information in server_info. --- src/cpp/ripple/NetworkOPs.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index cc52879837..b589567df0 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1127,7 +1127,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 +1478,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; } From f2337b81dbf411cadfa8880f0fc74d38289a8889 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 14 Jan 2013 16:58:14 -0800 Subject: [PATCH 2/5] Have strUnhex report malformed hex. --- src/cpp/ripple/utils.cpp | 42 ++++++++++++++++++++++++++++++++++++---- src/cpp/ripple/utils.h | 2 +- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/cpp/ripple/utils.cpp b/src/cpp/ripple/utils.cpp index 760ea7a6bc..8dc565924b 100644 --- a/src/cpp/ripple/utils.cpp +++ b/src/cpp/ripple/utils.cpp @@ -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 strUnHex(const std::string& strSrc) diff --git a/src/cpp/ripple/utils.h b/src/cpp/ripple/utils.h index c5790bc858..77ae2f26fd 100644 --- a/src/cpp/ripple/utils.h +++ b/src/cpp/ripple/utils.h @@ -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); From 88c702a95754d007d6710b9a930964fde71603d7 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 14 Jan 2013 16:59:06 -0800 Subject: [PATCH 3/5] Make actual submitting optional for submitTransactionSync --- src/cpp/ripple/NetworkOPs.cpp | 5 +++-- src/cpp/ripple/NetworkOPs.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index b589567df0..d093891a01 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -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 { diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 66d757ffac..3101880ddd 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -160,7 +160,7 @@ public: // typedef boost::function 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); From 4e06e0ffa5f4c951b14e439e5192594581f00a14 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 14 Jan 2013 16:59:47 -0800 Subject: [PATCH 4/5] Add server support for sign and submit tx_blobs. --- src/cpp/ripple/RPCErr.cpp | 1 + src/cpp/ripple/RPCErr.h | 1 + src/cpp/ripple/RPCHandler.cpp | 523 ++++++++++++++++++++-------------- src/cpp/ripple/RPCHandler.h | 2 + 4 files changed, 317 insertions(+), 210 deletions(-) diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index 57eb4dadd6..9ab952080b 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -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." }, diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index 858c3f9b5d..fa62c0e625 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -43,6 +43,7 @@ enum { // Bad parameter rpcACT_MALFORMED, rpcQUALITY_MALFORMED, + rpcBAD_BLOB, rpcBAD_SEED, rpcDST_ACT_MALFORMED, rpcDST_ACT_MISSING, diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 3791bf9469..8ad6ec1355 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -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 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(*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(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: , +// secret: +// } +Json::Value RPCHandler::doSign(Json::Value jvRequest) +{ + return transactionSign(jvRequest, false); +} + // { // tx_json: , // 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 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 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(*sopTrans); + stpTrans = boost::make_shared(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; } } @@ -2474,6 +2576,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 }, diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index bfd99ba1ab..8c4727f3b3 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -21,6 +21,7 @@ class RPCHandler // Utilities void addSubmitPath(Json::Value& txJSON); boost::unordered_set parseAccountIds(const Json::Value& jvArray); + Json::Value transactionSign(Json::Value jvRequest, bool bSubmit); Json::Value lookupLedger(Json::Value jvRequest, Ledger::pointer& lpLedger); @@ -59,6 +60,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); From 104613a0a6b9a874f9f7c2b3d19ec852fa8c3277 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 14 Jan 2013 17:00:09 -0800 Subject: [PATCH 5/5] Add command line support for sign. --- src/cpp/ripple/CallRPC.cpp | 23 +++++++++++++++++++---- src/cpp/ripple/CallRPC.h | 2 +- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 1f498a0471..2b6ffff988 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -285,14 +285,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; @@ -459,7 +473,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 }, diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index 4c893256c2..1d6c4d2c75 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -30,7 +30,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);