FLO-wallet-core/src/test/net_tests.cpp
Wladimir J. van der Laan 5eaaa83ac1 Kill insecure_random and associated global state
There are only a few uses of `insecure_random` outside the tests.
This PR replaces uses of insecure_random (and its accompanying global
state) in the core code with an FastRandomContext that is automatically
seeded on creation.

This is meant to be used for inner loops. The FastRandomContext
can be in the outer scope, or the class itself, then rand32() is used
inside the loop. Useful e.g. for pushing addresses in CNode or the fee
rounding, or randomization for coin selection.

As a context is created per purpose, thus it gets rid of
cross-thread unprotected shared usage of a single set of globals, this
should also get rid of the potential race conditions.

- I'd say TxMempool::check is not called enough to warrant using a special
  fast random context, this is switched to GetRand() (open for
  discussion...)

- The use of `insecure_rand` in ConnectThroughProxy has been replaced by
  an atomic integer counter. The only goal here is to have a different
  credentials pair for each connection to go on a different Tor circuit,
  it does not need to be random nor unpredictable.

- To avoid having a FastRandomContext on every CNode, the context is
  passed into PushAddress as appropriate.

There remains an insecure_random for test usage in `test_random.h`.
2016-10-17 13:08:35 +02:00

178 lines
5.3 KiB
C++

// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "addrman.h"
#include "test/test_bitcoin.h"
#include <string>
#include <boost/test/unit_test.hpp>
#include "hash.h"
#include "serialize.h"
#include "streams.h"
#include "net.h"
#include "netbase.h"
#include "chainparams.h"
using namespace std;
class CAddrManSerializationMock : public CAddrMan
{
public:
virtual void Serialize(CDataStream& s, int nType, int nVersionDummy) const = 0;
//! Ensure that bucket placement is always the same for testing purposes.
void MakeDeterministic()
{
nKey.SetNull();
insecure_rand = FastRandomContext(true);
}
};
class CAddrManUncorrupted : public CAddrManSerializationMock
{
public:
void Serialize(CDataStream& s, int nType, int nVersionDummy) const
{
CAddrMan::Serialize(s, nType, nVersionDummy);
}
};
class CAddrManCorrupted : public CAddrManSerializationMock
{
public:
void Serialize(CDataStream& s, int nType, int nVersionDummy) const
{
// Produces corrupt output that claims addrman has 20 addrs when it only has one addr.
unsigned char nVersion = 1;
s << nVersion;
s << ((unsigned char)32);
s << nKey;
s << 10; // nNew
s << 10; // nTried
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
s << nUBuckets;
CService serv;
Lookup("252.1.1.1", serv, 7777, false);
CAddress addr = CAddress(serv, NODE_NONE);
CNetAddr resolved;
LookupHost("252.2.2.2", resolved, false);
CAddrInfo info = CAddrInfo(addr, resolved);
s << info;
}
};
CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman)
{
CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
ssPeersIn << FLATDATA(Params().MessageStart());
ssPeersIn << _addrman;
std::string str = ssPeersIn.str();
vector<unsigned char> vchData(str.begin(), str.end());
return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
}
BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(caddrdb_read)
{
CAddrManUncorrupted addrmanUncorrupted;
addrmanUncorrupted.MakeDeterministic();
CService addr1, addr2, addr3;
Lookup("250.7.1.1", addr1, 8333, false);
Lookup("250.7.2.2", addr2, 9999, false);
Lookup("250.7.3.3", addr3, 9999, false);
// Add three addresses to new table.
CService source;
Lookup("252.5.1.1", source, 8333, false);
addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source);
addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source);
addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source);
// Test that the de-serialization does not throw an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
bool exceptionThrown = false;
CAddrMan addrman1;
BOOST_CHECK(addrman1.size() == 0);
try {
unsigned char pchMsgTmp[4];
ssPeers1 >> FLATDATA(pchMsgTmp);
ssPeers1 >> addrman1;
} catch (const std::exception& e) {
exceptionThrown = true;
}
BOOST_CHECK(addrman1.size() == 3);
BOOST_CHECK(exceptionThrown == false);
// Test that CAddrDB::Read creates an addrman with the correct number of addrs.
CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);
CAddrMan addrman2;
CAddrDB adb;
BOOST_CHECK(addrman2.size() == 0);
adb.Read(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 3);
}
BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
CAddrManCorrupted addrmanCorrupted;
addrmanCorrupted.MakeDeterministic();
// Test that the de-serialization of corrupted addrman throws an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
bool exceptionThrown = false;
CAddrMan addrman1;
BOOST_CHECK(addrman1.size() == 0);
try {
unsigned char pchMsgTmp[4];
ssPeers1 >> FLATDATA(pchMsgTmp);
ssPeers1 >> addrman1;
} catch (const std::exception& e) {
exceptionThrown = true;
}
// Even through de-serialization failed addrman is not left in a clean state.
BOOST_CHECK(addrman1.size() == 1);
BOOST_CHECK(exceptionThrown);
// Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails.
CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);
CAddrMan addrman2;
CAddrDB adb;
BOOST_CHECK(addrman2.size() == 0);
adb.Read(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 0);
}
BOOST_AUTO_TEST_CASE(cnode_simple_test)
{
SOCKET hSocket = INVALID_SOCKET;
NodeId id = 0;
int height = 0;
in_addr ipv4Addr;
ipv4Addr.s_addr = 0xa0b0c001;
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
std::string pszDest = "";
bool fInboundIn = false;
// Test that fFeeler is false by default.
CNode* pnode1 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, 0, pszDest, fInboundIn);
BOOST_CHECK(pnode1->fInbound == false);
BOOST_CHECK(pnode1->fFeeler == false);
fInboundIn = true;
CNode* pnode2 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, 1, pszDest, fInboundIn);
BOOST_CHECK(pnode2->fInbound == true);
BOOST_CHECK(pnode2->fFeeler == false);
}
BOOST_AUTO_TEST_SUITE_END()