diff --git a/Ledger.cpp b/Ledger.cpp index 2cf8f2a3d6..63b62c8119 100644 --- a/Ledger.cpp +++ b/Ledger.cpp @@ -285,7 +285,7 @@ bool Ledger::unitTest() as=ledger->getAccountState(la2); assert(!as); - Transaction::pointer t(new Transaction(NEW, l1, l1->getAcctSeq(), l2->getAddress(), 2500, 0, 1)); + Transaction::pointer t(new Transaction(l1, l2->getAddress(), 2500, 0, 1)); assert(!!t->getID()); Ledger::TransResult tr=ledger->applyTransaction(t); diff --git a/LocalTransaction.cpp b/LocalTransaction.cpp new file mode 100644 index 0000000000..f5c7014c90 --- /dev/null +++ b/LocalTransaction.cpp @@ -0,0 +1,17 @@ + +#include "LocalTransaction.h" +#include "Application.h" +#include "Wallet.h" + +bool LocalTransaction::makeTransaction() +{ + if(!!mTransaction) return true; + + LocalAccount::pointer lac(theApp->getWallet().findAccountForTransaction(mAmount)); + if(!lac) return false; + + mTransaction=Transaction::pointer(new Transaction(lac, mDestAcctID, mAmount, mTag, + theApp->getOPs().getCurrentLedgerID())); + if(mTransaction->getStatus()!=NEW) return false; + return true; +} diff --git a/LocalTransaction.h b/LocalTransaction.h index 853c00d225..bcd36d7911 100644 --- a/LocalTransaction.h +++ b/LocalTransaction.h @@ -37,7 +37,7 @@ public: const std::string& getComment() const { return mComment; } Transaction::pointer getTransaction() { return mTransaction; } - bool makeTransaction(void); + bool makeTransaction(); }; #endif diff --git a/Makefile b/Makefile index 4b546623a4..b9ef834dca 100644 --- a/Makefile +++ b/Makefile @@ -71,10 +71,10 @@ SRCS= keystore.cpp BitcoinUtil.cpp \ Application.cpp TimingService.cpp KnownNodeList.cpp ConnectionPool.cpp Peer.cpp \ PeerDoor.cpp RPCDoor.cpp RPCServer.cpp rpc.cpp Conversion.cpp RequestParser.cpp HashedObject.cpp \ UniqueNodeList.cpp PubKeyCache.cpp SHAMapDiff.cpp DeterministicKeys.cpp LedgerMaster.cpp \ - LedgerHistory.cpp NetworkOPs.cpp CallRPC.cpp DBInit.cpp + LedgerHistory.cpp NetworkOPs.cpp CallRPC.cpp DBInit.cpp LocalTransaction.cpp DBSRCS= SqliteDatabase.cpp database.cpp -DBSRCC=sqlite3.c +DBSRCC= sqlite3.c UTILSRCS= pugixml.cpp diff --git a/RPCServer.cpp b/RPCServer.cpp index dcdb84d9c7..f664ea82a4 100644 --- a/RPCServer.cpp +++ b/RPCServer.cpp @@ -16,6 +16,7 @@ #include "RPC.h" #include "Wallet.h" #include "Conversion.h" +#include "LocalTransaction.h" /* Just read from wire until the entire request is in. @@ -103,7 +104,7 @@ std::string RPCServer::handleRequest(const std::string& requestStr) w.write(std::cerr, valParams); #endif - Json::Value result=doCommand(strMethod, valParams); + Json::Value result(doCommand(strMethod, valParams)); #ifdef DEBUG w.write(std::cerr, result); @@ -317,8 +318,57 @@ Json::Value RPCServer::doSendTo(Json::Value& params) // sendto if(!params.isArray() || (params.size()<2)) return JSONRPCError(500, "Invalid parameters"); + + int paramCount=getParamCount(params); + if((paramCount<2)||(paramCount>3)) + return JSONRPCError(500, "Invalid parameters"); - return "Not yet"; + std::string sDest, sAmount; + if(!extractString(sDest, params, 0) || !extractString(sAmount, params, 1)) + return JSONRPCError(500, "Invalid parameters"); + + uint160 destAccount=parseAccount(sDest); + if(!destAccount) + 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 (...) + { + return JSONRPCError(500, "Invalid amount"); + } + + uint32 iTag(0); + try + { + if(paramCount>2) + { + std::string sTag; + extractString(sTag, params, 2); + iTag=boost::lexical_cast(sTag); + } + } + catch (...) + { + return JSONRPCError(500, "Invalid tag"); + } + +#ifdef DEBUG + std::cerr << "SendTo(" << destAccount.GetHex() << ") amount=" << iAmount << + ", tag=" << iTag << std::endl; +#endif + + LocalTransaction::pointer lt(new LocalTransaction(destAccount, iAmount, iTag)); + if(!lt->makeTransaction()) + return JSONRPCError(500, "Insufficient funds in unlocked accounts"); + + // WRITEME - process transaction + + return lt->getTransaction()->getJson(true); } Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params) diff --git a/Transaction.cpp b/Transaction.cpp index b603cacfe3..21f3b3d8e6 100644 --- a/Transaction.cpp +++ b/Transaction.cpp @@ -14,16 +14,17 @@ Transaction::Transaction() : mTransactionID(0), mAccountFrom(0), mAccountTo(0), { } -Transaction::Transaction(TransStatus status, LocalAccount::pointer fromLocalAccount, uint32 fromSeq, - const uint160& toAccount, uint64 amount, uint32 ident, uint32 ledger) : - mAccountTo(toAccount), mAmount(amount), mFromAccountSeq(fromSeq), mSourceLedger(ledger), - mIdent(ident), mInLedger(0), mStatus(NEW) +Transaction::Transaction(LocalAccount::pointer fromLocalAccount, const uint160& toAccount, uint64 amount, + uint32 ident, uint32 ledger) : + mAccountTo(toAccount), mAmount(amount), mSourceLedger(ledger), mIdent(ident), mInLedger(0), mStatus(NEW) { mAccountFrom=fromLocalAccount->getAddress(); mFromPubKey=fromLocalAccount->getPublicKey(); + mFromAccountSeq=fromLocalAccount->getAcctSeq(); + if(!mFromAccountSeq) mStatus=INCOMPLETE; assert(mFromPubKey); updateFee(); - sign(fromLocalAccount); + if(!sign(fromLocalAccount)) mStatus=INCOMPLETE; } Transaction::Transaction(const std::vector &t, bool validate) : mStatus(INVALID) @@ -279,3 +280,46 @@ bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedg } return ret; } + +Json::Value Transaction::getJson(bool decorate) const +{ + Json::Value ret(Json::objectValue); + ret["TransactionID"]=mTransactionID.GetHex(); + ret["Amount"]=boost::lexical_cast(mAmount); + ret["Fee"]=boost::lexical_cast(mFee); + if(mInLedger) ret["InLedger"]=mInLedger; + + switch(mStatus) + { + case NEW: ret["Status"]="new"; break; + case INVALID: ret["Status"]="invalid"; break; + case INCLUDED: ret["Status"]="included"; break; + case CONFLICTED: ret["Status"]="conflicted"; break; + case COMMITTED: ret["Status"]="committed"; break; + case HELD: ret["Status"]="held"; break; + case REMOVED: ret["Status"]="removed"; break; + case OBSOLETE: ret["Status"]="obsolete"; break; + case INCOMPLETE: ret["Status"]="incomplete"; break; + default: ret["Status"]="unknown"; + } + + Json::Value source(Json::objectValue); + source["AccountID"]=NewcoinAddress(mAccountFrom).GetString(); + source["AccountSeq"]=mFromAccountSeq; + source["Ledger"]=mSourceLedger; + if(!!mIdent) source["Identifier"]=mIdent; + + Json::Value destination(Json::objectValue); + destination["AccountID"]=NewcoinAddress(mAccountTo).GetString(); + + if(decorate) + { + LocalAccount::pointer lac=theApp->getWallet().getLocalAccount(mAccountFrom); + if(!!lac) source=lac->getJson(); + lac=theApp->getWallet().getLocalAccount(mAccountTo); + if(!!lac) destination=lac->getJson(); + } + ret["Source"]=source; + ret["Destination"]=destination; + return ret; +} diff --git a/Transaction.h b/Transaction.h index dac0fa145f..57664f1dba 100644 --- a/Transaction.h +++ b/Transaction.h @@ -1,10 +1,14 @@ #ifndef __TRANSACTION__ #define __TRANSACTION__ +#include + #include #include #include +#include "json/value.h" + #include "key.h" #include "uint256.h" #include "newcoin.pb.h" @@ -52,8 +56,7 @@ private: public: Transaction(); Transaction(const std::vector& rawTransaction, bool validate); - Transaction(TransStatus Status, LocalAccount::pointer fromLocal, uint32 fromSeq, const uint160& to, uint64 amount, - uint32 ident, uint32 ledger); + Transaction(LocalAccount::pointer fromLocal, const uint160& to, uint64 amount, uint32 ident, uint32 ledger); bool sign(LocalAccount::pointer fromLocalAccount); bool checkSign() const; @@ -96,6 +99,8 @@ public: bool operator<=(const Transaction&) const; bool operator>=(const Transaction&) const; + Json::Value getJson(bool decorate) const; + protected: static Transaction::pointer transactionFromSQL(const std::string& statement); Transaction(const uint256& transactionID, const uint160& accountFrom, const uint160& accountTo, diff --git a/Wallet.cpp b/Wallet.cpp index 5f4a478599..0ebb5f662b 100644 --- a/Wallet.cpp +++ b/Wallet.cpp @@ -4,6 +4,7 @@ #include "openssl/rand.h" #include "openssl/ec.h" +#include "boost/foreach.hpp" #include "boost/lexical_cast.hpp" #include "Wallet.h" @@ -361,6 +362,11 @@ std::string LocalAccount::getFullName() const return ret; } +bool LocalAccount::isLocked() const +{ + return mFamily->isLocked(); +} + std::string LocalAccount::getFamilyName() const { return mFamily->getShortName(); @@ -549,6 +555,15 @@ LocalAccount::pointer Wallet::getLocalAccount(const uint160& acctID) return ait->second; } +LocalAccount::pointer Wallet::findAccountForTransaction(uint64 amount) +{ + boost::recursive_mutex::scoped_lock sl(mLock); + for(std::map::iterator it=mAccounts.begin(); it!=mAccounts.end(); ++it) + if(!it->second->isLocked() && (it->second->getBalance()>=amount) ) + return it->second; + return LocalAccount::pointer(); +} + LocalAccount::pointer Wallet::parseAccount(const std::string& specifier) { // : or diff --git a/Wallet.h b/Wallet.h index cf719398b8..fb81f3f3d9 100644 --- a/Wallet.h +++ b/Wallet.h @@ -181,6 +181,7 @@ public: LocalAccount::pointer getLocalAccount(const uint160& famBase, int seq); LocalAccount::pointer getLocalAccount(const uint160& acctID); LocalAccount::pointer getNewLocalAccount(const uint160& family); + LocalAccount::pointer findAccountForTransaction(uint64 amount); uint160 peekKey(const uint160& family, int seq); std::string getPubGenHex(const uint160& famBase); std::string getShortName(const uint160& famBase);