Browse Source

Merge remote-tracking branch 'up/develop' into deploydialog

cl-refactor
yann300 10 years ago
parent
commit
ae2fb8d479
  1. 3
      CMakeLists.txt
  2. 13
      alethzero/DappHost.cpp
  3. 6
      alethzero/DappHost.h
  4. 17
      alethzero/MainWin.cpp
  5. 7
      eth/main.cpp
  6. 1
      extdep/getstuff.bat
  7. 12
      libdevcore/RLP.cpp
  8. 21
      libdevcore/RLP.h
  9. 22
      libethash-cl/ethash_cl_miner.cpp
  10. 5
      libethash-cl/ethash_cl_miner.h
  11. 1
      libethcore/KeyManager.cpp
  12. 12
      libethereum/BlockChain.cpp
  13. 109
      libp2p/RLPXFrameCoder.cpp
  14. 51
      libp2p/RLPXFrameCoder.h
  15. 90
      libp2p/RLPXFrameReader.cpp
  16. 55
      libp2p/RLPXFrameReader.h
  17. 161
      libp2p/RLPXFrameWriter.cpp
  18. 88
      libp2p/RLPXFrameWriter.h
  19. 0
      libp2p/RLPXPacket.cpp
  20. 69
      libp2p/RLPXPacket.h
  21. 112
      libp2p/RLPXSocketIO.cpp
  22. 71
      libp2p/RLPXSocketIO.h
  23. 18
      libp2p/Session.cpp
  24. 25
      libsolidity/AST.cpp
  25. 22
      libsolidity/ArrayUtils.cpp
  26. 4
      libsolidity/ArrayUtils.h
  27. 16
      libsolidity/CompilerUtils.cpp
  28. 26
      libsolidity/ExpressionCompiler.cpp
  29. 54
      libsolidity/LValue.cpp
  30. 29
      libsolidity/Types.cpp
  31. 7
      libsolidity/Types.h
  32. 69
      libwhisper/WhisperDB.cpp
  33. 55
      libwhisper/WhisperDB.h
  34. 4
      test/TestHelper.cpp
  35. 2
      test/TestHelper.h
  36. 30
      test/libethcore/keymanager.cpp
  37. 70
      test/libethereum/BlockchainTestsFiller/bcRPC_API_TestFiller.json
  38. 36
      test/libp2p/peer.cpp
  39. 188
      test/libp2p/rlpx.cpp
  40. 16
      test/libsolidity/SolidityEndToEndTest.cpp
  41. 24
      test/libsolidity/SolidityNameAndTypeResolution.cpp
  42. 122
      test/libwhisper/whisperDB.cpp

3
CMakeLists.txt

@ -363,6 +363,9 @@ else()
endif()
if (EVMJIT)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows" AND NOT DEFINED LLVM_DIR)
set(LLVM_DIR "${CMAKE_SOURCE_DIR}/extdep/install/windows/x64/share/llvm/cmake")
endif()
set(EVMJIT_CPP TRUE) # include CPP-JIT connector
add_subdirectory(evmjit)
endif()

13
alethzero/DappHost.cpp

@ -28,7 +28,11 @@
using namespace dev;
DappHost::DappHost(int _port, int _threads):
m_port(_port), m_threads(_threads), m_running(false), m_daemon(nullptr)
m_port(_port),
m_url(QString("http://localhost:%1/").arg(m_port)),
m_threads(_threads),
m_running(false),
m_daemon(nullptr)
{
startListening();
}
@ -135,5 +139,10 @@ QUrl DappHost::hostDapp(Dapp&& _dapp)
for (ManifestEntry const& entry: m_dapp.manifest.entries)
m_entriesByPath[QString::fromStdString(entry.path)] = &entry;
return QUrl(QString("http://localhost:%1/").arg(m_port));
return m_url;
}
bool DappHost::servesUrl(QUrl const& _url) const
{
return m_url == _url || m_url.isParentOf(_url);
}

6
alethzero/DappHost.h

@ -40,6 +40,9 @@ public:
/// Load and host a dapp. Previsous dapp in discarded. Synchronous
QUrl hostDapp(Dapp&& _dapp);
/// @returns true if the given url is served from this DappHost.
bool servesUrl(QUrl const& _url) const;
private:
void startListening();
void stopListening();
@ -48,7 +51,8 @@ private:
void sendResponse(std::string const& _url, MHD_Connection* _connection);
static int callback(void* _cls, MHD_Connection* _connection, char const* _url, char const* _method, char const* _version, char const* _uploadData, size_t* _uploadDataSize, void** _conCls);
int m_port;
int const m_port;
QUrl const m_url;
int m_threads;
bool m_running;
MHD_Daemon* m_daemon;

17
alethzero/MainWin.cpp

@ -232,6 +232,11 @@ Main::Main(QWidget *parent) :
{
ui->tabWidget->setTabText(0, ui->webView->title());
});
connect(ui->webView, &QWebEngineView::urlChanged, [=](QUrl const& _url)
{
if (!m_dappHost->servesUrl(_url))
ui->urlEdit->setText(_url.toString());
});
m_dappHost.reset(new DappHost(8081));
m_dappLoader = new DappLoader(this, web3(), getNameReg());
@ -638,18 +643,22 @@ pair<Address, bytes> Main::fromString(std::string const& _n) const
if (_n == "(Create Contract)")
return make_pair(Address(), bytes());
std::string n = _n;
if (n.find("0x") == 0)
n.erase(0, 2);
auto g_newNameReg = getNameReg();
if (g_newNameReg)
{
Address a = abiOut<Address>(ethereum()->call(g_newNameReg, abiIn("addr(bytes32)", ::toString32(_n))).output);
Address a = abiOut<Address>(ethereum()->call(g_newNameReg, abiIn("addr(bytes32)", ::toString32(n))).output);
if (a)
return make_pair(a, bytes());
}
if (_n.size() == 40)
if (n.size() == 40)
{
try
{
return make_pair(Address(fromHex(_n, WhenError::Throw)), bytes());
return make_pair(Address(fromHex(n, WhenError::Throw)), bytes());
}
catch (BadHexCharacter& _e)
{
@ -665,7 +674,7 @@ pair<Address, bytes> Main::fromString(std::string const& _n) const
}
else
try {
return ICAP::decoded(_n).address([&](Address const& a, bytes const& b) -> bytes
return ICAP::decoded(n).address([&](Address const& a, bytes const& b) -> bytes
{
return ethereum()->call(a, b).output;
}, g_newNameReg);

7
eth/main.cpp

@ -910,7 +910,12 @@ void interactiveMode(eth::Client* c, std::shared_ptr<eth::TrivialGasPricer> gasP
f << endl << " STACK" << endl;
for (auto i: vm->stack())
f << (h256)i << endl;
f << " MEMORY" << endl << dev::memDump(vm->memory());
std::string memDump = (
(vm->memory().size() > 1000) ?
" mem size greater than 1000 bytes " :
dev::memDump(vm->memory())
);
f << " MEMORY" << endl << memDump;
f << " STORAGE" << endl;
for (auto const& i: ext->state().storage(ext->myAddress))
f << showbase << hex << i.first << ": " << i.second << endl;

1
extdep/getstuff.bat

@ -11,6 +11,7 @@ call :download curl 7.4.2
call :download jsoncpp 1.6.2
call :download json-rpc-cpp 0.5.0
call :download leveldb 1.2
call :download llvm 3.7svn
call :download microhttpd 0.9.2
call :download qt 5.4.1
call :download miniupnpc 1.9

12
libdevcore/RLP.cpp

@ -173,8 +173,14 @@ size_t RLP::length() const
if (lengthSize > sizeof(ret))
// We did not check, but would most probably not fit in our memory.
BOOST_THROW_EXCEPTION(UndersizeRLP());
// No leading zeroes.
if (!m_data[1])
BOOST_THROW_EXCEPTION(BadRLP());
for (unsigned i = 0; i < lengthSize; ++i)
ret = (ret << 8) | m_data[i + 1];
// Must be greater than the limit.
if (ret < c_rlpListStart - c_rlpDataImmLenStart - c_rlpMaxLengthBytes)
BOOST_THROW_EXCEPTION(BadRLP());
}
else if (n <= c_rlpListIndLenZero)
return n - c_rlpListStart;
@ -189,8 +195,12 @@ size_t RLP::length() const
if (lengthSize > sizeof(ret))
// We did not check, but would most probably not fit in our memory.
BOOST_THROW_EXCEPTION(UndersizeRLP());
if (!m_data[1])
BOOST_THROW_EXCEPTION(BadRLP());
for (unsigned i = 0; i < lengthSize; ++i)
ret = (ret << 8) | m_data[i + 1];
if (ret < 0x100 - c_rlpListStart - c_rlpMaxLengthBytes)
BOOST_THROW_EXCEPTION(BadRLP());
}
// We have to be able to add payloadOffset to length without overflow.
// This rejects roughly 4GB-sized RLPs on some platforms.
@ -203,7 +213,7 @@ size_t RLP::items() const
{
if (isList())
{
bytesConstRef d = payload().cropped(0, length());
bytesConstRef d = payload();
size_t i = 0;
for (; d.size(); ++i)
d = d.cropped(sizeAsEncoded(d));

21
libdevcore/RLP.h

@ -232,25 +232,22 @@ public:
template <class T, class U>
std::pair<T, U> toPair() const
{
if (itemCountStrict() != 2)
BOOST_THROW_EXCEPTION(BadCast());
std::pair<T, U> ret;
if (isList())
{
ret.first = (T)(*this)[0];
ret.second = (U)(*this)[1];
}
ret.first = (T)(*this)[0];
ret.second = (U)(*this)[1];
return ret;
}
template <class T, size_t N>
std::array<T, N> toArray() const
{
if (itemCount() != N || !isList())
if (itemCountStrict() != N)
BOOST_THROW_EXCEPTION(BadCast());
std::array<T, N> ret;
for (size_t i = 0; i < N; ++i)
{
ret[i] = (T)operator[](i);
}
return ret;
}
@ -281,7 +278,9 @@ public:
template <class _N> _N toHash(int _flags = Strict) const
{
requireGood();
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall)))
auto p = payload();
auto l = p.size();
if (!isData() || (l > _N::size && (_flags & FailIfTooBig)) || (l < _N::size && (_flags & FailIfTooSmall)))
{
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
@ -290,8 +289,8 @@ public:
}
_N ret;
size_t s = std::min<size_t>(_N::size, length());
memcpy(ret.data() + _N::size - s, payload().data(), s);
size_t s = std::min<size_t>(_N::size, l);
memcpy(ret.data() + _N::size - s, p.data(), s);
return ret;
}

22
libethash-cl/ethash_cl_miner.cpp

@ -317,6 +317,8 @@ bool ethash_cl_miner::init(
m_globalWorkSize = ((m_globalWorkSize / s_workgroupSize) + 1) * s_workgroupSize;
// remember the device's address bits
m_deviceBits = device.getInfo<CL_DEVICE_ADDRESS_BITS>();
// make sure first step of global work size adjustment is large enough
m_stepWorkSizeAdjust = pow(2, m_deviceBits / 2 + 1);
// patch source code
// note: ETHASH_CL_MINER_KERNEL is simply ethash_cl_miner_kernel.cl compiled
@ -522,14 +524,26 @@ void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook
{
if (d > chrono::milliseconds(s_msPerBatch * 10 / 9))
{
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, >> " << _msPerBatch << " ms." << endl;
m_globalWorkSize = max<unsigned>(128, m_globalWorkSize + s_workgroupSize);
// Divide the step by 2 when adjustment way change
if (m_wayWorkSizeAdjust > -1)
m_stepWorkSizeAdjust = max<unsigned>(1, m_stepWorkSizeAdjust / 2);
m_wayWorkSizeAdjust = -1;
// cerr << "m_stepWorkSizeAdjust: " << m_stepWorkSizeAdjust << ", m_wayWorkSizeAdjust: " << m_wayWorkSizeAdjust << endl;
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, >> " << s_msPerBatch << " ms." << endl;
m_globalWorkSize = max<unsigned>(128, m_globalWorkSize - m_stepWorkSizeAdjust);
// cerr << "New global work size" << m_globalWorkSize << endl;
}
else if (d < chrono::milliseconds(s_msPerBatch * 9 / 10))
{
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, << " << _msPerBatch << " ms." << endl;
m_globalWorkSize = min<unsigned>(pow(2, m_deviceBits) - 1, m_globalWorkSize - s_workgroupSize);
// Divide the step by 2 when adjustment way change
if (m_wayWorkSizeAdjust < 1)
m_stepWorkSizeAdjust = max<unsigned>(1, m_stepWorkSizeAdjust / 2);
m_wayWorkSizeAdjust = 1;
// cerr << "m_stepWorkSizeAdjust: " << m_stepWorkSizeAdjust << ", m_wayWorkSizeAdjust: " << m_wayWorkSizeAdjust << endl;
// cerr << "Batch of " << m_globalWorkSize << " took " << chrono::duration_cast<chrono::milliseconds>(d).count() << " ms, << " << s_msPerBatch << " ms." << endl;
m_globalWorkSize = min<unsigned>(pow(2, m_deviceBits) - 1, m_globalWorkSize + m_stepWorkSizeAdjust);
// Global work size should never be less than the workgroup size
m_globalWorkSize = max<unsigned>(s_workgroupSize, m_globalWorkSize);
// cerr << "New global work size" << m_globalWorkSize << endl;

5
libethash-cl/ethash_cl_miner.h

@ -89,6 +89,11 @@ private:
bool m_openclOnePointOne;
unsigned m_deviceBits;
/// The step used in the work size adjustment
unsigned int m_stepWorkSizeAdjust;
/// The Work Size way of adjustment, > 0 when previously increased, < 0 when previously decreased
int m_wayWorkSizeAdjust = 0;
/// The local work size for the search
static unsigned s_workgroupSize;
/// The initial global work size for the searches

1
libethcore/KeyManager.cpp

@ -213,6 +213,7 @@ void KeyManager::kill(Address const& _a)
m_addrLookup.erase(_a);
m_keyInfo.erase(id);
m_store.kill(id);
write(m_keysFile);
}
Addresses KeyManager::accounts() const

12
libethereum/BlockChain.cpp

@ -597,9 +597,19 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
// Most of the time these two will be equal - only when we're doing a chain revert will they not be
if (common != last)
{
// Erase the number-lookup cache for the segment of the chain that we're reverting (if any).
unsigned n = number(route.front());
DEV_WRITE_GUARDED(x_blockHashes)
for (auto i = route.begin(); i != route.end() && *i != common; ++i, --n)
m_blockHashes.erase(h256(u256(n)));
DEV_WRITE_GUARDED(x_transactionAddresses)
m_transactionAddresses.clear(); // TODO: could perhaps delete them individually?
// If we are reverting previous blocks, we need to clear their blooms (in particular, to
// rebuild any higher level blooms that they contributed to).
clearBlockBlooms(number(common) + 1, number(last) + 1);
}
// Go through ret backwards until hash != last.parent and update m_transactionAddresses, m_blockHashes
for (auto i = route.rbegin(); i != route.rend() && *i != common; ++i)
@ -630,7 +640,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
}
}
// Collate transaction hashes and remember who they were.
h256s newTransactionAddresses;
//h256s newTransactionAddresses;
{
bytes blockBytes;
RLP blockRLP(*i == _block.info.hash() ? _block.block : &(blockBytes = block(*i)));

109
libp2p/RLPXFrameCoder.cpp

@ -23,42 +23,46 @@
#include <libdevcore/Assertions.h>
#include "RLPxHandshake.h"
#include "RLPXPacket.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
using namespace CryptoPP;
RLPXFrameInfo::RLPXFrameInfo(bytesConstRef _header)
RLPXFrameInfo::RLPXFrameInfo(bytesConstRef _header):
length((_header[0] * 256 + _header[1]) * 256 + _header[2]),
padding((16 - (length % 16)) % 16),
data(_header.cropped(3).toBytes()),
header(RLP(data, RLP::ThrowOnFail | RLP::FailIfTooSmall)),
protocolId(header[0].toInt<uint16_t>()),
multiFrame(header.itemCount() > 1),
sequenceId(multiFrame ? header[1].toInt<uint16_t>() : 0),
totalLength(header.itemCount() == 3 ? header[2].toInt<uint32_t>() : 0)
{}
RLPXFrameCoder::RLPXFrameCoder(RLPXHandshake const& _init)
{
length = (_header[0] * 256 + _header[1]) * 256 + _header[2];
padding = ((16 - (length % 16)) % 16);
RLP header(_header.cropped(3), RLP::ThrowOnFail | RLP::FailIfTooSmall);
auto itemCount = header.itemCount();
protocolId = header[0].toInt<uint16_t>();
hasSequence = itemCount > 1;
sequenceId = hasSequence ? header[1].toInt<uint16_t>() : 0;
totalLength = itemCount == 3 ? header[2].toInt<uint32_t>() : 0;
setup(_init.m_originated, _init.m_remoteEphemeral, _init.m_remoteNonce, _init.m_ecdhe, _init.m_nonce, &_init.m_ackCipher, &_init.m_authCipher);
}
RLPXFrameCoder::RLPXFrameCoder(RLPXHandshake const& _init)
RLPXFrameCoder::RLPXFrameCoder(bool _originated, h512 const& _remoteEphemeral, h256 const& _remoteNonce, crypto::ECDHE const& _ecdhe, h256 const& _nonce, bytesConstRef _ackCipher, bytesConstRef _authCipher)
{
// we need:
// originated?
// Secret == output of ecdhe agreement
// authCipher
// ackCipher
setup(_originated, _remoteEphemeral, _remoteNonce, _ecdhe, _nonce, _ackCipher, _authCipher);
}
void RLPXFrameCoder::setup(bool _originated, h512 const& _remoteEphemeral, h256 const& _remoteNonce, crypto::ECDHE const& _ecdhe, h256 const& _nonce, bytesConstRef _ackCipher, bytesConstRef _authCipher)
{
bytes keyMaterialBytes(64);
bytesRef keyMaterial(&keyMaterialBytes);
// shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce))
Secret ephemeralShared;
_init.m_ecdhe.agree(_init.m_remoteEphemeral, ephemeralShared);
_ecdhe.agree(_remoteEphemeral, ephemeralShared);
ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size));
h512 nonceMaterial;
h256 const& leftNonce = _init.m_originated ? _init.m_remoteNonce : _init.m_nonce;
h256 const& rightNonce = _init.m_originated ? _init.m_nonce : _init.m_remoteNonce;
h256 const& leftNonce = _originated ? _remoteNonce : _nonce;
h256 const& rightNonce = _originated ? _nonce : _remoteNonce;
leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size));
rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size));
auto outRef(keyMaterial.cropped(h256::size, h256::size));
@ -88,54 +92,81 @@ RLPXFrameCoder::RLPXFrameCoder(RLPXHandshake const& _init)
// Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack)
// ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init)
(*(h256*)outRef.data() ^ _init.m_remoteNonce).ref().copyTo(keyMaterial);
bytes const& egressCipher = _init.m_originated ? _init.m_authCipher : _init.m_ackCipher;
(*(h256*)outRef.data() ^ _remoteNonce).ref().copyTo(keyMaterial);
bytesConstRef egressCipher = _originated ? _authCipher : _ackCipher;
keyMaterialBytes.resize(h256::size + egressCipher.size());
keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size()));
egressCipher.copyTo(keyMaterial.cropped(h256::size, egressCipher.size()));
m_egressMac.Update(keyMaterial.data(), keyMaterial.size());
// recover mac-secret by re-xoring remoteNonce
(*(h256*)keyMaterial.data() ^ _init.m_remoteNonce ^ _init.m_nonce).ref().copyTo(keyMaterial);
bytes const& ingressCipher = _init.m_originated ? _init.m_ackCipher : _init.m_authCipher;
(*(h256*)keyMaterial.data() ^ _remoteNonce ^ _nonce).ref().copyTo(keyMaterial);
bytesConstRef ingressCipher = _originated ? _ackCipher : _authCipher;
keyMaterialBytes.resize(h256::size + ingressCipher.size());
keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size()));
ingressCipher.copyTo(keyMaterial.cropped(h256::size, ingressCipher.size()));
m_ingressMac.Update(keyMaterial.data(), keyMaterial.size());
}
void RLPXFrameCoder::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes)
void RLPXFrameCoder::writeFrame(uint16_t _protocolType, bytesConstRef _payload, bytes& o_bytes)
{
// _packet = type || rlpList()
RLPStream header;
uint32_t len = (uint32_t)_payload.size();
header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
header.appendList(1) << _protocolType;
writeFrame(header, _payload, o_bytes);
}
void RLPXFrameCoder::writeFrame(uint16_t _protocolType, uint16_t _seqId, bytesConstRef _payload, bytes& o_bytes)
{
RLPStream header;
uint32_t len = (uint32_t)_packet.size();
uint32_t len = (uint32_t)_payload.size();
header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
// zeroHeader: []byte{0xC2, 0x80, 0x80}. Should be rlpList(protocolType,seqId,totalPacketSize).
header.appendRaw(bytes({0xc2,0x80,0x80}));
// TODO: SECURITY check that header is <= 16 bytes
header.appendList(2) << _protocolType << _seqId;
writeFrame(header, _payload, o_bytes);
}
void RLPXFrameCoder::writeFrame(uint16_t _protocolType, uint16_t _seqId, uint32_t _totalSize, bytesConstRef _payload, bytes& o_bytes)
{
RLPStream header;
uint32_t len = (uint32_t)_payload.size();
header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
header.appendList(3) << _protocolType << _seqId << _totalSize;
writeFrame(header, _payload, o_bytes);
}
void RLPXFrameCoder::writeFrame(RLPStream const& _header, bytesConstRef _payload, bytes& o_bytes)
{
// TODO: SECURITY check header values && header <= 16 bytes
bytes headerWithMac(h256::size);
bytesConstRef(&header.out()).copyTo(bytesRef(&headerWithMac));
bytesConstRef(&_header.out()).copyTo(bytesRef(&headerWithMac));
m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16);
updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16));
egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size));
auto padding = (16 - (_packet.size() % 16)) % 16;
auto padding = (16 - (_payload.size() % 16)) % 16;
o_bytes.swap(headerWithMac);
o_bytes.resize(32 + _packet.size() + padding + h128::size);
bytesRef packetRef(o_bytes.data() + 32, _packet.size());
m_frameEnc.ProcessData(packetRef.data(), _packet.data(), _packet.size());
bytesRef paddingRef(o_bytes.data() + 32 + _packet.size(), padding);
o_bytes.resize(32 + _payload.size() + padding + h128::size);
bytesRef packetRef(o_bytes.data() + 32, _payload.size());
m_frameEnc.ProcessData(packetRef.data(), _payload.data(), _payload.size());
bytesRef paddingRef(o_bytes.data() + 32 + _payload.size(), padding);
if (padding)
m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding);
bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding);
bytesRef packetWithPaddingRef(o_bytes.data() + 32, _payload.size() + padding);
updateEgressMACWithFrame(packetWithPaddingRef);
bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size);
bytesRef macRef(o_bytes.data() + 32 + _payload.size() + padding, h128::size);
egressDigest().ref().copyTo(macRef);
}
void RLPXFrameCoder::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes)
{
RLPStream header;
uint32_t len = (uint32_t)_packet.size();
header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
header.appendRaw(bytes({0xc2,0x80,0x80}));
writeFrame(header, _packet, o_bytes);
}
bool RLPXFrameCoder::authAndDecryptHeader(bytesRef io)
{
asserts(io.size() == h256::size);

51
libp2p/RLPXFrameCoder.h

@ -33,18 +33,28 @@ namespace dev
namespace p2p
{
struct RLPXFrameDecryptFailed: virtual dev::Exception {};
/**
* @brief Encapsulation of Frame
* @todo coder integration; padding derived from coder
*/
struct RLPXFrameInfo
{
RLPXFrameInfo() = default;
/// Constructor. frame-size || protocol-type, [sequence-id[, total-packet-size]]
RLPXFrameInfo(bytesConstRef _frameHeader);
uint32_t length = 0; ///< Max: 2**24
uint8_t padding = 0;
uint32_t const length; ///< Size of frame (excludes padding). Max: 2**24
uint8_t const padding; ///< Length of padding which follows @length.
uint16_t protocolId = 0;
bool hasSequence = false;
uint16_t sequenceId = 0;
uint32_t totalLength = 0;
bytes const data; ///< Bytes of Header.
RLP const header; ///< Header RLP.
uint16_t const protocolId; ///< Protocol ID as negotiated by handshake.
bool const multiFrame; ///< If this frame is part of a sequence
uint16_t const sequenceId; ///< Sequence ID of frame
uint32_t const totalLength; ///< Set to total length of packet in first frame of multiframe packet
};
class RLPXHandshake;
@ -52,8 +62,12 @@ class RLPXHandshake;
/**
* @brief Encoder/decoder transport for RLPx connection established by RLPXHandshake.
*
* @todo rename to RLPXTranscoder
* @todo Remove 'Frame' nomenclature and expect caller to provide RLPXFrame
* @todo Remove handshake as friend, remove handshake-based constructor
*
* Thread Safety
* Distinct Objects: Safe.
* Distinct Objects: Unsafe.
* Shared objects: Unsafe.
*/
class RLPXFrameCoder
@ -61,12 +75,27 @@ class RLPXFrameCoder
friend class RLPXFrameIOMux;
friend class Session;
public:
/// Constructor.
/// Requires instance of RLPXHandshake which has completed first two phases of handshake.
/// Construct; requires instance of RLPXHandshake which has encrypted ECDH key exchange (first two phases of handshake).
RLPXFrameCoder(RLPXHandshake const& _init);
/// Construct with external key material.
RLPXFrameCoder(bool _originated, h512 const& _remoteEphemeral, h256 const& _remoteNonce, crypto::ECDHE const& _ephemeral, h256 const& _nonce, bytesConstRef _ackCipher, bytesConstRef _authCipher);
~RLPXFrameCoder() {}
/// Encrypt _packet as RLPx frame.
/// Establish shared secrets and setup AES and MAC states.
void setup(bool _originated, h512 const& _remoteEphemeral, h256 const& _remoteNonce, crypto::ECDHE const& _ephemeral, h256 const& _nonce, bytesConstRef _ackCipher, bytesConstRef _authCipher);
/// Write single-frame payload of packet(s).
void writeFrame(uint16_t _protocolType, bytesConstRef _payload, bytes& o_bytes);
/// Write continuation frame of segmented payload.
void writeFrame(uint16_t _protocolType, uint16_t _seqId, bytesConstRef _payload, bytes& o_bytes);
/// Write first frame of segmented or sequence-tagged payload.
void writeFrame(uint16_t _protocolType, uint16_t _seqId, uint32_t _totalSize, bytesConstRef _payload, bytes& o_bytes);
/// Legacy. Encrypt _packet as ill-defined legacy RLPx frame.
void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes);
/// Authenticate and decrypt header in-place.
@ -82,6 +111,8 @@ public:
h128 ingressDigest();
protected:
void writeFrame(RLPStream const& _header, bytesConstRef _payload, bytes& o_bytes);
/// Update state of egress MAC with frame header.
void updateEgressMACWithHeader(bytesConstRef _headerCipher);

90
libp2p/RLPXFrameReader.cpp

@ -0,0 +1,90 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXFrameReader.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#include "RLPXFrameReader.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
std::vector<RLPXPacket> RLPXFrameReader::demux(RLPXFrameCoder& _coder, RLPXFrameInfo const& _info, bytesRef _frame)
{
if (!_coder.authAndDecryptFrame(_frame))
BOOST_THROW_EXCEPTION(RLPXFrameDecryptFailed());
std::vector<RLPXPacket> ret;
if (_frame.empty())
// drop: bad frame (empty)
return ret;
if (_info.multiFrame && _info.totalLength && _frame.size() > _info.totalLength)
// drop: bad frame (too large)
return ret;
// trim mac
bytesConstRef buffer = _frame.cropped(0, _frame.size() - (h128::size + _info.padding));
// continue populating multiframe packets
if (_info.multiFrame && m_incomplete.count(_info.sequenceId))
{
uint32_t& remaining = m_incomplete.at(_info.sequenceId).second;
if (!_info.totalLength && buffer.size() > 0 && buffer.size() <= remaining)
{
remaining -= buffer.size();
RLPXPacket& p = m_incomplete.at(_info.sequenceId).first;
if(p.append(buffer) && !remaining)
ret.push_back(std::move(p));
if (!remaining)
m_incomplete.erase(_info.sequenceId);
if (!ret.empty() && remaining)
BOOST_THROW_EXCEPTION(RLPXInvalidPacket());
else if (ret.empty() && !remaining)
BOOST_THROW_EXCEPTION(RLPXInvalidPacket());
return ret;
}
else
m_incomplete.erase(_info.sequenceId);
}
while (!buffer.empty())
{
auto type = nextRLP(buffer);
if (type.empty())
break;
buffer = buffer.cropped(type.size());
// consume entire buffer if packet has sequence
auto packet = _info.multiFrame ? buffer : nextRLP(buffer);
buffer = buffer.cropped(packet.size());
RLPXPacket p(m_protocolType, type);
if (!packet.empty())
p.append(packet);
uint32_t remaining = _info.totalLength - type.size() - packet.size();
if (p.isValid())
ret.push_back(std::move(p));
else if (_info.multiFrame && remaining)
m_incomplete.insert(std::make_pair(_info.sequenceId, std::make_pair(std::move(p), remaining)));
else
BOOST_THROW_EXCEPTION(RLPXInvalidPacket());
}
return ret;
}

55
libp2p/RLPXFrameReader.h

@ -0,0 +1,55 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXFrameReader.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#pragma once
#include <libdevcore/Guards.h>
#include "RLPXFrameCoder.h"
#include "RLPXPacket.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace dev
{
namespace p2p
{
/**
* RLPFrameReader
* Reads and assembles RLPX frame byte buffers into RLPX packets. Additionally
* buffers incomplete packets which are pieced into multiple frames (has sequence).
*/
class RLPXFrameReader
{
public:
RLPXFrameReader(uint16_t _protocolType): m_protocolType(_protocolType) {}
/// Processes a single frame returning complete packets.
std::vector<RLPXPacket> demux(RLPXFrameCoder& _coder, RLPXFrameInfo const& _info, bytesRef _frame);
protected:
uint16_t m_protocolType;
std::map<uint16_t, std::pair<RLPXPacket, uint32_t>> m_incomplete; ///< Sequence: Incomplete packet and bytes remaining.
};
}
}

161
libp2p/RLPXFrameWriter.cpp

@ -0,0 +1,161 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXFrameWriter.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#include "RLPXFrameWriter.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
const uint16_t RLPXFrameWriter::EmptyFrameLength = h128::size * 3; // header + headerMAC + frameMAC
const uint16_t RLPXFrameWriter::MinFrameDequeLength = h128::size * 4; // header + headerMAC + padded-block + frameMAC
void RLPXFrameWriter::enque(RLPXPacket&& _p, PacketPriority _priority)
{
if (!_p.isValid())
return;
WriterState& qs = _priority ? m_q.first : m_q.second;
DEV_GUARDED(qs.x)
qs.q.push_back(move(_p));
}
void RLPXFrameWriter::enque(uint8_t _packetType, RLPStream& _payload, PacketPriority _priority)
{
enque(RLPXPacket(m_protocolType, (RLPStream() << _packetType), _payload), _priority);
}
size_t RLPXFrameWriter::mux(RLPXFrameCoder& _coder, unsigned _size, vector<bytes>& o_toWrite)
{
static const size_t c_blockSize = h128::size;
static const size_t c_overhead = c_blockSize * 3; // header + headerMac + frameMAC
if (_size < c_overhead + c_blockSize)
return 0;
size_t ret = 0;
size_t frameLen = _size / 16 * 16;
bytes payload(0);
bool swapQueues = false;
while (frameLen >= c_overhead + c_blockSize)
{
bool highPending;
bool lowPending;
DEV_GUARDED(m_q.first.x)
highPending = !!m_q.first.q.size();
DEV_GUARDED(m_q.second.x)
lowPending = !!m_q.second.q.size();
if (!highPending && !lowPending)
return ret;
// first run when !swapQueues, high > low, otherwise low > high
bool high = highPending && !swapQueues ? true : !lowPending;
WriterState &qs = high ? m_q.first : m_q.second;
size_t frameAllot = (!swapQueues && highPending && lowPending ? frameLen / 2 - (c_overhead + c_blockSize) > 0 ? frameLen / 2 : frameLen : frameLen) - c_overhead;
size_t offset = 0;
size_t length = 0;
while (frameAllot >= c_blockSize)
{
// Invariants:
// !qs.writing && payload.empty() initial entry
// !qs.writing && !payload.empty() continuation (multiple packets per frame)
// qs.writing && payload.empty() initial entry, continuation (multiple frames per packet)
// qs.writing && !payload.empty() INVALID
// write packet-type
if (qs.writing == nullptr)
{
{
Guard l(qs.x);
if (!qs.q.empty())
qs.writing = &qs.q[0];
else
break;
}
// break here if we can't write-out packet-type
// or payload is packed and next packet won't fit (implicit)
qs.multiFrame = qs.writing->size() > frameAllot;
assert(qs.writing->type().size());
if (qs.writing->type().size() > frameAllot || (qs.multiFrame && !payload.empty()))
{
qs.writing = nullptr;
qs.remaining = 0;
qs.multiFrame = false;
break;
}
else if (qs.multiFrame)
qs.sequence = ++m_sequenceId;
frameAllot -= qs.writing->type().size();
payload += qs.writing->type();
qs.remaining = qs.writing->data().size();
}
// write payload w/remaining allotment
assert(qs.multiFrame || (!qs.multiFrame && frameAllot >= qs.remaining));
if (frameAllot && qs.remaining)
{
offset = qs.writing->data().size() - qs.remaining;
length = qs.remaining <= frameAllot ? qs.remaining : frameAllot;
bytes portion = bytesConstRef(&qs.writing->data()).cropped(offset, length).toBytes();
qs.remaining -= length;
frameAllot -= portion.size();
payload += portion;
}
assert((!qs.remaining && (offset > 0 || !qs.multiFrame)) || (qs.remaining && qs.multiFrame));
if (!qs.remaining)
{
qs.writing = nullptr;
DEV_GUARDED(qs.x)
qs.q.pop_front();
ret++;
}
// qs.writing is left alone for first frame of multi-frame packet
if (qs.multiFrame)
break;
}
if (!payload.empty())
{
if (qs.multiFrame)
if (offset == 0)
// 1st frame of segmented packet writes total-size of packet
_coder.writeFrame(m_protocolType, qs.sequence, qs.writing->size(), &payload, payload);
else
_coder.writeFrame(m_protocolType, qs.sequence, &payload, payload);
else
_coder.writeFrame(m_protocolType, &payload, payload);
assert(frameLen >= payload.size());
frameLen -= payload.size();
o_toWrite.push_back(payload);
payload.resize(0);
if (!qs.remaining && qs.multiFrame)
qs.multiFrame = false;
}
else if (swapQueues)
break;
swapQueues = true;
}
return ret;
}

88
libp2p/RLPXFrameWriter.h

@ -0,0 +1,88 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXperimental.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#pragma once
#include <libdevcore/Guards.h>
#include "RLPXFrameCoder.h"
#include "RLPXPacket.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace dev
{
namespace p2p
{
/**
* @brief Multiplex packets into encrypted RLPX frames.
* @todo throw when enqueued packet is invalid
* @todo use RLPXFrameInfo
*/
class RLPXFrameWriter
{
/**
* @brief Queue and state for Writer
* Properties are used independently;
* only valid packets should be added to q
* @todo implement as class
*/
struct WriterState
{
std::deque<RLPXPacket> q;
mutable Mutex x;
RLPXPacket* writing = nullptr;
size_t remaining = 0;
bool multiFrame = false;
uint16_t sequence = -1;
};
public:
enum PacketPriority { PriorityLow = 0, PriorityHigh };
static const uint16_t EmptyFrameLength;
static const uint16_t MinFrameDequeLength;
RLPXFrameWriter(uint16_t _protocolType): m_protocolType(_protocolType) {}
RLPXFrameWriter(RLPXFrameWriter const& _s): m_protocolType(_s.m_protocolType) {}
/// Returns total number of queued packets. Thread-safe.
size_t size() const { size_t l; size_t h; DEV_GUARDED(m_q.first.x) h = m_q.first.q.size(); DEV_GUARDED(m_q.second.x) l = m_q.second.q.size(); return l + h; }
/// Moves @_payload output to queue, to be muxed into frames by mux() when network buffer is ready for writing. Thread-safe.
void enque(uint8_t _packetType, RLPStream& _payload, PacketPriority _priority = PriorityLow);
/// Returns number of packets framed and outputs frames to o_bytes. Not thread-safe.
size_t mux(RLPXFrameCoder& _coder, unsigned _size, std::vector<bytes>& o_toWrite);
protected:
/// Moves @_p to queue, to be muxed into frames by mux() when network buffer is ready for writing. Thread-safe.
void enque(RLPXPacket&& _p, PacketPriority _priority = PriorityLow);
private:
uint16_t const m_protocolType; // Protocol Type
std::pair<WriterState, WriterState> m_q; // High, Low frame queues
uint16_t m_sequenceId = 0; // Sequence ID
};
}
}

0
libp2p/RLPXPacket.cpp

69
libp2p/RLPXPacket.h

@ -0,0 +1,69 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXPacket.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#pragma once
#include <algorithm>
#include "Common.h"
namespace dev
{
namespace p2p
{
struct RLPXInvalidPacket: virtual dev::Exception {};
static bytesConstRef nextRLP(bytesConstRef _b) { try { RLP r(_b, RLP::AllowNonCanon); return _b.cropped(0, std::min((size_t)r.actualSize(), _b.size())); } catch(...) {} return bytesConstRef(); }
/**
* RLPX Packet
*/
class RLPXPacket
{
public:
/// Construct packet. RLPStream data is invalidated.
RLPXPacket(uint8_t _capId, RLPStream& _type, RLPStream& _data): m_cap(_capId), m_type(_type.out()), m_data(_data.out()) {}
/// Construct packet from single bytestream. RLPStream data is invalidated.
RLPXPacket(unsigned _capId, bytesConstRef _in): m_cap(_capId), m_type(nextRLP(_in).toBytes()) { if (_in.size() > m_type.size()) { m_data.resize(_in.size() - m_type.size()); _in.cropped(m_type.size()).copyTo(&m_data); } }
RLPXPacket(RLPXPacket const& _p) = delete;
RLPXPacket(RLPXPacket&& _p): m_cap(_p.m_cap), m_type(_p.m_type), m_data(_p.m_data) {}
bytes const& type() const { return m_type; }
bytes const& data() const { return m_data; }
size_t size() const { try { return RLP(m_type).actualSize() + RLP(m_data, RLP::LaissezFaire).actualSize(); } catch(...) { return 0; } }
/// Appends byte data and returns if packet is valid.
bool append(bytesConstRef _in) { auto offset = m_data.size(); m_data.resize(offset + _in.size()); _in.copyTo(bytesRef(&m_data).cropped(offset)); return isValid(); }
virtual bool isValid() const noexcept { try { return !(m_type.empty() && m_data.empty()) && RLP(m_type).actualSize() == m_type.size() && RLP(m_data).actualSize() == m_data.size(); } catch (...) {} return false; }
protected:
uint8_t m_cap;
bytes m_type;
bytes m_data;
};
}
}

112
libp2p/RLPXSocketIO.cpp

@ -0,0 +1,112 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXSocketIO.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#include "RLPXSocketIO.h"
#include <algorithm>
using namespace std;
using namespace dev;
using namespace dev::p2p;
uint32_t const RLPXSocketIO::MinFrameSize = h128::size * 3; // header + block + mac
uint32_t const RLPXSocketIO::MaxPacketSize = 1 << 24;
uint16_t const RLPXSocketIO::DefaultInitialCapacity = 8 << 8;
RLPXSocketIO::RLPXSocketIO(unsigned _protCount, RLPXFrameCoder& _coder, bi::tcp::socket& _socket, bool _flowControl, size_t _initialCapacity):
m_flowControl(_flowControl),
m_coder(_coder),
m_socket(_socket),
m_writers(move(writers(_protCount))),
m_egressCapacity(m_flowControl ? _initialCapacity : MaxPacketSize * m_writers.size())
{}
vector<RLPXFrameWriter> RLPXSocketIO::writers(unsigned _capacity)
{
vector<RLPXFrameWriter> ret;
for (unsigned i = 0; i < _capacity; i++)
ret.push_back(RLPXFrameWriter(i));
return ret;
}
void RLPXSocketIO::send(unsigned _protocolType, unsigned _type, RLPStream& _payload)
{
if (!m_socket.is_open())
return; // TCPSocketNotOpen
m_writers.at(_protocolType).enque(_type, _payload);
bool wasEmtpy = false;
DEV_GUARDED(x_queued)
wasEmtpy = (++m_queued == 1);
if (wasEmtpy)
doWrite();
}
void RLPXSocketIO::doWrite()
{
m_toSend.clear();
size_t capacity;
DEV_GUARDED(x_queued)
capacity = min(m_egressCapacity, MaxPacketSize);
size_t active = 0;
for (auto const& w: m_writers)
if (w.size())
active += 1;
size_t dequed = 0;
size_t protFrameSize = capacity / active;
if (protFrameSize >= MinFrameSize)
for (auto& w: m_writers)
dequed += w.mux(m_coder, protFrameSize, m_toSend);
if (dequed)
write(dequed);
else
deferWrite();
}
void RLPXSocketIO::deferWrite()
{
auto self(shared_from_this());
m_congestion.reset(new ba::deadline_timer(m_socket.get_io_service()));
m_congestion->expires_from_now(boost::posix_time::milliseconds(50));
m_congestion->async_wait([=](boost::system::error_code const& _ec) { m_congestion.reset(); if (!_ec) doWrite(); });
}
void RLPXSocketIO::write(size_t _dequed)
{
auto self(shared_from_this());
ba::async_write(m_socket, ba::buffer(m_toSend), [this, self, _dequed](boost::system::error_code ec, size_t written)
{
if (ec)
return; // TCPSocketWriteError
bool reschedule = false;
DEV_GUARDED(x_queued)
{
if (m_flowControl)
m_egressCapacity -= written;
m_queued -= _dequed;
reschedule = m_queued > 0;
}
if (reschedule)
doWrite();
});
}

71
libp2p/RLPXSocketIO.h

@ -0,0 +1,71 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXSocketIO.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#pragma once
#include "RLPXFrameWriter.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace dev
{
namespace p2p
{
class RLPXSocketIO: public std::enable_shared_from_this<RLPXSocketIO>
{
public:
static uint32_t const MinFrameSize;
static uint32_t const MaxPacketSize;
static uint16_t const DefaultInitialCapacity;
RLPXSocketIO(unsigned _protCount, RLPXFrameCoder& _coder, bi::tcp::socket& _socket, bool _flowControl = true, size_t _initialCapacity = DefaultInitialCapacity);
void send(unsigned _protocolType, unsigned _type, RLPStream& _payload);
void doWrite();
bool congested() const { return !!m_congestion; }
private:
static std::vector<RLPXFrameWriter> writers(unsigned _capacity);
void deferWrite();
void write(size_t _dequed);
bool const m_flowControl; ///< True if flow control is enabled.
RLPXFrameCoder& m_coder; ///< Encoder/decoder of frame payloads.
bi::tcp::socket& m_socket;
std::vector<bytes> m_toSend; ///< Reusable byte buffer for pending socket writes.
std::vector<RLPXFrameWriter> m_writers; ///< Write queues for each protocol. TODO: map to bytes (of capability)
std::unique_ptr<ba::deadline_timer> m_congestion; ///< Scheduled when writes are deferred due to congestion.
Mutex x_queued;
unsigned m_queued = 0; ///< Track total queued packets to ensure single write loop
uint32_t m_egressCapacity;
};
}
}

18
libp2p/Session.cpp

@ -375,10 +375,16 @@ void Session::doRead()
return;
}
RLPXFrameInfo header;
uint16_t hProtocolId;
uint32_t hLength;
uint8_t hPadding;
try
{
header = RLPXFrameInfo(bytesConstRef(m_data.data(), length));
RLPXFrameInfo header(bytesConstRef(m_data.data(), length));
hProtocolId = header.protocolId;
hLength = header.length;
hPadding = header.padding;
}
catch (std::exception const& _e)
{
@ -388,8 +394,8 @@ void Session::doRead()
}
/// read padded frame and mac
auto tlen = header.length + header.padding + h128::size;
ba::async_read(m_socket->ref(), boost::asio::buffer(m_data, tlen), [this, self, header, tlen](boost::system::error_code ec, std::size_t length)
auto tlen = hLength + hPadding + h128::size;
ba::async_read(m_socket->ref(), boost::asio::buffer(m_data, tlen), [this, self, hLength, hProtocolId, tlen](boost::system::error_code ec, std::size_t length)
{
ThreadContext tc(info().id.abridged());
ThreadContext tc2(info().clientVersion);
@ -402,7 +408,7 @@ void Session::doRead()
return;
}
bytesConstRef frame(m_data.data(), header.length);
bytesConstRef frame(m_data.data(), hLength);
if (!checkPacket(frame))
{
cerr << "Received " << frame.size() << ": " << toHex(frame) << endl;
@ -414,7 +420,7 @@ void Session::doRead()
{
auto packetType = (PacketType)RLP(frame.cropped(0, 1)).toInt<unsigned>();
RLP r(frame.cropped(1));
if (!readPacket(header.protocolId, packetType, r))
if (!readPacket(hProtocolId, packetType, r))
clog(NetWarn) << "Couldn't interpret packet." << RLP(r);
}
doRead();

25
libsolidity/AST.cpp

@ -426,7 +426,14 @@ void InheritanceSpecifier::checkTypeRequirements()
solAssert(base, "Base contract not available.");
TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes();
if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
BOOST_THROW_EXCEPTION(createTypeError(
"Wrong argument count for constructor call: " +
toString(m_arguments.size()) +
" arguments given but expected " +
toString(parameterTypes.size()) +
"."
));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
@ -629,7 +636,13 @@ void ModifierInvocation::checkTypeRequirements(vector<ContractDefinition const*>
if (!parameters)
BOOST_THROW_EXCEPTION(createTypeError("Referenced declaration is neither modifier nor base class."));
if (parameters->size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for modifier invocation."));
BOOST_THROW_EXCEPTION(createTypeError(
"Wrong argument count for modifier invocation: " +
toString(m_arguments.size()) +
" arguments given but expected " +
toString(parameters->size()) +
"."
));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*(*parameters)[i]->getType()))
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
@ -834,7 +847,13 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
// function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes();
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
BOOST_THROW_EXCEPTION(createTypeError(
"Wrong argument count for function call: " +
toString(m_arguments.size()) +
" arguments given but expected " +
toString(parameterTypes.size()) +
"."
));
if (isPositionalCall)
{

22
libsolidity/ArrayUtils.cpp

@ -37,7 +37,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// this copies source to target and also clears target if it was larger
// need to leave "target_ref target_byte_off" on the stack at the end
// stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top)
// stack layout: [source_ref] [source length] target_ref (top)
solAssert(_targetType.location() == DataLocation::Storage, "");
IntegerType uint256(256);
@ -53,16 +53,11 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16;
unsigned byteOffsetSize = (haveByteOffsetSource ? 1 : 0) + (haveByteOffsetTarget ? 1 : 0);
// stack: source_ref [source_byte_off] [source_length] target_ref target_byte_off
// stack: source_ref [source_length] target_ref
// store target_ref
// arrays always start at zero byte offset, pop offset
m_context << eth::Instruction::POP;
for (unsigned i = _sourceType.getSizeOnStack(); i > 0; --i)
m_context << eth::swapInstruction(i);
// stack: target_ref source_ref [source_byte_off] [source_length]
if (sourceIsStorage)
// arrays always start at zero byte offset, pop offset
m_context << eth::Instruction::POP;
// stack: target_ref source_ref [source_length]
// stack: target_ref source_ref [source_length]
// retrieve source length
if (_sourceType.location() != DataLocation::CallData || !_sourceType.isDynamicallySized())
@ -90,7 +85,6 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
m_context
<< eth::Instruction::POP << eth::Instruction::POP
<< eth::Instruction::POP << eth::Instruction::POP;
m_context << u256(0);
return;
}
// compute hashes (data positions)
@ -136,13 +130,11 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
auto const& sourceBaseArrayType = dynamic_cast<ArrayType const&>(*sourceBaseType);
m_context << eth::Instruction::DUP3;
if (sourceIsStorage)
m_context << u256(0);
else if (sourceBaseArrayType.location() == DataLocation::Memory)
if (sourceBaseArrayType.location() == DataLocation::Memory)
m_context << eth::Instruction::MLOAD;
m_context << eth::dupInstruction(sourceIsStorage ? 4 : 3) << u256(0);
m_context << eth::Instruction::DUP3;
copyArrayToStorage(dynamic_cast<ArrayType const&>(*targetBaseType), sourceBaseArrayType);
m_context << eth::Instruction::POP << eth::Instruction::POP;
m_context << eth::Instruction::POP;
}
else if (directCopy)
{
@ -235,7 +227,6 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// stack: target_ref target_data_end target_data_pos_updated
clearStorageLoop(*targetBaseType);
m_context << eth::Instruction::POP;
m_context << u256(0);
}
void ArrayUtils::copyArrayToMemory(const ArrayType& _sourceType, bool _padToWordBoundaries) const
@ -365,7 +356,6 @@ void ArrayUtils::copyArrayToMemory(const ArrayType& _sourceType, bool _padToWord
u256 storageSize = _sourceType.getBaseType()->getStorageSize();
solAssert(storageSize > 1 || (storageSize == 1 && storageBytes > 0), "");
m_context << eth::Instruction::POP; // remove offset, arrays always start new slot
retrieveLength(_sourceType);
// stack here: memory_offset storage_offset length
// jump to end if length is zero

4
libsolidity/ArrayUtils.h

@ -41,8 +41,8 @@ public:
/// Copies an array to an array in storage. The arrays can be of different types only if
/// their storage representation is the same.
/// Stack pre: source_reference [source_byte_offset/source_length] target_reference target_byte_offset
/// Stack post: target_reference target_byte_offset
/// Stack pre: source_reference [source_length] target_reference
/// Stack post: target_reference
void copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const;
/// Copies the data part of an array (which cannot be dynamically nested) from anywhere
/// to a given position in memory.

16
libsolidity/CompilerUtils.cpp

@ -229,7 +229,7 @@ void CompilerUtils::encodeToMemory(
if (arrayType.location() == DataLocation::CallData)
m_context << eth::Instruction::DUP2; // length is on stack
else if (arrayType.location() == DataLocation::Storage)
m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
else
{
solAssert(arrayType.location() == DataLocation::Memory, "");
@ -416,13 +416,6 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
{
// stack: <source ref> (variably sized)
unsigned stackSize = typeOnStack.getSizeOnStack();
bool fromStorage = (typeOnStack.location() == DataLocation::Storage);
if (fromStorage)
{
stackSize--;
// remove storage offset, as requested by ArrayUtils::retrieveLength
m_context << eth::Instruction::POP;
}
ArrayUtils(m_context).retrieveLength(typeOnStack);
// allocate memory
@ -446,8 +439,6 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
{
solAssert(typeOnStack.getBaseType()->isValueType(), "");
copyToStackTop(2 + stackSize, stackSize);
if (fromStorage)
m_context << u256(0); // add byte offset again
ArrayUtils(m_context).copyArrayToMemory(typeOnStack);
}
else
@ -462,6 +453,8 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
copyToStackTop(3 + stackSize, stackSize);
copyToStackTop(2 + stackSize, 1);
ArrayUtils(m_context).accessIndex(typeOnStack, false);
if (typeOnStack.location() == DataLocation::Storage)
StorageItem(m_context, *typeOnStack.getBaseType()).retrieveValue(SourceLocation(), true);
convertType(*typeOnStack.getBaseType(), *targetType.getBaseType(), _cleanupNeeded);
storeInMemoryDynamic(*targetType.getBaseType(), true);
m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD;
@ -512,8 +505,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
if (typeOnStack.location() != DataLocation::Memory)
{
solAssert(typeOnStack.location() == DataLocation::Storage, "");
// stack: <source ref> <source byte offset>
m_context << eth::Instruction::POP;
// stack: <source ref>
m_context << typeOnStack.memorySize();
allocateMemory();
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;

26
libsolidity/ExpressionCompiler.cpp

@ -714,7 +714,6 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
case DataLocation::Storage:
{
m_context << eth::Instruction::POP; // structs always align to new slot
pair<u256, unsigned> const& offsets = type.getStorageOffsetsOfMember(member);
m_context << offsets.first << eth::Instruction::ADD << u256(offsets.second);
setLValueToStorageItem(_memberAccess);
@ -792,8 +791,6 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
Type const& baseType = *_indexAccess.getBaseExpression().getType();
if (baseType.getCategory() == Type::Category::Mapping)
{
// storage byte offset is ignored for mappings, it should be zero.
m_context << eth::Instruction::POP;
// stack: storage_base_ref
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
m_context << u256(0); // memory position
@ -812,10 +809,6 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
// remove storage byte offset
if (arrayType.location() == DataLocation::Storage)
m_context << eth::Instruction::POP;
_indexAccess.getIndexExpression()->accept(*this);
// stack layout: <base_ref> [<length>] <index>
ArrayUtils(m_context).accessIndex(arrayType);
@ -942,24 +935,27 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
}
else
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
bool const c_isSigned = type.isSigned();
bool isSigned = false;
if (auto type = dynamic_cast<IntegerType const*>(&_type))
isSigned = type->isSigned();
switch (_operator)
{
case Token::GreaterThanOrEqual:
m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT)
<< eth::Instruction::ISZERO;
m_context <<
(isSigned ? eth::Instruction::SLT : eth::Instruction::LT) <<
eth::Instruction::ISZERO;
break;
case Token::LessThanOrEqual:
m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT)
<< eth::Instruction::ISZERO;
m_context <<
(isSigned ? eth::Instruction::SGT : eth::Instruction::GT) <<
eth::Instruction::ISZERO;
break;
case Token::GreaterThan:
m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
break;
case Token::LessThan:
m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator."));

54
libsolidity/LValue.cpp

@ -152,7 +152,14 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
{
// stack: storage_key storage_offset
if (!m_dataType.isValueType())
return; // no distinction between value and reference for non-value types
{
solAssert(m_dataType.getSizeOnStack() == 1, "Invalid storage ref size.");
if (_remove)
m_context << eth::Instruction::POP; // remove byte offset
else
m_context << eth::Instruction::DUP2;
return;
}
if (!_remove)
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
if (m_dataType.getStorageBytes() == 32)
@ -236,16 +243,18 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
"Wrong type conversation for assignment.");
if (m_dataType.getCategory() == Type::Category::Array)
{
m_context << eth::Instruction::POP; // remove byte offset
ArrayUtils(m_context).copyArrayToStorage(
dynamic_cast<ArrayType const&>(m_dataType),
dynamic_cast<ArrayType const&>(_sourceType));
if (_move)
utils.popStackElement(m_dataType);
m_context << eth::Instruction::POP;
}
else if (m_dataType.getCategory() == Type::Category::Struct)
{
// stack layout: source_ref [source_offset] target_ref target_offset
// note that we have structs, so offsets should be zero and are ignored
// stack layout: source_ref target_ref target_offset
// note that we have structs, so offset should be zero and are ignored
m_context << eth::Instruction::POP;
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
auto const& sourceType = dynamic_cast<StructType const&>(_sourceType);
solAssert(
@ -262,44 +271,37 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
TypePointer sourceMemberType = sourceType.getMemberType(member.name);
if (sourceType.location() == DataLocation::Storage)
{
// stack layout: source_ref source_offset target_ref target_offset
// stack layout: source_ref target_ref
pair<u256, unsigned> const& offsets = sourceType.getStorageOffsetsOfMember(member.name);
m_context << offsets.first << eth::Instruction::DUP5 << eth::Instruction::ADD;
m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD;
m_context << u256(offsets.second);
// stack: source_ref source_off target_ref target_off source_member_ref source_member_off
// stack: source_ref target_ref source_member_ref source_member_off
StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
// stack: source_ref source_off target_ref target_off source_value...
// stack: source_ref target_ref source_value...
}
else
{
solAssert(sourceType.location() == DataLocation::Memory, "");
// stack layout: source_ref target_ref target_offset
// stack layout: source_ref target_ref
TypePointer sourceMemberType = sourceType.getMemberType(member.name);
m_context << sourceType.memoryOffsetOfMember(member.name);
m_context << eth::Instruction::DUP4 << eth::Instruction::ADD;
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true);
// stack layout: source_ref target_ref target_offset source_value...
// stack layout: source_ref target_ref source_value...
}
unsigned stackSize = sourceMemberType->getSizeOnStack();
pair<u256, unsigned> const& offsets = structType.getStorageOffsetsOfMember(member.name);
m_context << eth::dupInstruction(2 + stackSize) << offsets.first << eth::Instruction::ADD;
m_context << eth::dupInstruction(1 + stackSize) << offsets.first << eth::Instruction::ADD;
m_context << u256(offsets.second);
// stack: source_ref [source_off] target_ref target_off source_value... target_member_ref target_member_byte_off
// stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
}
// stack layout: source_ref [source_offset] target_ref target_offset
unsigned sourceStackSize = sourceType.getSizeOnStack();
// stack layout: source_ref target_ref
solAssert(sourceType.getSizeOnStack() == 1, "Unexpected source size.");
if (_move)
utils.popStackSlots(2 + sourceType.getSizeOnStack());
else if (sourceType.getSizeOnStack() >= 1)
{
// remove the source ref
solAssert(sourceStackSize <= 2, "Invalid stack size.");
m_context << eth::swapInstruction(sourceStackSize);
if (sourceStackSize == 2)
m_context << eth::Instruction::POP;
m_context << eth::Instruction::SWAP2 << eth::Instruction::POP;
}
utils.popStackSlots(2);
else
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
}
else
BOOST_THROW_EXCEPTION(
@ -429,8 +431,6 @@ StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, const
m_arrayType(_arrayType)
{
solAssert(m_arrayType.isDynamicallySized(), "");
// storage byte offset must be zero
m_context << eth::Instruction::POP;
}
void StorageArrayLength::retrieveValue(SourceLocation const&, bool _remove) const

29
libsolidity/Types.cpp

@ -210,6 +210,8 @@ TypePointer Type::forLiteral(Literal const& _literal)
case Token::FalseLiteral:
return make_shared<BoolType>();
case Token::Number:
if (!IntegerConstantType::isValidLiteral(_literal))
return TypePointer();
return make_shared<IntegerConstantType>(_literal);
case Token::StringLiteral:
return make_shared<StringLiteralType>(_literal);
@ -321,6 +323,19 @@ const MemberList IntegerType::AddressMemberList({
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
});
bool IntegerConstantType::isValidLiteral(const Literal& _literal)
{
try
{
bigint x(_literal.getValue());
}
catch (...)
{
return false;
}
return true;
}
IntegerConstantType::IntegerConstantType(Literal const& _literal)
{
m_value = bigint(_literal.getValue());
@ -823,11 +838,9 @@ unsigned ArrayType::getSizeOnStack() const
if (m_location == DataLocation::CallData)
// offset [length] (stack top)
return 1 + (isDynamicallySized() ? 1 : 0);
else if (m_location == DataLocation::Storage)
// storage_key storage_offset
return 2;
else
// offset
// storage slot or memory offset
// byte offset inside storage value is omitted
return 1;
}
@ -1035,14 +1048,6 @@ bool StructType::canLiveOutsideStorage() const
return true;
}
unsigned StructType::getSizeOnStack() const
{
if (location() == DataLocation::Storage)
return 2; // slot and offset
else
return 1;
}
string StructType::toString(bool _short) const
{
string ret = "struct " + m_struct.getName();

7
libsolidity/Types.h

@ -286,6 +286,9 @@ class IntegerConstantType: public Type
public:
virtual Category getCategory() const override { return Category::IntegerConstant; }
/// @returns true if the literal is a valid integer.
static bool isValidLiteral(Literal const& _literal);
explicit IntegerConstantType(Literal const& _literal);
explicit IntegerConstantType(bigint _value): m_value(_value) {}
@ -298,7 +301,6 @@ public:
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 1; }
virtual std::string toString(bool _short) const override;
virtual u256 literalValue(Literal const* _literal) const override;
@ -580,7 +582,6 @@ public:
u256 memorySize() const;
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override;
virtual unsigned getSizeOnStack() const override;
virtual std::string toString(bool _short) const override;
virtual MemberList const& getMembers() const override;
@ -616,7 +617,6 @@ public:
{
return externalType()->getCalldataEncodedSize(_padded);
}
virtual unsigned getSizeOnStack() const override { return 1; }
virtual unsigned getStorageBytes() const override;
virtual bool canLiveOutsideStorage() const override { return true; }
virtual std::string toString(bool _short) const override;
@ -812,7 +812,6 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual std::string toString(bool _short) const override;
virtual unsigned getSizeOnStack() const override { return 2; }
virtual bool canLiveOutsideStorage() const override { return false; }
TypePointer const& getKeyType() const { return m_keyType; }

69
libwhisper/WhisperDB.cpp

@ -0,0 +1,69 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file WhisperDB.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date July 2015
*/
#include "WhisperDB.h"
#include <boost/filesystem.hpp>
#include <libdevcore/FileSystem.h>
using namespace std;
using namespace dev;
using namespace dev::shh;
WhisperDB::WhisperDB()
{
string path = dev::getDataDir("shh");
boost::filesystem::create_directories(path);
leveldb::Options op;
op.create_if_missing = true;
op.max_open_files = 256;
leveldb::DB* p = nullptr;
leveldb::Status status = leveldb::DB::Open(op, path + "/messages", &p);
m_db.reset(p);
if (!status.ok())
BOOST_THROW_EXCEPTION(FailedToOpenLevelDB(status.ToString()));
}
string WhisperDB::lookup(dev::h256 const& _key) const
{
string ret;
leveldb::Slice slice((char const*)_key.data(), _key.size);
leveldb::Status status = m_db->Get(m_readOptions, slice, &ret);
if (!status.ok() && !status.IsNotFound())
BOOST_THROW_EXCEPTION(FailedLookupInLevelDB(status.ToString()));
return ret;
}
void WhisperDB::insert(dev::h256 const& _key, string const& _value)
{
leveldb::Slice slice((char const*)_key.data(), _key.size);
leveldb::Status status = m_db->Put(m_writeOptions, slice, _value);
if (!status.ok())
BOOST_THROW_EXCEPTION(FailedInsertInLevelDB(status.ToString()));
}
void WhisperDB::kill(dev::h256 const& _key)
{
leveldb::Slice slice((char const*)_key.data(), _key.size);
leveldb::Status status = m_db->Delete(m_writeOptions, slice);
if (!status.ok())
BOOST_THROW_EXCEPTION(FailedDeleteInLevelDB(status.ToString()));
}

55
libwhisper/WhisperDB.h

@ -0,0 +1,55 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file WhisperDB.h
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date July 2015
*/
#pragma once
#include <libdevcore/db.h>
#include <libdevcore/FixedHash.h>
#include "Common.h"
namespace dev
{
namespace shh
{
struct FailedToOpenLevelDB: virtual Exception { FailedToOpenLevelDB(std::string const& _message): Exception(_message) {} };
struct FailedInsertInLevelDB: virtual Exception { FailedInsertInLevelDB(std::string const& _message): Exception(_message) {} };
struct FailedLookupInLevelDB: virtual Exception { FailedLookupInLevelDB(std::string const& _message): Exception(_message) {} };
struct FailedDeleteInLevelDB: virtual Exception { FailedDeleteInLevelDB(std::string const& _message): Exception(_message) {} };
class WhisperDB
{
public:
WhisperDB();
~WhisperDB() {}
std::string lookup(dev::h256 const& _key) const;
void insert(dev::h256 const& _key, std::string const& _value);
void kill(dev::h256 const& _key);
private:
leveldb::ReadOptions m_readOptions;
leveldb::WriteOptions m_writeOptions;
std::unique_ptr<leveldb::DB> m_db;
};
}
}

4
test/TestHelper.cpp

@ -479,7 +479,7 @@ bytes importCode(json_spirit::mObject& _o)
{
bytes code;
if (_o["code"].type() == json_spirit::str_type)
if (_o["code"].get_str().find_first_of("0x") != 0)
if (_o["code"].get_str().find("0x") != 0)
code = compileLLL(_o["code"].get_str(), false);
else
code = fromHex(_o["code"].get_str().substr(2));
@ -767,6 +767,8 @@ Options::Options()
wallet = true;
else if (arg == "--nonetwork")
nonetwork = true;
else if (arg == "--network")
nonetwork = false;
else if (arg == "--nodag")
nodag = true;
else if (arg == "--all")

2
test/TestHelper.h

@ -224,7 +224,7 @@ public:
bool inputLimits = false;
bool bigData = false;
bool wallet = false;
bool nonetwork = false;
bool nonetwork = true;
bool nodag = true;
/// @}

30
test/libethcore/keymanager.cpp

@ -107,11 +107,10 @@ BOOST_AUTO_TEST_CASE(KeyManagerHints)
BOOST_AUTO_TEST_CASE(KeyManagerAccounts)
{
KeyManager km;
string password = "hardPassword";
TransientDirectory tmpDir;
km.setKeysFile(tmpDir.path()+ "keysFile.json");
KeyManager km(tmpDir.path()+ "keysFile.json", tmpDir.path());
BOOST_CHECK_NO_THROW(km.create(password));
BOOST_CHECK(km.accounts().empty());
@ -121,4 +120,31 @@ BOOST_AUTO_TEST_CASE(KeyManagerAccounts)
km.kill(a);
}
BOOST_AUTO_TEST_CASE(KeyManagerKill)
{
string password = "hardPassword";
TransientDirectory tmpDir;
KeyPair kp = KeyPair::create();
{
KeyManager km(tmpDir.path() + "keysFile.json", tmpDir.path());
BOOST_CHECK_NO_THROW(km.create(password));
BOOST_CHECK(km.accounts().empty());
BOOST_CHECK(km.load(password));
BOOST_CHECK(km.import(kp.secret(), "test"));
}
{
KeyManager km(tmpDir.path() + "keysFile.json", tmpDir.path());
BOOST_CHECK(km.load(password));
Addresses addresses = km.accounts();
BOOST_CHECK(addresses.size() == 1 && addresses[0] == kp.address());
km.kill(addresses[0]);
}
{
KeyManager km(tmpDir.path() + "keysFile.json", tmpDir.path());
BOOST_CHECK(km.load(password));
BOOST_CHECK(km.accounts().empty());
}
}
BOOST_AUTO_TEST_SUITE_END()

70
test/libethereum/BlockchainTestsFiller/bcRPC_API_TestFiller.json

@ -27,6 +27,7 @@
},
"blocks" : [
{
"blocknumber" : "1",
"transactions" : [
{
"data" : "create contract: 6295ee1b4f6dd65047762f924ecd367c17eabf8f",
@ -42,7 +43,8 @@
"uncleHeaders" : [
]
},
{
{
"blocknumber" : "2",
"transactions" : [
{
"data" : "getBool",
@ -59,6 +61,7 @@
]
},
{
"blocknumber" : "3",
"transactions" : [
{
"data" : "getInt8",
@ -75,6 +78,7 @@
]
},
{
"blocknumber" : "4",
"transactions" : [
{
"data" : "getUint8",
@ -127,6 +131,7 @@
]
},
{
"blocknumber" : "5",
"transactions" : [
{
"data" : "getInt256",
@ -143,6 +148,7 @@
]
},
{
"blocknumber" : "6",
"transactions" : [
{
"data" : "getUint256",
@ -159,6 +165,7 @@
]
},
{
"blocknumber" : "7",
"transactions" : [
{
"data" : "getAddress",
@ -175,6 +182,7 @@
]
},
{
"blocknumber" : "8",
"transactions" : [
{
"data" : "getBytes32",
@ -191,6 +199,7 @@
]
},
{
"blocknumber" : "9",
"transactions" : [
{
"data" : "setBool",
@ -207,6 +216,7 @@
]
},
{
"blocknumber" : "10",
"transactions" : [
{
"data" : "setBool",
@ -223,6 +233,7 @@
]
},
{
"blocknumber" : "11",
"transactions" : [
{
"data" : "setInt8",
@ -239,6 +250,7 @@
]
},
{
"blocknumber" : "12",
"transactions" : [
{
"data" : "setUint8",
@ -255,6 +267,7 @@
]
},
{
"blocknumber" : "13",
"transactions" : [
{
"data" : "setInt256",
@ -271,6 +284,7 @@
]
},
{
"blocknumber" : "14",
"transactions" : [
{
"data" : "setUint256",
@ -287,6 +301,7 @@
]
},
{
"blocknumber" : "15",
"transactions" : [
{
"data" : "setAddress",
@ -303,6 +318,7 @@
]
},
{
"blocknumber" : "16",
"transactions" : [
{
"data" : "setBytes32",
@ -319,6 +335,7 @@
]
},
{
"blocknumber" : "17",
"transactions" : [
{
"data" : "getInt8",
@ -335,6 +352,7 @@
]
},
{
"blocknumber" : "18",
"transactions" : [
{
"data" : "getUint8",
@ -351,6 +369,7 @@
]
},
{
"blocknumber" : "19",
"transactions" : [
{
"data" : "getInt256",
@ -367,6 +386,7 @@
]
},
{
"blocknumber" : "20",
"transactions" : [
{
"data" : "getUint256",
@ -383,6 +403,7 @@
]
},
{
"blocknumber" : "21",
"transactions" : [
{
"data" : "getAddress",
@ -399,6 +420,7 @@
]
},
{
"blocknumber" : "22",
"transactions" : [
{
"data" : "getBytes32",
@ -415,6 +437,7 @@
]
},
{
"blocknumber" : "23",
"transactions" : [
{
"data" : "log0",
@ -431,6 +454,7 @@
]
},
{
"blocknumber" : "24",
"transactions" : [
{
"data" : "log0a",
@ -447,6 +471,7 @@
]
},
{
"blocknumber" : "25",
"transactions" : [
{
"data" : "log1",
@ -463,6 +488,7 @@
]
},
{
"blocknumber" : "26",
"transactions" : [
{
"data" : "log1a",
@ -479,6 +505,43 @@
]
},
{
"blocknumber" : "27",
"reverted": true,
"transactions" : [
{
"data" : "log1",
"data" : "0x4e7ad367",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "26",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"blocknumber" : "28",
"reverted": true,
"transactions" : [
{
"data" : "log1a",
"data" : "0x4e7ad367",
"gasLimit" : "314159",
"gasPrice" : "1",
"nonce" : "27",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"value" : "10"
}
],
"uncleHeaders" : [
]
},
{
"blocknumber" : "27",
"transactions" : [
{
"data" : "log2",
@ -495,6 +558,7 @@
]
},
{
"blocknumber" : "28",
"transactions" : [
{
"data" : "log2a",
@ -511,6 +575,7 @@
]
},
{
"blocknumber" : "29",
"transactions" : [
{
"data" : "log3",
@ -527,6 +592,7 @@
]
},
{
"blocknumber" : "30",
"transactions" : [
{
"data" : "log3a",
@ -543,6 +609,7 @@
]
},
{
"blocknumber" : "31",
"transactions" : [
{
"data" : "log4",
@ -559,6 +626,7 @@
]
},
{
"blocknumber" : "32",
"transactions" : [
{
"data" : "log4a",

36
test/libp2p/peer.cpp

@ -43,28 +43,32 @@ BOOST_AUTO_TEST_CASE(host)
if (test::Options::get().nonetwork)
return;
VerbosityHolder sentinel(10);
VerbosityHolder setTemporaryLevel(10);
NetworkPreferences host1prefs("127.0.0.1", 30301, false);
NetworkPreferences host2prefs("127.0.0.1", 30302, false);
NetworkPreferences host2prefs("127.0.0.1", 30302, false);
Host host1("Test", host1prefs);
host1.start();
Host host2("Test", host2prefs);
auto node2 = host2.id();
host1.start();
host2.start();
while (!host2.haveNetwork())
this_thread::sleep_for(chrono::milliseconds(20));
auto node2 = host2.id();
int const step = 10;
for (int i = 0; i < 3000 && (!host1.isStarted() || !host2.isStarted()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host1.isStarted() && host2.isStarted());
host1.addNode(node2, NodeIPEndpoint(bi::address::from_string("127.0.0.1"), host2prefs.listenPort, host2prefs.listenPort));
this_thread::sleep_for(chrono::seconds(3));
auto host1peerCount = host1.peerCount();
auto host2peerCount = host2.peerCount();
BOOST_REQUIRE_EQUAL(host1peerCount, 1);
BOOST_REQUIRE_EQUAL(host2peerCount, 1);
for (int i = 0; i < 3000 && (!host1.haveNetwork() || !host2.haveNetwork()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host1.haveNetwork() && host2.haveNetwork());
for (int i = 0; i < 3000 && (!host1.peerCount() || !host2.peerCount()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE_EQUAL(host1.peerCount(), 1);
BOOST_REQUIRE_EQUAL(host2.peerCount(), 1);
}
BOOST_AUTO_TEST_CASE(networkConfig)

188
test/libp2p/rlpx.cpp

@ -30,10 +30,13 @@
#include <libdevcrypto/ECDHE.h>
#include <libdevcrypto/CryptoPP.h>
#include <libp2p/RLPxHandshake.h>
#include <libp2p/RLPXFrameWriter.h>
#include <libp2p/RLPXFrameReader.h>
using namespace std;
using namespace dev;
using namespace dev::crypto;
using namespace dev::p2p;
using namespace CryptoPP;
BOOST_AUTO_TEST_SUITE(rlpx)
@ -450,5 +453,190 @@ BOOST_AUTO_TEST_CASE(ecies_interop_test_primitives)
BOOST_REQUIRE(plainTest3 == expectedPlain3);
}
BOOST_AUTO_TEST_CASE(segmentedPacketFlush)
{
ECDHE localEph;
h256 localNonce = Nonce::get();
ECDHE remoteEph;
h256 remoteNonce = Nonce::get();
bytes ackCipher{0};
bytes authCipher{1};
RLPXFrameCoder encoder(true, remoteEph.pubkey(), remoteNonce, localEph, localNonce, &ackCipher, &authCipher);
/// Test writing a 64byte RLPStream and drain with frame size that
/// forces packet to be pieced into 4 frames.
/// (Minimum frame size has room for 16 bytes of payload)
// 64-byte payload minus 3 bytes for packet-type and RLP overhead.
// Note: mux() is called with RLPXFrameWriter::MinFrameDequeLength
// which is equal to 64byte, however, after overhead this means
// there are only 16 bytes of payload which will be dequed.
auto dequeLen = 16;
bytes stuff = sha3("A").asBytes();
bytes payload;
payload += stuff;
payload += stuff;
payload.resize(payload.size() - 3 /* packet-type, rlp-overhead */);
BOOST_REQUIRE_EQUAL(61, payload.size());
auto drains = (payload.size() + 3) / dequeLen;
BOOST_REQUIRE_EQUAL(4, drains);
RLPXFrameWriter w(0);
RLPStream rlpPayload(RLPStream() << payload);
uint8_t packetType = 0;
bytes packetTypeRLP = (RLPStream() << packetType).out();
w.enque(packetType, rlpPayload);
vector<bytes> encframes;
for (unsigned i = 1; i < drains; i++)
{
auto n = w.mux(encoder, RLPXFrameWriter::MinFrameDequeLength, encframes);
BOOST_REQUIRE_EQUAL(0, n);
BOOST_REQUIRE_EQUAL(encframes.size(), i);
}
BOOST_REQUIRE_EQUAL(1, w.mux(encoder, RLPXFrameWriter::MinFrameDequeLength, encframes));
BOOST_REQUIRE_EQUAL(encframes.size(), drains);
BOOST_REQUIRE_EQUAL(0, w.mux(encoder, RLPXFrameWriter::MinFrameDequeLength, encframes));
BOOST_REQUIRE_EQUAL(encframes.size(), drains);
// we should now have a bunch of ciphertext in encframes
BOOST_REQUIRE(encframes.size() == drains);
for (auto const& c: encframes)
{
BOOST_REQUIRE_EQUAL(c.size(), RLPXFrameWriter::MinFrameDequeLength);
}
// read and assemble dequed encframes
RLPXFrameCoder decoder(false, localEph.pubkey(), localNonce, remoteEph, remoteNonce, &ackCipher, &authCipher);
vector<RLPXPacket> packets;
RLPXFrameReader r(0);
for (size_t i = 0; i < encframes.size(); i++)
{
bytesRef frameWithHeader(encframes[i].data(), encframes[i].size());
bytesRef header = frameWithHeader.cropped(0, h256::size);
bool decryptedHeader = decoder.authAndDecryptHeader(header);
BOOST_REQUIRE(decryptedHeader);
bytesRef frame = frameWithHeader.cropped(h256::size);
RLPXFrameInfo f(header);
for (RLPXPacket& p: r.demux(decoder, f, frame))
packets.push_back(move(p));
}
BOOST_REQUIRE_EQUAL(packets.size(), 1);
BOOST_REQUIRE_EQUAL(packets.front().size(), packetTypeRLP.size() + rlpPayload.out().size());
BOOST_REQUIRE_EQUAL(sha3(RLP(packets.front().data()).payload()), sha3(payload));
BOOST_REQUIRE_EQUAL(sha3(packets.front().type()), sha3(packetTypeRLP));
}
BOOST_AUTO_TEST_CASE(coalescedPacketsPadded)
{
ECDHE localEph;
h256 localNonce = Nonce::get();
ECDHE remoteEph;
h256 remoteNonce = Nonce::get();
bytes ackCipher{0};
bytes authCipher{1};
RLPXFrameCoder encoder(true, remoteEph.pubkey(), remoteNonce, localEph, localNonce, &ackCipher, &authCipher);
/// Test writing four 32 byte RLPStream packets such that
/// a single 1KB frame will incldue all four packets.
auto dequeLen = 1024; // sufficient enough for all packets
bytes stuff = sha3("A").asBytes();
vector<bytes> packetsOut;
for (unsigned i = 0; i < 4; i++)
packetsOut.push_back(stuff);
RLPXFrameWriter w(0);
uint8_t packetType = 127;
bytes packetTypeRLP((RLPStream() << packetType).out());
for (auto const& p: packetsOut)
w.enque(packetType, (RLPStream() << p));
vector<bytes> encframes;
BOOST_REQUIRE_EQUAL(4, w.mux(encoder, dequeLen, encframes));
BOOST_REQUIRE_EQUAL(0, w.mux(encoder, dequeLen, encframes));
BOOST_REQUIRE_EQUAL(1, encframes.size());
auto expectedFrameSize = RLPXFrameWriter::EmptyFrameLength + packetsOut.size() * (/*packet-type*/ 1 + h256::size + /*rlp-prefix*/ 1);
expectedFrameSize += ((16 - (expectedFrameSize % 16)) % 16);
BOOST_REQUIRE_EQUAL(expectedFrameSize, encframes[0].size());
// read and assemble dequed encframes
RLPXFrameCoder decoder(false, localEph.pubkey(), localNonce, remoteEph, remoteNonce, &ackCipher, &authCipher);
vector<RLPXPacket> packets;
RLPXFrameReader r(0);
bytesRef frameWithHeader(encframes[0].data(), encframes[0].size());
bytesRef header = frameWithHeader.cropped(0, h256::size);
bool decryptedHeader = decoder.authAndDecryptHeader(header);
BOOST_REQUIRE(decryptedHeader);
bytesRef frame = frameWithHeader.cropped(h256::size);
RLPXFrameInfo f(header);
BOOST_REQUIRE_EQUAL(f.multiFrame, false);
for (RLPXPacket& p: r.demux(decoder, f, frame))
packets.push_back(move(p));
RLPStream rlpPayload;
rlpPayload << stuff;
BOOST_REQUIRE_EQUAL(packets.size(), 4);
while (!packets.empty())
{
BOOST_REQUIRE_EQUAL(packets.back().size(), packetTypeRLP.size() + rlpPayload.out().size());
BOOST_REQUIRE_EQUAL(sha3(RLP(packets.back().data()).payload()), sha3(stuff));
BOOST_REQUIRE_EQUAL(sha3(packets.back().type()), sha3(packetTypeRLP));
packets.pop_back();
}
}
BOOST_AUTO_TEST_CASE(singleFramePacketFlush)
{
ECDHE localEph;
h256 localNonce = Nonce::get();
ECDHE remoteEph;
h256 remoteNonce = Nonce::get();
bytes ackCipher{0};
bytes authCipher{1};
RLPXFrameCoder encoder(true, remoteEph.pubkey(), remoteNonce, localEph, localNonce, &ackCipher, &authCipher);
/// Test writing four 32 byte RLPStream packets such that
/// a single 1KB frame will incldue all four packets.
bytes stuff = sha3("A").asBytes();
RLPXFrameWriter w(0);
uint8_t packetType = 127;
bytes packetTypeRLP((RLPStream() << packetType).out());
w.enque(packetType, (RLPStream() << stuff));
vector<bytes> encframes;
auto dequeLen = RLPXFrameWriter::EmptyFrameLength + 34;
dequeLen += ((16 - (dequeLen % 16)) % 16);
BOOST_REQUIRE_EQUAL(1, w.mux(encoder, dequeLen, encframes));
BOOST_REQUIRE_EQUAL(0, w.mux(encoder, dequeLen, encframes));
BOOST_REQUIRE_EQUAL(1, encframes.size());
BOOST_REQUIRE_EQUAL(dequeLen, encframes[0].size());
// read and assemble dequed encframes
RLPXFrameCoder decoder(false, localEph.pubkey(), localNonce, remoteEph, remoteNonce, &ackCipher, &authCipher);
vector<RLPXPacket> packets;
RLPXFrameReader r(0);
bytesRef frameWithHeader(encframes[0].data(), encframes[0].size());
bytesRef header = frameWithHeader.cropped(0, h256::size);
bool decryptedHeader = decoder.authAndDecryptHeader(header);
BOOST_REQUIRE(decryptedHeader);
bytesRef frame = frameWithHeader.cropped(h256::size);
RLPXFrameInfo f(header);
BOOST_REQUIRE_EQUAL(f.multiFrame, false);
for (RLPXPacket& p: r.demux(decoder, f, frame))
packets.push_back(move(p));
RLPStream rlpPayload;
rlpPayload << stuff;
BOOST_REQUIRE_EQUAL(packets.size(), 1);
BOOST_REQUIRE_EQUAL(packets.back().size(), packetTypeRLP.size() + rlpPayload.out().size());
BOOST_REQUIRE_EQUAL(sha3(RLP(packets.back().data()).payload()), sha3(stuff));
BOOST_REQUIRE_EQUAL(sha3(packets.back().type()), sha3(packetTypeRLP));
}
BOOST_AUTO_TEST_CASE(manyProtocols)
{
}
BOOST_AUTO_TEST_SUITE_END()

16
test/libsolidity/SolidityEndToEndTest.cpp

@ -585,6 +585,22 @@ BOOST_AUTO_TEST_CASE(inc_dec_operators)
BOOST_CHECK(callContractFunction("f()") == encodeArgs(0x53866));
}
BOOST_AUTO_TEST_CASE(bytes_comparison)
{
char const* sourceCode = R"(
contract test {
function f() returns (bool) {
bytes2 a = "a";
bytes2 x = "aa";
bytes2 b = "b";
return a < x && x < b;
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("f()") == encodeArgs(true));
}
BOOST_AUTO_TEST_CASE(state_smoke_test)
{
char const* sourceCode = "contract test {\n"

24
test/libsolidity/SolidityNameAndTypeResolution.cpp

@ -2110,6 +2110,30 @@ BOOST_AUTO_TEST_CASE(literal_strings)
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(invalid_integer_literal_fraction)
{
char const* text = R"(
contract Foo {
function f() {
var x = 1.20;
}
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp)
{
char const* text = R"(
contract Foo {
function f() {
var x = 1e2;
}
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END()
}

122
test/libwhisper/whisperDB.cpp

@ -0,0 +1,122 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file whisperMessage.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date July 2015
*/
#include <thread>
#include <boost/test/unit_test.hpp>
#include <libwhisper/WhisperDB.h>
using namespace std;
using namespace dev;
using namespace dev::shh;
BOOST_AUTO_TEST_SUITE(whisperDB)
BOOST_AUTO_TEST_CASE(basic)
{
VerbosityHolder setTemporaryLevel(10);
cnote << "Testing Whisper DB...";
string s;
string const text1 = "lorem";
string const text2 = "ipsum";
h256 h1(0xBEEF);
h256 h2(0xC0FFEE);
WhisperDB db;
db.kill(h1);
db.kill(h2);
s = db.lookup(h1);
BOOST_REQUIRE(s.empty());
db.insert(h1, text2);
s = db.lookup(h2);
BOOST_REQUIRE(s.empty());
s = db.lookup(h1);
BOOST_REQUIRE(!s.compare(text2));
db.insert(h1, text1);
s = db.lookup(h2);
BOOST_REQUIRE(s.empty());
s = db.lookup(h1);
BOOST_REQUIRE(!s.compare(text1));
db.insert(h2, text2);
s = db.lookup(h2);
BOOST_REQUIRE(!s.compare(text2));
s = db.lookup(h1);
BOOST_REQUIRE(!s.compare(text1));
db.kill(h1);
db.kill(h2);
s = db.lookup(h2);
BOOST_REQUIRE(s.empty());
s = db.lookup(h1);
BOOST_REQUIRE(s.empty());
}
BOOST_AUTO_TEST_CASE(persistence)
{
VerbosityHolder setTemporaryLevel(10);
cnote << "Testing persistence of Whisper DB...";
string s;
string const text1 = "sator";
string const text2 = "arepo";
h256 const h1(0x12345678);
h256 const h2(0xBADD00DE);
{
WhisperDB db;
db.kill(h1);
db.kill(h2);
s = db.lookup(h1);
BOOST_REQUIRE(s.empty());
db.insert(h1, text2);
s = db.lookup(h2);
BOOST_REQUIRE(s.empty());
s = db.lookup(h1);
BOOST_REQUIRE(!s.compare(text2));
}
this_thread::sleep_for(chrono::milliseconds(20));
{
WhisperDB db;
db.insert(h1, text1);
db.insert(h2, text2);
}
this_thread::sleep_for(chrono::milliseconds(20));
{
WhisperDB db;
s = db.lookup(h2);
BOOST_REQUIRE(!s.compare(text2));
s = db.lookup(h1);
BOOST_REQUIRE(!s.compare(text1));
db.kill(h1);
db.kill(h2);
}
}
BOOST_AUTO_TEST_SUITE_END()
Loading…
Cancel
Save