From d9fdca340cf89137fe514cc2ad3f177ea4f5c1c9 Mon Sep 17 00:00:00 2001 From: Miguel Freitas Date: Sat, 20 Jul 2013 19:54:48 -0300 Subject: [PATCH] implement CScript => string extraction, check username constraits, improve username hash --- src/bitcoinrpc.cpp | 4 ++-- src/core.cpp | 7 ++++++- src/main.cpp | 45 +++++++++++++++++++++++++++++++++---------- src/main.h | 2 ++ src/rpcblockchain.cpp | 15 ++++++++++++++- src/rpcwallet.cpp | 2 +- src/script.cpp | 10 ++++++++++ src/script.h | 39 +++++++++++++++++++++++++++++++++++++ 8 files changed, 109 insertions(+), 15 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 11fac422..b5ab8865 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -176,10 +176,10 @@ Value stop(const Array& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( "stop\n" - "Stop Bitcoin server."); + "Stop Twister server."); // Shutdown will take long enough that the response should get back StartShutdown(); - return "Bitcoin server stopping"; + return "Twister server stopping"; } diff --git a/src/core.cpp b/src/core.cpp index ffcd06a4..93d2cb59 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -80,7 +80,12 @@ uint256 CTransaction::GetHash() const uint256 CTransaction::GetUsernameHash() const { - return SerializeHash(userName); + if(userName.IsSmallString()) { + return SerializeHash(userName.ExtractSmallString()); + } + // [MF] TODO: remove this assert later, it will fail for spammessage. + assert(!"username not small string"); + return uint256(); } diff --git a/src/main.cpp b/src/main.cpp index b478396e..ad48664c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -408,7 +408,9 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) CBlock blockTmp; if (pblock == NULL) { - CCoins coins; + /* [MF] FIXME: Use GetTransaction to obtain block? */ + /* + CCoins coins; if (pcoinsTip->GetCoins(GetUsernameHash(), coins)) { CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); if (pindex) { @@ -417,6 +419,8 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) pblock = &blockTmp; } } + */ + return 0; } if (pblock) { @@ -452,7 +456,26 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) - +bool CheckUsername(const std::string &userName, CValidationState &state) +{ + if (!userName.size() || userName.size() > 16 ) + return state.DoS(10, error("CheckUsername() : username size out of limits")); + BOOST_FOREACH(char c, userName) { + if( isalpha(c) ) { + if( !islower(c) ) + return state.DoS(10, error("CheckUsername() : username must be lowercase")); + } else if( isdigit(c) ) { + // digit ok + } else if( isspace(c) ) { + return state.DoS(10, error("CheckUsername() : username spaces not allowed")); + } else if( c == '_' ) { + // underscore ok + } else { + return state.DoS(10, error("CheckUsername() : invalid username character")); + } + } + return true; +} // [MF] check tx consistency and pow, not duplicated id. @@ -467,10 +490,12 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) } else { if (tx.userName.empty()) return state.DoS(10, error("CheckTransaction() : username empty")); - if (tx.userName.size() > MAX_USERNAME_SIZE) - return state.DoS(100, error("CheckTransaction() : username too big")); if (tx.pubKey.empty()) return state.DoS(10, error("CheckTransaction() : pubKey empty")); + if (!tx.userName.IsSmallString()) + return state.DoS(10, error("CheckTransaction() : username not smallstring")); + if (!CheckUsername( tx.userName.ExtractSmallString(), state )) + return state.DoS(10, error("CheckTransaction() : username check failed")); } // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) @@ -574,7 +599,7 @@ bool CTxMemPool::addUnchecked(const uint256& userhash, CTransaction &tx) bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) { // Remove transaction from memory pool - { + if( !tx.IsSpamMessage() ){ LOCK(cs); uint256 hash = tx.GetUsernameHash(); if (mapTx.count(hash)) @@ -1095,7 +1120,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C } // Do not allow blocks that contain transactions which 'overwrite' older transactions, - for (unsigned int i = 0; i < block.vtx.size(); i++) { + for (unsigned int i = 1; i < block.vtx.size(); i++) { if( pblocktree->HaveTxIndex(block.vtx[i].GetUsernameHash()) ) return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction")); @@ -1110,16 +1135,16 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C int64 nStart = GetTimeMicros(); CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; - vPos.reserve(block.vtx.size()); + vPos.reserve(block.vtx.size()-1); for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction &tx = block.vtx[i]; CTxUndo txundo; - if (!tx.IsSpamMessage()) + if (!tx.IsSpamMessage()) { blockundo.vtxundo.push_back(txundo); - - vPos.push_back(std::make_pair(tx.GetUsernameHash(), pos)); + vPos.push_back(std::make_pair(tx.GetUsernameHash(), pos)); + } pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64 nTime = GetTimeMicros() - nStart; diff --git a/src/main.h b/src/main.h index b6b9fd1b..b7f8724e 100644 --- a/src/main.h +++ b/src/main.h @@ -270,6 +270,8 @@ inline bool AllowFree(double dPriority) return dPriority > COIN * 144 / 250; } +bool CheckUsername(const std::string &userName, CValidationState &state); + // Context-independent validity checks bool CheckTransaction(const CTransaction& tx, CValidationState& state); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index a26a2ae7..584090f0 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -55,9 +55,22 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); Array txs; - BOOST_FOREACH(const CTransaction&tx, block.vtx) + Array txuserhashes; + Array usernames; + std::string spamMessage; + BOOST_FOREACH(const CTransaction&tx, block.vtx) { txs.push_back(tx.GetHash().GetHex()); + if( tx.IsSpamMessage() ) { + spamMessage = tx.message.ExtractPushDataString(0); + } else { + txuserhashes.push_back(tx.GetUsernameHash().GetHex()); + usernames.push_back(tx.userName.ExtractSmallString()); + } + } result.push_back(Pair("tx", txs)); + result.push_back(Pair("spamMessage", spamMessage)); + result.push_back(Pair("userhashes", txuserhashes)); + result.push_back(Pair("usernames", usernames)); result.push_back(Pair("time", (boost::int64_t)block.GetBlockTime())); result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce)); result.push_back(Pair("bits", HexBits(block.nBits))); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 8c56f376..5d115b7f 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1304,7 +1304,7 @@ Value encryptwallet(const Array& params, bool fHelp) // slack space in .dat files; that is bad if the old data is // unencrypted private keys. So: StartShutdown(); - return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; + return "wallet encrypted; Twister server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; } class DescribeAddressVisitor : public boost::static_visitor diff --git a/src/script.cpp b/src/script.cpp index e1dc4445..9190fc42 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1777,6 +1777,16 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } +bool CScript::IsSmallString() const +{ + if( !size() || this->at(0) >= OP_PUSHDATA1 ) + return false; + unsigned int opSize = this->at(0); + if( this->size() != opSize + 1 ) + return false; + return true; +} + class CScriptVisitor : public boost::static_visitor { private: diff --git a/src/script.h b/src/script.h index 03afe8b6..8c6ba4e8 100644 --- a/src/script.h +++ b/src/script.h @@ -533,6 +533,8 @@ public: bool IsPayToScriptHash() const; + bool IsSmallString() const; + // Called by IsStandardTx bool IsPushOnly() const { @@ -548,6 +550,43 @@ public: return true; } + bool ExtractPushData( std::vector< std::vector > &vData) const + { + vData.clear(); + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + std::vector vch; + if (!GetOp(pc, opcode, vch)) + return false; + if (opcode > OP_16) + return false; + vData.push_back(vch); + } + return true; + } + + // only for username + std::string ExtractSmallString() const + { + if( !IsSmallString() ) + return std::string(); + + unsigned int opSize = this->at(0); + return std::string((const char*)&(this[1]), opSize); + } + + std::string ExtractPushDataString(int n) const + { + std::vector< std::vector > vData; + if( ExtractPushData(vData) && vData.size() >= n+1 ) { + std::string str((const char*)vData[n].data(), vData[n].size()); + return str; + } + return std::string(); + } + void SetDestination(const CTxDestination& address); void SetMultisig(int nRequired, const std::vector& keys);