Browse Source

Merge branch 'master' of /home/miguel/softs/twister

miguelfreitas
Miguel Freitas 11 years ago
parent
commit
699d7a0802
  1. 5
      README.md
  2. 18
      TODO
  3. 1
      libtorrent/include/libtorrent/kademlia/node.hpp
  4. 2
      libtorrent/src/kademlia/find_data.cpp
  5. 73
      libtorrent/src/kademlia/node.cpp
  6. 5
      libtorrent/src/torrent.cpp
  7. 18
      src/bitcoinrpc.cpp
  8. 7
      src/init.cpp
  9. 6
      src/net.cpp
  10. 4
      src/netbase.cpp
  11. 249
      src/twister.cpp
  12. 5
      src/twister.h
  13. 2
      twister-test.py

5
README.md

@ -41,6 +41,11 @@ Developers of either bitcoin or libtorrent are welcomed and will be granted
immediate write-access to the repository (a small retribution for immediate write-access to the repository (a small retribution for
bastardizing their codebases). bastardizing their codebases).
Compiling
---------
See `INSTALL`.
Testing Testing
------- -------

18
TODO

@ -8,6 +8,9 @@ pseudocode:
while( h > max_h ) while( h > max_h )
getTxIndex( "userX_h" ) => block h contains the previous tx getTxIndex( "userX_h" ) => block h contains the previous tx
- Until old public key is properly used, disable banning torrent peers due to bad piece hashes.
note: torrent.cpp line 3286 (function piece_failed), iteration to ban peers is disabled (continue).
- Count UTF8 chars in acceptSignedPost to proper limit the 140 characters. - Count UTF8 chars in acceptSignedPost to proper limit the 140 characters.
- Encrypt user_data (which contains all DMs) - Encrypt user_data (which contains all DMs)
@ -36,6 +39,7 @@ merkle tree inside that block. This resource propagation cannot be sent right af
registration for obvious reasons (no block yet, other nodes wouldn't accept the signed dht put). registration for obvious reasons (no block yet, other nodes wouldn't accept the signed dht put).
- Discuss and implement the acceptable level of spam per day (priorizing localization). - Discuss and implement the acceptable level of spam per day (priorizing localization).
DONE (except for the discussion part...)
- Implement the mention forwarding mechanism discussed in the paper so user don't need to do polling - Implement the mention forwarding mechanism discussed in the paper so user don't need to do polling
and can also be sure to receive all mentions. and can also be sure to receive all mentions.
@ -47,6 +51,18 @@ according to previous bittorrent research, would be enough to keep data availabl
probability). twister also persists keys to disk. As userbase increases, old post storage and probability). twister also persists keys to disk. As userbase increases, old post storage and
unreliable multivalued keys should better expire. Since those posts include the height and time, a unreliable multivalued keys should better expire. Since those posts include the height and time, a
policy may me defined. policy may me defined.
=> Implemented shouldDhtResourceExpire() which is tested on initialization but not really used yet.
- Check stored dht values if their signature is still valid before trying to refresh another node.
Key pair might have changed and currently we receive a lot of errors from other nodes.
- save_file() must truncate file.
- Save lastk field to post so torrent-less navigation through posts is possible. => DONE
- Implement dht-to-torrent gateway, the "swarm" resource (so poster may not need to be member
of his own torrent)
- - Estimate number of online followers by quering the "tracker" resource (implement a value within
this resource to report the number of torrent peers)

1
libtorrent/include/libtorrent/kademlia/node.hpp

@ -180,6 +180,7 @@ public:
void tick(); void tick();
bool refresh_storage(); bool refresh_storage();
bool has_expired(dht_storage_item const& item);
bool save_storage(entry &save) const; bool save_storage(entry &save) const;
void refresh(node_id const& id, find_data::nodes_callback const& f); void refresh(node_id const& id, find_data::nodes_callback const& f);
void bootstrap(std::vector<udp::endpoint> const& nodes void bootstrap(std::vector<udp::endpoint> const& nodes

2
libtorrent/src/kademlia/find_data.cpp

@ -208,7 +208,7 @@ bool find_data::invoke(observer_ptr o)
// im not going to ask trackers from myself // im not going to ask trackers from myself
if( o->id() == m_node.nid() ) if( o->id() == m_node.nid() )
return true; return false;
entry e; entry e;
e["z"] = "q"; e["z"] = "q";

73
libtorrent/src/kademlia/node.cpp

@ -55,6 +55,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/rsa.hpp" #include "libtorrent/rsa.hpp"
#include "../../src/twister.h" #include "../../src/twister.h"
//#define ENABLE_DHT_ITEM_EXPIRE
namespace libtorrent { namespace dht namespace libtorrent { namespace dht
{ {
@ -502,6 +503,12 @@ bool node_impl::refresh_storage() {
if( lsto.size() == 1 ) { if( lsto.size() == 1 ) {
dht_storage_item const& item = lsto.front(); dht_storage_item const& item = lsto.front();
#ifdef ENABLE_DHT_ITEM_EXPIRE
if( has_expired(item) ) {
continue;
}
#endif
lazy_entry p; lazy_entry p;
int pos; int pos;
error_code err; error_code err;
@ -537,6 +544,49 @@ bool node_impl::refresh_storage() {
return did_something; return did_something;
} }
bool node_impl::has_expired(dht_storage_item const& item) {
if (!verifySignature(item.p, item.sig_user, item.sig_p)) {
// invalid signature counts as expired
printf("node_impl::has_expired verifySignature failed\n");
return true;
}
lazy_entry arg_ent;
int pos;
error_code err;
// FIXME: optimize to avoid bdecode (store seq separated, etc)
int ret = lazy_bdecode(item.p.data(), item.p.data() + item.p.size(), arg_ent, err, &pos, 10, 500);
const static key_desc_t msg_desc[] = {
{"v", lazy_entry::none_t, 0, 0},
{"seq", lazy_entry::int_t, 0, key_desc_t::optional},
{"time", lazy_entry::int_t, 0, 0},
{"height", lazy_entry::int_t, 0, 0},
{"target", lazy_entry::dict_t, 0, key_desc_t::parse_children},
{"n", lazy_entry::string_t, 0, 0},
{"r", lazy_entry::string_t, 0, 0},
{"t", lazy_entry::string_t, 0, 0},
};
enum {mk_v = 0, mk_seq, mk_time, mk_height,
mk_target, mk_n, mk_r, mk_t};
// attempt to parse the message
lazy_entry const* msg_keys[8];
char error_string[200];
if (!verify_message(&arg_ent, msg_desc, msg_keys, 8, error_string, sizeof(error_string)))
{
printf("node_impl::has_expired verify_message failed\n");
// parse error (how come?) counts as expired
return true;
}
bool multi = (msg_keys[mk_t]->string_value() == "m");
int height = msg_keys[mk_height]->int_value();
std::string resource = msg_keys[mk_r]->string_value();
return shouldDhtResourceExpire(resource, multi, height);
}
bool node_impl::save_storage(entry &save) const { bool node_impl::save_storage(entry &save) const {
bool did_something = false; bool did_something = false;
@ -593,7 +643,16 @@ void node_impl::load_storage(entry const* e) {
item.p = j->find_key("p")->string(); item.p = j->find_key("p")->string();
item.sig_p = j->find_key("sig_p")->string(); item.sig_p = j->find_key("sig_p")->string();
item.sig_user = j->find_key("sig_user")->string(); item.sig_user = j->find_key("sig_user")->string();
to_add.push_back(item);
// just for printf for now
bool expired = has_expired(item);
#ifdef ENABLE_DHT_ITEM_EXPIRE
if( !expired ) {
#endif
to_add.push_back(item);
#ifdef ENABLE_DHT_ITEM_EXPIRE
}
#endif
} }
m_storage_table.insert(std::make_pair(target, to_add)); m_storage_table.insert(std::make_pair(target, to_add));
} }
@ -1152,6 +1211,18 @@ void node_impl::incoming_request(msg const& m, entry& e)
return; return;
} }
/* we can't check username, otherwise we break hashtags etc.
if (multi && !usernameExists(msg_keys[mk_n]->string_value())) {
incoming_error(e, "unknown user for resource");
return;
}
*/
if (msg_keys[mk_r]->string_value().size() > 32) {
incoming_error(e, "resource name too big");
return;
}
if (!multi && (!msg_keys[mk_seq] || msg_keys[mk_seq]->int_value() < 0)) { if (!multi && (!msg_keys[mk_seq] || msg_keys[mk_seq]->int_value() < 0)) {
incoming_error(e, "seq is required for single"); incoming_error(e, "seq is required for single");
return; return;

5
libtorrent/src/torrent.cpp

@ -3283,6 +3283,9 @@ namespace libtorrent
for (std::set<void*>::iterator i = peers.begin() for (std::set<void*>::iterator i = peers.begin()
, end(peers.end()); i != end; ++i) , end(peers.end()); i != end; ++i)
{ {
// [MF] FIXME FIXME: BANNING BY FAILED HASH DISABLED - READ TODO!
continue;
policy::peer* p = static_cast<policy::peer*>(*i); policy::peer* p = static_cast<policy::peer*>(*i);
if (p == 0) continue; if (p == 0) continue;
TORRENT_ASSERT(p->in_use); TORRENT_ASSERT(p->in_use);
@ -3337,7 +3340,7 @@ namespace libtorrent
if (p->connection) if (p->connection)
{ {
#ifdef TORRENT_LOGGING #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
debug_log("*** BANNING PEER: \"%s\" Too many corrupt pieces" debug_log("*** BANNING PEER: \"%s\" Too many corrupt pieces"
, print_endpoint(p->ip()).c_str()); , print_endpoint(p->ip()).c_str());
#endif #endif

18
src/bitcoinrpc.cpp

@ -852,7 +852,7 @@ void StartRPCThreads()
} }
rpc_worker_group = new boost::thread_group(); rpc_worker_group = new boost::thread_group();
for (int i = 0; i < GetArg("-rpcthreads", 4); i++) for (int i = 0; i < GetArg("-rpcthreads", 10); i++)
rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
} }
@ -983,11 +983,23 @@ void ServiceConnection(AcceptedConnection *conn)
std::vector<char> file_data; std::vector<char> file_data;
if( load_file(strURI.c_str(), file_data) == 0 ) { if( load_file(strURI.c_str(), file_data) == 0 ) {
std::string str(file_data.data(), file_data.size()); std::string str(file_data.data(), file_data.size());
conn->stream() << HTTPReply(HTTP_OK, str, false, "text/html") << std::flush; const char *contentType = "text/html";
if( strURI.find(".js") != std::string::npos )
contentType = "text/javascript";
if( strURI.find(".css") != std::string::npos )
contentType = "text/css";
if( strURI.find(".png") != std::string::npos )
contentType = "image/png";
if( strURI.find(".ttf") != std::string::npos )
contentType = "application/x-font-ttf";
if( strURI.find(".jpg") != std::string::npos ||
strURI.find(".jpeg") != std::string::npos )
contentType = "image/jpeg";
conn->stream() << HTTPReply(HTTP_OK, str, false, contentType) << std::flush;
} else { } else {
conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush; conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush;
} }
break; continue;
} }
// Check authorization // Check authorization

7
src/init.cpp

@ -726,6 +726,11 @@ bool AppInit2(boost::thread_group& threadGroup)
strLoadError = _("Corrupted block database detected"); strLoadError = _("Corrupted block database detected");
break; break;
} }
if( mapBlockIndex.size() > 1000 && nBestHeight == 0 ) {
strLoadError = _("mapBlockIndex detected but nBestHeight still zero, trying to repair (reindex)");
break;
}
} catch(std::exception &e) { } catch(std::exception &e) {
strLoadError = _("Error opening block database"); strLoadError = _("Error opening block database");
break; break;
@ -738,7 +743,7 @@ bool AppInit2(boost::thread_group& threadGroup)
// first suggest a reindex // first suggest a reindex
if (!fReset) { if (!fReset) {
/*bool fRet =*/ uiInterface.ThreadSafeMessageBox( /*bool fRet =*/ uiInterface.ThreadSafeMessageBox(
strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"), strLoadError + ".\n\n" + _("Do you want to rebuild the block database now? (assuming YES)"),
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
if (true /* [MF] fRet*/) { if (true /* [MF] fRet*/) {
fReindex = true; fReindex = true;

6
src/net.cpp

@ -462,9 +462,9 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
/// debug print /// debug print
printf("trying connection %s lastseen=%.1fhrs\n", // printf("trying connection %s lastseen=%.1fhrs\n",
pszDest ? pszDest : addrConnect.ToString().c_str(), // pszDest ? pszDest : addrConnect.ToString().c_str(),
pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); // pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
// Connect // Connect
SOCKET hSocket; SOCKET hSocket;

4
src/netbase.cpp

@ -359,7 +359,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout);
if (nRet == 0) if (nRet == 0)
{ {
printf("connection timeout\n"); //printf("connection timeout\n");
closesocket(hSocket); closesocket(hSocket);
return false; return false;
} }
@ -393,7 +393,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
else else
#endif #endif
{ {
printf("connect() failed: %i\n",WSAGetLastError()); //printf("connect() failed: %i\n",WSAGetLastError());
closesocket(hSocket); closesocket(hSocket);
return false; return false;
} }

249
src/twister.cpp

@ -31,6 +31,7 @@ twister::twister()
#include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/aux_/session_impl.hpp"
#define DEBUG_ACCEPT_POST 1 #define DEBUG_ACCEPT_POST 1
#define DEBUG_EXPIRE_DHT_ITEM 1
using namespace libtorrent; using namespace libtorrent;
static session *ses = NULL; static session *ses = NULL;
@ -41,13 +42,19 @@ static map<sha1_hash, alert_manager*> m_dhtgetMap;
static CCriticalSection cs_twister; static CCriticalSection cs_twister;
static map<std::string, bool> m_specialResources; static map<std::string, bool> m_specialResources;
enum ExpireResType { SimpleNoExpire, NumberedNoExpire, PostNoExpireRecent };
static map<std::string, ExpireResType> m_noExpireResources;
static map<std::string, torrent_handle> m_userTorrent; static map<std::string, torrent_handle> m_userTorrent;
static std::string m_preferredSpamLang = "[en]"; static std::string m_preferredSpamLang = "[en]";
static std::string m_receivedSpamMsgStr; static std::string m_receivedSpamMsgStr;
static std::string m_receivedSpamUserStr; static std::string m_receivedSpamUserStr;
static int64 m_lastSpamTime = 0;
static std::map<std::string,UserData> m_users; static std::map<std::string,UserData> m_users;
#define USER_DATA_FILE "user_data"
#define GLOBAL_DATA_FILE "global_data"
sha1_hash dhtTargetHash(std::string const &username, std::string const &resource, std::string const &type) sha1_hash dhtTargetHash(std::string const &username, std::string const &resource, std::string const &type)
{ {
entry target; entry target;
@ -95,6 +102,47 @@ int lastPostKfromTorrent(std::string const &username)
return status.last_have; return status.last_have;
} }
int saveGlobalData(std::string const& filename)
{
LOCK(cs_twister);
entry globalDict;
globalDict["preferredSpamLang"] = m_preferredSpamLang;
globalDict["receivedSpamMsg"] = m_receivedSpamMsgStr;
globalDict["receivedSpamUser"] = m_receivedSpamUserStr;
globalDict["lastSpamTime"] = m_lastSpamTime;
std::vector<char> buf;
bencode(std::back_inserter(buf), globalDict);
return save_file(filename, buf);
}
int loadGlobalData(std::string const& filename)
{
LOCK(cs_twister);
std::vector<char> in;
if (load_file(filename, in) == 0) {
lazy_entry userDict;
error_code ec;
if (lazy_bdecode(&in[0], &in[0] + in.size(), userDict, ec) == 0) {
if( userDict.type() != lazy_entry::dict_t ) goto data_error;
m_preferredSpamLang = userDict.dict_find_string_value("preferredSpamLang");
m_receivedSpamMsgStr = userDict.dict_find_string_value("receivedSpamMsg");
m_receivedSpamUserStr = userDict.dict_find_string_value("receivedSpamUser");
m_lastSpamTime = userDict.dict_find_int_value("lastSpamTime");
return 0;
}
}
return -1;
data_error:
printf("loadGlobalData: unexpected bencode type - global_data corrupt!\n");
return -2;
}
void ThreadWaitExtIP() void ThreadWaitExtIP()
{ {
RenameThread("wait-extip"); RenameThread("wait-extip");
@ -159,6 +207,9 @@ void ThreadWaitExtIP()
//settings.dht_announce_interval = 60; // test //settings.dht_announce_interval = 60; // test
//settings.min_announce_interval = 60; // test //settings.min_announce_interval = 60; // test
settings.anonymous_mode = false; // (false => send peer_id, avoid connecting to itself) settings.anonymous_mode = false; // (false => send peer_id, avoid connecting to itself)
// disable read cache => there is still some bug due to twister piece size changes
settings.use_read_cache = false;
settings.cache_size = 0;
ses->set_settings(settings); ses->set_settings(settings);
printf("libtorrent + dht started\n"); printf("libtorrent + dht started\n");
@ -171,9 +222,12 @@ void ThreadWaitExtIP()
break; break;
} }
boost::filesystem::path globalDataPath = GetDataDir() / GLOBAL_DATA_FILE;
loadGlobalData(globalDataPath.string());
{ {
LOCK(cs_twister); LOCK(cs_twister);
boost::filesystem::path userDataPath = GetDataDir() / "user_data"; boost::filesystem::path userDataPath = GetDataDir() / USER_DATA_FILE;
loadUserData(userDataPath.string(), m_users); loadUserData(userDataPath.string(), m_users);
printf("loaded user_data for %zd users\n", m_users.size()); printf("loaded user_data for %zd users\n", m_users.size());
@ -389,6 +443,12 @@ void startSessionTorrent(boost::thread_group& threadGroup)
m_specialResources["tracker"] = true; m_specialResources["tracker"] = true;
m_specialResources["swarm"] = true; m_specialResources["swarm"] = true;
// these are the resources which shouldn't expire
m_noExpireResources["avatar"] = SimpleNoExpire;
m_noExpireResources["profile"] = SimpleNoExpire;
m_noExpireResources["following"] = NumberedNoExpire;
m_noExpireResources["status"] = SimpleNoExpire;
m_noExpireResources["post"] = PostNoExpireRecent;
threadGroup.create_thread(boost::bind(&ThreadWaitExtIP)); threadGroup.create_thread(boost::bind(&ThreadWaitExtIP));
threadGroup.create_thread(boost::bind(&ThreadMaintainDHTNodes)); threadGroup.create_thread(boost::bind(&ThreadMaintainDHTNodes));
@ -442,18 +502,21 @@ void stopSessionTorrent()
entry session_state; entry session_state;
ses->save_state(session_state); ses->save_state(session_state);
std::vector<char> out; std::vector<char> out;
bencode(std::back_inserter(out), session_state); bencode(std::back_inserter(out), session_state);
boost::filesystem::path sesStatePath = GetDataDir() / "ses_state"; boost::filesystem::path sesStatePath = GetDataDir() / "ses_state";
save_file(sesStatePath.string(), out); save_file(sesStatePath.string(), out);
delete ses; delete ses;
ses = NULL; ses = NULL;
} }
boost::filesystem::path globalDataPath = GetDataDir() / GLOBAL_DATA_FILE;
saveGlobalData(globalDataPath.string());
if( m_users.size() ) { if( m_users.size() ) {
printf("saving user_data (followers and DMs)...\n"); printf("saving user_data (followers and DMs)...\n");
boost::filesystem::path userDataPath = GetDataDir() / "user_data"; boost::filesystem::path userDataPath = GetDataDir() / USER_DATA_FILE;
saveUserData(userDataPath.string(), m_users); saveUserData(userDataPath.string(), m_users);
} }
@ -579,9 +642,11 @@ bool processReceivedDM(lazy_entry const* post)
} else { } else {
std::string textOut; std::string textOut;
if( key.Decrypt(sec, textOut) ) { if( key.Decrypt(sec, textOut) ) {
/* this printf is good for debug, but bad for security.
printf("Received DM for user '%s' text = '%s'\n", printf("Received DM for user '%s' text = '%s'\n",
item.second.username.c_str(), item.second.username.c_str(),
textOut.c_str()); textOut.c_str());
*/
std::string n = post->dict_find_string_value("n"); std::string n = post->dict_find_string_value("n");
@ -713,6 +778,15 @@ bool validatePostNumberForUser(std::string const &username, int k)
return true; return true;
} }
bool usernameExists(std::string const &username)
{
CTransaction txOut;
uint256 hashBlock;
uint256 userhash = SerializeHash(username);
return GetTransaction(userhash, txOut, hashBlock);
}
/* /*
"userpost" : "userpost" :
{ {
@ -808,11 +882,90 @@ int getBestHeight()
return nBestHeight; return nBestHeight;
} }
bool shouldDhtResourceExpire(std::string resource, bool multi, int height)
{
if ((height + BLOCK_AGE_TO_EXPIRE_DHT_ENTRY) < getBestHeight() ) {
if( multi ) {
#ifdef DEBUG_EXPIRE_DHT_ITEM
printf("shouldDhtResourceExpire: expiring resource multi '%s'\n", resource.c_str());
#endif
return true;
}
// extract basic resource string (without numbering)
std::string resourceBasic;
for(size_t i = 0; i < resource.size() && isalpha(resource.at(i)); i++) {
resourceBasic.push_back(resource.at(i));
}
int resourceNumber = -1;
if( resource.length() > resourceBasic.length() ) {
// make sure it is a valid number following (all digits)
if( resource.at(resourceBasic.length()) == '0' &&
resource.size() > resourceBasic.length() + 1 ){
// leading zeros not allowed
} else {
size_t i;
for(i = resourceBasic.length(); i < resource.size() &&
isdigit(resource.at(i)); i++) {
}
if(i == resource.size()) {
resourceNumber = atoi( resource.c_str() + resourceBasic.length() );
}
}
}
if( !m_noExpireResources.count(resourceBasic) ) {
// unknown resource. expire it.
#ifdef DEBUG_EXPIRE_DHT_ITEM
printf("shouldDhtResourceExpire: expiring non-special resource '%s'\n", resource.c_str());
#endif
return true;
} else {
if( m_noExpireResources[resourceBasic] == SimpleNoExpire &&
resource.length() > resourceBasic.length() ) {
// this resource admits no number. expire it!
#ifdef DEBUG_EXPIRE_DHT_ITEM
printf("shouldDhtResourceExpire: expiring resource with unexpected numbering '%s'\n", resource.c_str());
#endif
return true;
}
if( m_noExpireResources[resourceBasic] == NumberedNoExpire &&
(resourceNumber < 0 || resourceNumber > 200) ) {
// try keeping a sane number here, otherwise expire it!
#ifdef DEBUG_EXPIRE_DHT_ITEM
printf("shouldDhtResourceExpire: expiring numbered resource with no sane number '%s'\n", resource.c_str());
#endif
return true;
}
if( m_noExpireResources[resourceBasic] == PostNoExpireRecent && resourceNumber < 0 ) {
#ifdef DEBUG_EXPIRE_DHT_ITEM
printf("shouldDhtResourceExpire: expiring post with invalid numbering '%s'\n", resource.c_str());
#endif
return true;
}
if( m_noExpireResources[resourceBasic] == PostNoExpireRecent &&
(height + BLOCK_AGE_TO_EXPIRE_DHT_POSTS) < getBestHeight() ) {
#ifdef DEBUG_EXPIRE_DHT_ITEM
printf("shouldDhtResourceExpire: expiring old post resource '%s'\n", resource.c_str());
#endif
return true;
}
}
}
return false;
}
void receivedSpamMessage(std::string const &message, std::string const &user) void receivedSpamMessage(std::string const &message, std::string const &user)
{ {
LOCK(cs_twister); LOCK(cs_twister);
if( !m_receivedSpamMsgStr.length() || bool hasSingleLangCode = (message.find("[") == message.rfind("["));
(m_preferredSpamLang.length() && message.find(m_preferredSpamLang) != string::npos) ) { bool hasPreferredLang = m_preferredSpamLang.length();
bool isSameLang = hasPreferredLang && hasSingleLangCode &&
message.find(m_preferredSpamLang) != string::npos;
bool currentlyEmpty = !m_receivedSpamMsgStr.length();
if( currentlyEmpty || (isSameLang && rand() < (RAND_MAX/2)) ) {
m_receivedSpamMsgStr = message; m_receivedSpamMsgStr = message;
m_receivedSpamUserStr = user; m_receivedSpamUserStr = user;
} }
@ -904,6 +1057,34 @@ Value dhtget(const Array& params, bool fHelp)
return ret; return ret;
} }
int findLastPublicPostLocalUser( std::string strUsername )
{
int lastk = -1;
LOCK(cs_twister);
if( strUsername.size() && m_userTorrent.count(strUsername) &&
m_userTorrent[strUsername].is_valid() ){
std::vector<std::string> pieces;
int max_id = std::numeric_limits<int>::max();
int since_id = -1;
m_userTorrent[strUsername].get_pieces(pieces, 1, max_id, since_id, USERPOST_FLAG_RT);
if( pieces.size() ) {
string const& piece = pieces.front();
lazy_entry v;
int pos;
error_code ec;
if (lazy_bdecode(piece.data(), piece.data()+piece.size(), v, ec, &pos) == 0) {
lazy_entry const* post = v.dict_find_dict("userpost");
lastk = post->dict_find_int_value("k",-1);
}
}
}
return lastk;
}
Value newpostmsg(const Array& params, bool fHelp) Value newpostmsg(const Array& params, bool fHelp)
{ {
if (fHelp || (params.size() != 3 && params.size() != 5)) if (fHelp || (params.size() != 3 && params.size() != 5))
@ -927,6 +1108,11 @@ Value newpostmsg(const Array& params, bool fHelp)
} }
entry v; entry v;
// [MF] Warning: findLastPublicPostLocalUser requires that we follow ourselves
int lastk = findLastPublicPostLocalUser(strUsername);
if( lastk >= 0 )
v["userpost"]["lastk"] = lastk;
if( !createSignedUserpost(v, strUsername, k, strMsg, if( !createSignedUserpost(v, strUsername, k, strMsg,
NULL, NULL, NULL, NULL, NULL, NULL,
strReplyN, replyK) ) strReplyN, replyK) )
@ -1046,6 +1232,11 @@ Value newrtmsg(const Array& params, bool fHelp)
entry const *sig_rt= vrt.find_key("sig_userpost"); entry const *sig_rt= vrt.find_key("sig_userpost");
entry v; entry v;
// [MF] Warning: findLastPublicPostLocalUser requires that we follow ourselves
int lastk = findLastPublicPostLocalUser(strUsername);
if( lastk >= 0 )
v["userpost"]["lastk"] = lastk;
if( !createSignedUserpost(v, strUsername, k, "", if( !createSignedUserpost(v, strUsername, k, "",
rt, sig_rt, NULL, rt, sig_rt, NULL,
std::string(""), 0) ) std::string(""), 0) )
@ -1140,24 +1331,26 @@ Value getposts(const Array& params, bool fHelp)
{ {
LOCK(cs_twister); LOCK(cs_twister);
if( m_receivedSpamMsgStr.length() ) { // we must agree on an acceptable level here
// we must agree on an acceptable level here // what about one every eight hours? (not cumulative)
if( rand() < (RAND_MAX/10) ) { if( m_receivedSpamMsgStr.length() && GetAdjustedTime() > m_lastSpamTime + (8*3600) ) {
entry v; m_lastSpamTime = GetAdjustedTime();
entry &userpost = v["userpost"];
entry v;
userpost["n"] = m_receivedSpamUserStr; entry &userpost = v["userpost"];
userpost["k"] = 1;
userpost["time"] = GetAdjustedTime(); userpost["n"] = m_receivedSpamUserStr;
userpost["height"] = getBestHeight(); userpost["k"] = 1;
userpost["time"] = GetAdjustedTime();
userpost["msg"] = m_receivedSpamMsgStr; userpost["height"] = getBestHeight();
unsigned char vchSig[65]; userpost["msg"] = m_receivedSpamMsgStr;
RAND_bytes(vchSig,sizeof(vchSig));
v["sig_userpost"] = std::string((const char *)vchSig, sizeof(vchSig)); unsigned char vchSig[65];
ret.insert(ret.begin(),entryToJson(v)); RAND_bytes(vchSig,sizeof(vchSig));
} v["sig_userpost"] = std::string((const char *)vchSig, sizeof(vchSig));
ret.insert(ret.begin(),entryToJson(v));
m_receivedSpamMsgStr = ""; m_receivedSpamMsgStr = "";
m_receivedSpamUserStr = ""; m_receivedSpamUserStr = "";
} }

5
src/twister.h

@ -10,6 +10,9 @@
#define USERPOST_FLAG_RT 0x01 #define USERPOST_FLAG_RT 0x01
#define USERPOST_FLAG_DM 0x02 #define USERPOST_FLAG_DM 0x02
#define BLOCK_AGE_TO_EXPIRE_DHT_ENTRY (2016) // about 2 weeks
#define BLOCK_AGE_TO_EXPIRE_DHT_POSTS (4320*6) // about 6 months
class twister class twister
{ {
@ -26,9 +29,11 @@ bool verifySignature(std::string const &strMessage, std::string const &strUserna
bool acceptSignedPost(char const *data, int data_size, std::string username, int seq, std::string &errmsg, boost::uint32_t *flags); bool acceptSignedPost(char const *data, int data_size, std::string username, int seq, std::string &errmsg, boost::uint32_t *flags);
bool validatePostNumberForUser(std::string const &username, int k); bool validatePostNumberForUser(std::string const &username, int k);
bool usernameExists(std::string const &username);
void receivedSpamMessage(std::string const &message, std::string const &user); void receivedSpamMessage(std::string const &message, std::string const &user);
int getBestHeight(); int getBestHeight();
bool shouldDhtResourceExpire(std::string resource, bool multi, int height);
#endif // TWISTER_H #endif // TWISTER_H

2
twister-test.py

@ -3,7 +3,7 @@
import os,sys,time import os,sys,time
ext_ip = os.environ['EXTIP'] ext_ip = os.environ['EXTIP']
twister = "../twister-qt-build-desktop/twisterd" twister = "./twisterd"
cmd = sys.argv[1] cmd = sys.argv[1]
n = int(sys.argv[2]) n = int(sys.argv[2])

Loading…
Cancel
Save