diff --git a/src/Amount.cpp b/src/Amount.cpp index 89fa2b753e..76936cbbc4 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -1,11 +1,156 @@ #include #include +#include #include #include +#include "Config.h" #include "SerializedTypes.h" +#include "utils.h" + +bool STAmount::currencyFromString(uint160& uDstCurrency, const std::string& sCurrency) +{ + bool bSuccess = true; + + if (sCurrency.empty() || !sCurrency.compare(SYSTEM_CURRENCY_CODE)) + { + uDstCurrency = 0; + } + else if (3 == sCurrency.size()) + { + std::vector vucIso(3); + + std::transform(sCurrency.begin(), sCurrency.end(), vucIso.begin(), ::toupper); + + // std::string sIso; + // sIso.assign(vucIso.begin(), vucIso.end()); + // std::cerr << "currency: " << sIso << std::endl; + + Serializer s; + + s.addZeros(98/8); + s.addRaw(vucIso); + s.addZeros(16/8); + s.addZeros(25/8); + + SerializerIterator sit(s); + + uDstCurrency = sit.get160(); + } + else + { + bSuccess = false; + } + + return bSuccess; +} + +std::string STAmount::getCurrencyHuman() +{ + std::string sCurrency; + + if (mIsNative) + { + return SYSTEM_CURRENCY_CODE; + } + else + { + uint160 uReserved = mCurrency; + Serializer s(160/20); + + s.add160(mCurrency); + + SerializerIterator sit(s); + + std::vector vucZeros = sit.getRaw(96/8); + std::vector vucIso = sit.getRaw(24/8); + std::vector vucVersion = sit.getRaw(16/8); + std::vector vucReserved = sit.getRaw(24/8); + + if (!::isZero(vucZeros.begin(), vucZeros.size())) + { + throw std::runtime_error("bad currency: zeros"); + } + else if (!::isZero(vucVersion.begin(), vucVersion.size())) + { + throw std::runtime_error("bad currency: version"); + } + else if (!::isZero(vucReserved.begin(), vucReserved.size())) + { + throw std::runtime_error("bad currency: reserved"); + } + else + { + sCurrency.assign(vucIso.begin(), vucIso.end()); + } + } + + return sCurrency; +} + +// Not meant to be the ultimate parser. For use by RPC which is supposed to be sane and trusted. +bool STAmount::setValue(const std::string& sAmount, const std::string& sCurrency) +{ + if (!currencyFromString(mCurrency, sCurrency)) + return false; + + uint64 uValue; + int iOffset; + size_t uDecimal = sAmount.find_first_of(".,"); + bool bInteger = uDecimal == std::string::npos; + + if (bInteger) + { + uValue = sAmount.empty() ? 0 : boost::lexical_cast(sAmount); + iOffset = 0; + } + else + { + // Example size decimal size-decimal offset + // .1 2 0 2 -1 + // 123. 4 3 1 0 + // 1.23 4 1 3 -2 + iOffset = -(sAmount.size()-uDecimal-1); + + uint64 uInteger = uDecimal ? boost::lexical_cast(sAmount.substr(0, uDecimal)) : 0; + uint64 uFraction = iOffset ? boost::lexical_cast(sAmount.substr(uDecimal+1)) : 0; + + uValue = uInteger; + for (int i=-iOffset; i--;) + uValue *= 10; + + uValue += uFraction; + } + + mIsNative = !mCurrency; + if (mIsNative) + { + if (bInteger) + iOffset = -SYSTEM_CURRENCY_PRECISION; + + while (iOffset > -SYSTEM_CURRENCY_PRECISION) { + uValue *= 10; + --iOffset; + } + + while (iOffset < -SYSTEM_CURRENCY_PRECISION) { + uValue /= 10; + ++iOffset; + } + + mValue = uValue; + } + else + { + mValue = uValue; + mOffset = iOffset; + canonicalize(); + } + + return true; +} // amount = value * [10 ^ offset] // representation range is 10^80 - 10^(-80) diff --git a/src/Application.cpp b/src/Application.cpp index 8b47e4b737..24512869bd 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -15,17 +15,6 @@ Application* theApp=NULL; -/* -What needs to happen: - Listen for connections - Try to maintain the right number of connections - Process messages from peers - Process messages from RPC - Periodically publish a new ledger - Save the various pieces of data - -*/ - DatabaseCon::DatabaseCon(const std::string& name, const char *initStrings[], int initCount) { std::string path=strprintf("%s%s", theConfig.DATA_DIR.c_str(), name.c_str()); @@ -117,9 +106,8 @@ void Application::run() rootAddress.setAccountPublic(rootGeneratorMaster, 0); - std::cerr << "Master seed: " << rootSeedMaster.humanFamilySeed() << std::endl; - std::cerr << "Master generator: " << rootGeneratorMaster.humanFamilyGenerator() << std::endl; - std::cerr << "Root public key: " << rootAddress.humanAccountPublic() << std::endl; + // Print enough information to be able to claim root account. + std::cerr << "Root master seed: " << rootSeedMaster.humanFamilySeed() << std::endl; std::cerr << "Root account: " << rootAddress.humanAccountID() << std::endl; Ledger::pointer firstLedger = boost::make_shared(rootAddress, 100000000); diff --git a/src/Config.cpp b/src/Config.cpp index 1e3f862cc9..3b40d38c69 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -7,6 +7,7 @@ #include #include +// Fees are in XNS raw. #define DEFAULT_FEE_CREATE 1000 #define DEFAULT_FEE_DEFAULT 100 @@ -121,15 +122,6 @@ void Config::load() FEE_DEFAULT = boost::lexical_cast(strTemp); } } - - /* - node=root.child("DB_TYPE"); - if(!node.empty()) - { - if( stricmp(node.child_value(),"mysql")==0 ) theApp->setDB(Database::newMysqlDatabase("host","user","pass")); - else theApp->setSerializer(new DiskSerializer()); - }else */ - } // vim:ts=4 diff --git a/src/Config.h b/src/Config.h index 4c43466e71..c5aadc6091 100644 --- a/src/Config.h +++ b/src/Config.h @@ -2,11 +2,14 @@ #define __CONFIG__ #include "types.h" +#include "SerializedTypes.h" #include -#define SYSTEM_NAME "newcoin" -#define VALIDATORS_SITE "redstem.com" +#define SYSTEM_NAME "newcoin" +#define VALIDATORS_SITE "redstem.com" +#define SYSTEM_CURRENCY_CODE "XNS" +#define SYSTEM_CURRENCY_PRECISION 6 #define VALIDATORS_FILE_NAME "validators.txt" const int SYSTEM_PEER_PORT = 6561; @@ -26,47 +29,49 @@ const int SYSTEM_PEER_PORT = 6561; class Config { public: - // core software parameters - int VERSION; + // Core software parameters + int VERSION; std::string VERSION_STR; - // network parameters - int NETWORK_START_TIME; // The Unix time we start ledger 0 - int TRANSACTION_FEE_BASE; - int LEDGER_SECONDS; - int LEDGER_PROPOSAL_DELAY_SECONDS; - int LEDGER_AVALANCHE_SECONDS; + // Network parameters + int NETWORK_START_TIME; // The Unix time we start ledger 0 + int TRANSACTION_FEE_BASE; + int LEDGER_SECONDS; + int LEDGER_PROPOSAL_DELAY_SECONDS; + int LEDGER_AVALANCHE_SECONDS; // Note: The following parameters do not relate to the UNL or trust at all - int NETWORK_QUORUM; // Minimum number of nodes to consider the network present - int VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative + int NETWORK_QUORUM; // Minimum number of nodes to consider the network present + int VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative - // node networking parameters + // Peer networking parameters std::string PEER_IP; - int PEER_PORT; - int NUMBER_CONNECTIONS; -// bool NODE_INBOUND; // we accept inbound connections -// bool NODE_DATABASE; // we offer historical data services -// bool NODE_PUBLIC; // we do not attempt to hide our identity -// bool NODE_DUMB; // we are a 'dumb' client -// bool NODE_SMART; // we offer services to 'dumb' clients - - // RPC parameters - std::string RPC_IP; - int RPC_PORT; - std::string RPC_USER; - std::string RPC_PASSWORD; - - std::string VALIDATION_PASSWORD; - std::string VALIDATION_KEY; - + int PEER_PORT; + int NUMBER_CONNECTIONS; std::string PEER_SSL_CIPHER_LIST; int PEER_SCAN_INTERVAL_MIN; int PEER_START_MAX; int PEER_CONNECT_LOW_WATER; - uint64 FEE_CREATE; // Fee to create an account - uint64 FEE_DEFAULT; // Default fee. +// bool NODE_INBOUND; // We accept inbound connections +// bool NODE_DATABASE; // We offer historical data services +// bool NODE_PUBLIC; // We do not attempt to hide our identity +// bool NODE_DUMB; // We are a 'dumb' client +// bool NODE_SMART; // We offer services to 'dumb' clients + + // RPC parameters + std::string RPC_IP; + int RPC_PORT; + std::string RPC_USER; + std::string RPC_PASSWORD; + + // Validation + std::string VALIDATION_PASSWORD; + std::string VALIDATION_KEY; + + // Fees + STAmount FEE_CREATE; // Fee to create an account + STAmount FEE_DEFAULT; // Default fee. // configuration parameters std::string DATA_DIR; diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 5bcc479340..4051491b36 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -29,7 +29,7 @@ Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(s startAccount->peekSLE().setIFieldAmount(sfBalance, startAmount); startAccount->peekSLE().setIFieldU32(sfSequence, 1); writeBack(lepCREATE, startAccount->getSLE()); -#ifdef DEBUG +#if 0 std::cerr << "Root account:"; startAccount->dump(); #endif diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 34fccc392c..a6b0cdc609 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -353,63 +353,63 @@ Json::Value RPCServer::doPeers(Json::Value& params) return theApp->getConnectionPool().getPeersJson(); } -Json::Value RPCServer::doSendTo(Json::Value& params) -{ // Implement simple sending without gathering - // sendto - // sendto - if (!params.isArray() || (params.size()<2)) + +// send regular_seed paying_account account_id amount [currency] [send_max] [send_currency] +Json::Value RPCServer::doSend(Json::Value& params) +{ + NewcoinAddress naSeed; + NewcoinAddress naSrcAccountID; + NewcoinAddress naDstAccountID; + STAmount saSrcAmount; + STAmount saDstAmount; + std::string sSrcCurrency; + std::string sDstCurrency; + + if (params.size() >= 5) + sDstCurrency = params[4u].asString(); + + if (params.size() >= 7) + sSrcCurrency = params[6u].asString(); + + if (!params.isArray() || params.size() < 3 || params.size() > 7) + { return JSONRPCError(500, "Invalid parameters"); - - int paramCount=getParamCount(params); - if ((paramCount<2)||(paramCount>3)) - return JSONRPCError(500, "Invalid parameters"); - - std::string sDest, sAmount; - if (!extractString(sDest, params, 0) || !extractString(sAmount, params, 1)) - return JSONRPCError(500, "Invalid parameters"); - - NewcoinAddress destAccount; - - destAccount.setAccountID(sDest) || destAccount.setAccountPublic(sDest); - if (!destAccount.isValid()) - return JSONRPCError(500, "Unable to parse destination account"); - - uint64 iAmount; - try - { - iAmount=boost::lexical_cast(sAmount); - if (iAmount<=0) return JSONRPCError(500, "Invalid amount"); } - catch (...) + else if (!naSeed.setFamilySeedGeneric(params[0u].asString())) { - return JSONRPCError(500, "Invalid amount"); + return JSONRPCError(500, "disallowed seed"); } - - uint32 iTag(0); - try + else if (!naSrcAccountID.setAccountID(params[1u].asString())) { - if (paramCount>2) - { - std::string sTag; - extractString(sTag, params, 2); - iTag=boost::lexical_cast(sTag); - } + return JSONRPCError(500, "source account id needed"); } - catch (...) + else if (!naDstAccountID.setAccountID(params[2u].asString())) { - return JSONRPCError(500, "Invalid tag"); + return JSONRPCError(500, "create account id needed"); } + else if (!saDstAmount.setValue(params[3u].asString(), sDstCurrency)) + { + return JSONRPCError(500, "bad dst amount/currency"); + } + else if (!saSrcAmount.setValue(params[5u].asString(), sSrcCurrency)) + { + return JSONRPCError(500, "bad src amount/currency"); + } + else + { + Json::Value obj(Json::objectValue); -#ifdef DEBUG - std::cerr << "SendTo(" << destAccount.humanAccountID() << ") amount=" << iAmount << - ", tag=" << iTag << std::endl; -#endif + // obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["seed"] = naSeed.humanFamilySeed(); + obj["srcAccountID"] = naSrcAccountID.humanAccountID(); + obj["dstAccountID"] = naDstAccountID.humanAccountID(); + obj["srcAmount"] = saSrcAmount.getText(); + obj["srcISO"] = saSrcAmount.getCurrencyHuman(); + obj["dstAmount"] = saDstAmount.getText(); + obj["dstISO"] = saDstAmount.getCurrencyHuman(); - LocalTransaction::pointer lt(new LocalTransaction(destAccount, iAmount, iTag)); - if (!lt->makeTransaction()) - return JSONRPCError(500, "Insufficient funds in unlocked accounts"); - lt->performTransaction(); - return lt->getTransaction()->getJson(true); + return obj; + } } Json::Value RPCServer::doTx(Json::Value& params) @@ -549,6 +549,16 @@ Json::Value RPCServer::doValidatorCreate(Json::Value& params) { return obj; } +Json::Value RPCServer::doWalletAccounts(Json::Value& params) +{ + return "not implemented"; +} + +Json::Value RPCServer::doWalletAdd(Json::Value& params) +{ + return "not implemented"; +} + // wallet_claim [] [] // // To provide an example to client writers, we do everything we expect a client to do here. @@ -573,7 +583,7 @@ Json::Value RPCServer::doWalletClaim(Json::Value& params) } else { - // Trying to build: + // Building: // peer_wallet_claim // [] // @@ -646,31 +656,31 @@ Json::Value RPCServer::doWalletClaim(Json::Value& params) // YYY Need annotation and source tag Json::Value RPCServer::doWalletCreate(Json::Value& params) { - NewcoinAddress naSourceID; - NewcoinAddress naCreateID; + NewcoinAddress naSrcAccountID; + NewcoinAddress naDstAccountID; NewcoinAddress naRegularSeed; if (params.size() < 3 || params.size() > 4) { return "invalid params"; } - else if (!naSourceID.setAccountID(params[1u].asString())) - { - return "source account id needed"; - } - else if (!naCreateID.setAccountID(params[2u].asString())) - { - return "create account id needed"; - } else if (!naRegularSeed.setFamilySeedGeneric(params[0u].asString())) { return "disallowed seed"; } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return "source account id needed"; + } + else if (!naDstAccountID.setAccountID(params[2u].asString())) + { + return "create account id needed"; + } else if (!theApp->getOPs().available()) { // We require access to the paying account's sequence number and key information. return "network not available"; } - else if (theApp->getMasterLedger().getCurrentLedger()->getAccountState(naCreateID)) + else if (theApp->getMasterLedger().getCurrentLedger()->getAccountState(naDstAccountID)) { return "account already exists"; } @@ -684,7 +694,7 @@ Json::Value RPCServer::doWalletCreate(Json::Value& params) Ledger::pointer ledger = theApp->getMasterLedger().getCurrentLedger(); LedgerStateParms qry = lepNONE; - SerializedLedgerEntry::pointer sleSrc = ledger->getAccountRoot(qry, naSourceID); + SerializedLedgerEntry::pointer sleSrc = ledger->getAccountRoot(qry, naSrcAccountID); if (!sleSrc) { @@ -694,7 +704,6 @@ Json::Value RPCServer::doWalletCreate(Json::Value& params) STAmount saSrcBalance = sleSrc->getIValueFieldAmount(sfBalance); STAmount saInitialFunds = (params.size() < 4) ? 0 : boost::lexical_cast(params[3u].asString()); -#if 0 if (saSrcBalance < theConfig.FEE_CREATE + saInitialFunds) { return "insufficent funds"; @@ -703,7 +712,7 @@ Json::Value RPCServer::doWalletCreate(Json::Value& params) { return "source account has not been claimed"; } -#endif + NewcoinAddress naRegularGenerator; NewcoinAddress naRegular0Public; NewcoinAddress naRegular0Private; @@ -742,7 +751,7 @@ Json::Value RPCServer::doWalletCreate(Json::Value& params) do { ++iIndex; naMasterAccountPublic.setAccountPublic(naMasterGenerator, iIndex); - } while (naSourceID.getAccountID() != naMasterAccountPublic.getAccountID()); + } while (naSrcAccountID.getAccountID() != naMasterAccountPublic.getAccountID()); NewcoinAddress naRegularAccountPublic; NewcoinAddress naRegularAccountPrivate; @@ -761,11 +770,11 @@ Json::Value RPCServer::doWalletCreate(Json::Value& params) Transaction::pointer trans = Transaction::sharedCreate( naRegularAccountPublic, naRegularAccountPrivate, - naSourceID, + naSrcAccountID, sleSrc->getIFieldU32(sfSequence), theConfig.FEE_CREATE, 0, // YYY No source tag - naCreateID, + naDstAccountID, saInitialFunds); // Initial funds in XNC. (void) theApp->getOPs().processTransaction(trans); @@ -841,6 +850,11 @@ Json::Value RPCServer::doWalletSeed(Json::Value& params) } } +Json::Value RPCServer::doWalletVerify(Json::Value& params) +{ + return "not implemented"; +} + void RPCServer::validatorsResponse(const boost::system::error_code& err, std::string strResponse) { std::cerr << "Fetch '" VALIDATORS_FILE_NAME "' complete." << std::endl; @@ -974,6 +988,8 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params if (command == "account_info") return doAccountInfo(params); if (command == "connect") return doConnect(params); if (command == "peers") return doPeers(params); + + if (command == "send") return doSend(params); if (command == "stop") return doStop(params); if (command == "unl_add") return doUnlAdd(params); @@ -985,16 +1001,18 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params if (command == "validation_create") return doValidatorCreate(params); + if (command == "wallet_accounts") return doWalletAccounts(params); + if (command == "wallet_add") return doWalletAdd(params); if (command == "wallet_claim") return doWalletClaim(params); if (command == "wallet_create") return doWalletCreate(params); if (command == "wallet_propose") return doWalletPropose(params); if (command == "wallet_seed") return doWalletSeed(params); + if (command == "wallet_verify") return doWalletVerify(params); // // Obsolete or need rewrite: // - if (command=="sendto") return doSendTo(params); if (command=="tx") return doTx(params); if (command=="ledger") return doLedger(params); diff --git a/src/RPCServer.h b/src/RPCServer.h index d5197af6f7..43a5426982 100644 --- a/src/RPCServer.h +++ b/src/RPCServer.h @@ -35,7 +35,7 @@ class RPCServer : public boost::enable_shared_from_this Json::Value doConnect(Json::Value& params); Json::Value doLedger(Json::Value& params); Json::Value doPeers(Json::Value& params); - Json::Value doSendTo(Json::Value& params); + Json::Value doSend(Json::Value& params); Json::Value doSessionClose(Json::Value& params); Json::Value doSessionOpen(Json::Value& params); Json::Value doStop(Json::Value& params); @@ -52,12 +52,14 @@ class RPCServer : public boost::enable_shared_from_this Json::Value doValidatorCreate(Json::Value& params); Json::Value doWalletAccounts(Json::Value& params); + Json::Value doWalletAdd(Json::Value& params); Json::Value doWalletClaim(Json::Value& params); Json::Value doWalletCreate(Json::Value& params); Json::Value doWalletLock(Json::Value& params); Json::Value doWalletPropose(Json::Value& params); Json::Value doWalletSeed(Json::Value& params); Json::Value doWalletUnlock(Json::Value& params); + Json::Value doWalletVerify(Json::Value& params); void validatorsResponse(const boost::system::error_code& err, std::string strResponse); diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index b551208560..5a902b29de 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -232,10 +232,12 @@ public: int getOffset() const { return mOffset; } uint64 getValue() const { return mValue; } void setValue(const STAmount& v) { mValue=v; } + std::string getCurrencyHuman(); bool isNative() const { return mIsNative; } const uint160& getCurrency() const { return mCurrency; } void zero() { mOffset = mIsNative ? -100 : 0; mValue = 0; } bool isZero() const { return mValue == 0; } + bool setValue(const std::string& sAmount, const std::string& sCurrency); virtual bool isEquivalent(const SerializedType& t) const; @@ -279,6 +281,7 @@ public: const char* name = NULL); static STAmount deSerialize(SerializerIterator&); + static bool currencyFromString(uint160& uDstCurrency, const std::string& sCurrency); }; class STHash128 : public SerializedType diff --git a/src/Serializer.cpp b/src/Serializer.cpp index b1803de4c8..91b8dcf755 100644 --- a/src/Serializer.cpp +++ b/src/Serializer.cpp @@ -4,6 +4,16 @@ #include #include +int Serializer::addZeros(size_t uBytes) +{ + int ret = mData.size(); + + while (uBytes--) + mData.push_back(0); + + return ret; +} + int Serializer::add16(uint16 i) { int ret = mData.size(); @@ -574,4 +584,13 @@ std::vector SerializerIterator::getTaggedList() mPos += length; return tl; } + +std::vector SerializerIterator::getRaw(int iLength) +{ + int iPos = mPos; + mPos += iLength; + + return mSerializer.getRaw(iPos, iLength); +} + // vim:ts=4 diff --git a/src/Serializer.h b/src/Serializer.h index e20be248a6..6000c4e89b 100644 --- a/src/Serializer.h +++ b/src/Serializer.h @@ -36,6 +36,7 @@ class Serializer int addRaw(const std::vector &vector); int addRaw(const void *ptr, int len); int addRaw(const Serializer& s); + int addZeros(size_t uBytes); int addVL(const std::vector &vector); int addVL(const void *ptr, int len); @@ -136,6 +137,8 @@ public: uint160 get160(); uint256 get256(); + std::vector getRaw(int iLength); + std::vector getVL(); std::vector getTaggedList(); }; diff --git a/src/utils.h b/src/utils.h index 94a6295d78..14784c8570 100644 --- a/src/utils.h +++ b/src/utils.h @@ -54,18 +54,30 @@ std::string strHex(Iterator first, int iSize) return strDst; } -inline const std::string strHex(const std::string& strSrc) { +inline const std::string strHex(const std::string& strSrc) +{ return strHex(strSrc.begin(), strSrc.size()); } -inline std::string strHex(const std::vector vchData) { - return strHex(vchData.begin(), vchData.size()); +inline std::string strHex(const std::vector vucData) +{ + return strHex(vucData.begin(), vucData.size()); } -inline const std::string strHex(const uint160& ui) { +inline const std::string strHex(const uint160& ui) +{ return strHex(ui.begin(), ui.size()); } +template +bool isZero(Iterator first, int iSize) +{ + while (iSize && !*first++) + --iSize; + + return !iSize; +} + int charUnHex(char cDigit); void strUnHex(std::string& strDst, const std::string& strSrc);