Browse Source

Merge branch 'develop' into build_enhancement

Conflicts:
	eth/main.cpp
	neth/main.cpp
cl-refactor
Marek Kotewicz 10 years ago
parent
commit
1315b56b25
  1. 2
      CMakeLists.txt
  2. 11
      alethzero/MainWin.cpp
  3. 4
      cmake/EthDependencies.cmake
  4. 25
      eth/main.cpp
  5. 2
      libdevcore/Common.cpp
  6. 3
      libdevcore/Worker.cpp
  7. 18
      libdevcore/Worker.h
  8. 18
      libethcore/BlockInfo.cpp
  9. 4
      libethcore/BlockInfo.h
  10. 4
      libethcore/CommonEth.cpp
  11. 21
      libethereum/BlockChain.cpp
  12. 12
      libethereum/BlockChain.h
  13. 22
      libethereum/BlockDetails.h
  14. 1
      libethereum/EthereumHost.h
  15. 14
      libethereum/Executive.cpp
  16. 2
      libethereum/Interface.h
  17. 2
      libethereum/MessageFilter.cpp
  18. 9
      libethereum/State.cpp
  19. 11
      libethereum/Utility.h
  20. 6
      libevm/ExtVMFace.h
  21. 6
      libevm/FeeStructure.cpp
  22. 6
      libevm/FeeStructure.h
  23. 30
      libevm/VM.h
  24. 117
      libevmcore/Assembly.cpp
  25. 3
      libevmcore/Assembly.h
  26. 2
      libevmcore/Instruction.h
  27. 14
      libp2p/Common.cpp
  28. 1
      libp2p/Common.h
  29. 658
      libp2p/Host.cpp
  30. 72
      libp2p/Host.h
  31. 2
      libp2p/Session.cpp
  32. 83
      libsolidity/AST.cpp
  33. 90
      libsolidity/AST.h
  34. 1
      libsolidity/ASTForward.h
  35. 15
      libsolidity/Compiler.cpp
  36. 5
      libsolidity/Compiler.h
  37. 5
      libsolidity/CompilerContext.cpp
  38. 4
      libsolidity/CompilerContext.h
  39. 7
      libsolidity/CompilerStack.cpp
  40. 9
      libsolidity/CompilerStack.h
  41. 10
      libsolidity/DeclarationContainer.cpp
  42. 10
      libsolidity/DeclarationContainer.h
  43. 235
      libsolidity/ExpressionCompiler.cpp
  44. 17
      libsolidity/ExpressionCompiler.h
  45. 101
      libsolidity/GlobalContext.cpp
  46. 62
      libsolidity/GlobalContext.h
  47. 62
      libsolidity/NameAndTypeResolver.cpp
  48. 17
      libsolidity/NameAndTypeResolver.h
  49. 9
      libsolidity/Parser.cpp
  50. 291
      libsolidity/Scanner.cpp
  51. 32
      libsolidity/Scanner.h
  52. 15
      libsolidity/Token.h
  53. 180
      libsolidity/Types.cpp
  54. 160
      libsolidity/Types.h
  55. 1
      libweb3jsonrpc/WebThreeStubServer.cpp
  56. 6
      libwhisper/WhisperHost.cpp
  57. 9
      libwhisper/WhisperHost.h
  58. 14
      libwhisper/WhisperPeer.cpp
  59. 2
      libwhisper/WhisperPeer.h
  60. 1
      mix/.gitignore
  61. 36
      mix/ApplicationCtx.cpp
  62. 55
      mix/ApplicationCtx.h
  63. 94
      mix/CMakeLists.txt
  64. 88
      mix/CodeEditorExtensionManager.cpp
  65. 61
      mix/CodeEditorExtensionManager.h
  66. 97
      mix/ConstantCompilationCtrl.cpp
  67. 55
      mix/ConstantCompilationCtrl.h
  68. 61
      mix/ConstantCompilationModel.cpp
  69. 51
      mix/ConstantCompilationModel.h
  70. 38
      mix/EthereumMacOSXBundleInfo.plist.in
  71. 44
      mix/Extension.cpp
  72. 48
      mix/Extension.h
  73. 45
      mix/MixApplication.cpp
  74. 46
      mix/MixApplication.h
  75. 40
      mix/main.cpp
  76. 8
      mix/qml.qrc
  77. 35
      mix/qml/BasicContent.qml
  78. 54
      mix/qml/MainContent.qml
  79. 23
      mix/qml/TabStyle.qml
  80. 24
      mix/qml/main.qml
  81. 30
      neth/main.cpp
  82. 73
      test/TestHelper.cpp
  83. 4
      test/TestHelper.h
  84. 25
      test/createRandomTest.cpp
  85. 4
      test/solidityCompiler.cpp
  86. 196
      test/solidityEndToEndTest.cpp
  87. 2
      test/solidityExpressionCompiler.cpp
  88. 52
      test/solidityNameAndTypeResolution.cpp
  89. 161
      test/solidityParser.cpp
  90. 37
      test/stExampleFiller.json
  91. 68
      test/stPreCompiledContractsFiller.json
  92. 41
      test/stSpecialTestFiller.json
  93. 12
      test/stSystemOperationsTestFiller.json
  94. 8
      test/state.cpp
  95. 47
      test/vm.cpp
  96. 2
      test/vm.h
  97. 4
      test/vmIOandFlowOperationsTestFiller.json
  98. 1266
      test/vmLogTestFiller.json
  99. 31
      test/vmPushDupSwapTestFiller.json

2
CMakeLists.txt

@ -161,6 +161,7 @@ if (NOT LANGUAGES)
add_subdirectory(alethzero)
endif()
add_subdirectory(third)
add_subdirectory(mix)
if(QTQML)
#add_subdirectory(iethxi)
#add_subdirectory(walleth) // resurect once we want to submit ourselves to QML.
@ -174,3 +175,4 @@ add_test(NAME alltests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testet
#unset(TARGET_PLATFORM CACHE)

11
alethzero/MainWin.cpp

@ -1259,9 +1259,11 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>D/TD: <b>2^" << log2((double)info.difficulty) << "</b>/<b>2^" << log2((double)details.totalDifficulty) << "</b>";
s << "&nbsp;&emsp;&nbsp;Children: <b>" << details.children.size() << "</b></h5>";
s << "<br/>Gas used/limit: <b>" << info.gasUsed << "</b>/<b>" << info.gasLimit << "</b>";
s << "&nbsp;&emsp;&nbsp;Minimum gas price: <b>" << formatBalance(info.minGasPrice) << "</b>";
s << "<br/>Coinbase: <b>" << pretty(info.coinbaseAddress).toHtmlEscaped().toStdString() << "</b> " << info.coinbaseAddress;
s << "<br/>Nonce: <b>" << info.nonce << "</b>";
s << "<br/>Hash w/o nonce: <b>" << info.headerHashWithoutNonce() << "</b>";
s << "<br/>Difficulty: <b>" << info.difficulty << "</b>";
s << "<br/>Proof-of-Work: <b>" << ProofOfWork::eval(info.headerHashWithoutNonce(), info.nonce) << " &lt;= " << (h256)u256((bigint(1) << 256) / info.difficulty) << "</b>";
s << "<br/>Parent: <b>" << info.parentHash << "</b>";
// s << "<br/>Bloom: <b>" << details.bloom << "</b>";
s << "<br/>Log Bloom: <b>" << info.logBloom << "</b>";
@ -1282,6 +1284,7 @@ void Main::on_blocks_currentItemChanged()
for (auto const& i: block[1])
s << "<br/>" << sha3(i.data()).abridged();// << ": <b>" << i[1].toHash<h256>() << "</b> [<b>" << i[2].toInt<u256>() << "</b> used]";
s << "<br/>Post: <b>" << info.stateRoot << "</b>";
s << "<br/>Dump: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(block[0].data()) << "</span>";
}
else
{
@ -1665,7 +1668,7 @@ void Main::on_data_textChanged()
errs.append("<div style=\"border-left: 6px solid #c00; margin-top: 2px\">" + QString::fromStdString(i).toHtmlEscaped() + "</div>");
}
ui->code->setHtml(errs + lll + solidity + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped());
ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 0));
ui->gas->setMinimum((qint64)Client::txGas(m_data, 0));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
ui->gas->setEnabled(true);
@ -1676,7 +1679,7 @@ void Main::on_data_textChanged()
ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true)));
if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size())
{
ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 1));
ui->gas->setMinimum((qint64)Client::txGas(m_data, 1));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
ui->gas->setEnabled(true);
@ -1685,7 +1688,7 @@ void Main::on_data_textChanged()
{
if (ui->gas->isEnabled())
m_backupGas = ui->gas->value();
ui->gas->setValue((qint64)Client::txGas(m_data.size()));
ui->gas->setValue((qint64)Client::txGas(m_data));
ui->gas->setEnabled(false);
}
}

4
cmake/EthDependencies.cmake

@ -6,10 +6,6 @@
string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name)
set (CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/extdep/install/${_system_name}")
# mac os
# https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/UsingDynamicLibraries.html
set(ENV{DYLD_FALLBACK_LIBRARY_PATH} ${CMAKE_PREFIX_PATH}/lib)
# Dependencies must have a version number, to ensure reproducible build. The version provided here is the one that is in the extdep repository. If you use system libraries, version numbers may be different.
find_package (CryptoPP 5.6.2 EXACT REQUIRED)

25
eth/main.cpp

@ -27,9 +27,6 @@
#include <signal.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim_all.hpp>
#if ETH_JSONRPC
#include <libweb3jsonrpc/CorsHttpServer.h>
#endif
#include <libdevcrypto/FileSystem.h>
#include <libevmcore/Instruction.h>
#include <libevm/VM.h>
@ -41,6 +38,7 @@
#endif
#if ETH_JSONRPC
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <libweb3jsonrpc/CorsHttpServer.h>
#endif
#include "BuildInfo.h"
using namespace std;
@ -336,12 +334,12 @@ int main(int argc, char** argv)
web3.connect(remoteHost, remotePort);
#if ETH_JSONRPC
unique_ptr<WebThreeStubServer> jsonrpcServer;
shared_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector;
if (jsonrpc > -1)
{
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::CorsHttpServer(jsonrpc));
jsonrpcServer = unique_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, {us}));
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
@ -429,8 +427,9 @@ int main(int argc, char** argv)
{
if (jsonrpc < 0)
jsonrpc = 8080;
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::CorsHttpServer(jsonrpc));
jsonrpcServer = auto_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, {us}));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
@ -494,14 +493,12 @@ int main(int argc, char** argv)
cnote << ssbd.str();
int ssize = sechex.length();
int size = hexAddr.length();
u256 minGas = (u256)Client::txGas(data.size(), 0);
u256 minGas = (u256)Client::txGas(data, 0);
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else if (gasPrice < info.minGasPrice)
cwarn << "Minimum gas price is" << info.minGasPrice;
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
@ -561,9 +558,9 @@ int main(int argc, char** argv)
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(0, 0);
u256 minGas = (u256)Client::txGas(bytes(), 0);
Address dest = h160(fromHex(hexAddr));
c->transact(us.secret(), amount, dest, bytes(), minGas, info.minGasPrice);
c->transact(us.secret(), amount, dest, bytes(), minGas);
}
}
else
@ -600,11 +597,9 @@ int main(int argc, char** argv)
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Client::txGas(init.size(), 0);
u256 minGas = (u256)Client::txGas(init, 0);
if (endowment < 0)
cwarn << "Invalid endowment";
else if (gasPrice < info.minGasPrice)
cwarn << "Minimum gas price is" << info.minGasPrice;
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using namespace dev;
namespace dev
{
char const* Version = "0.7.10";
char const* Version = "0.7.12";
}

3
libdevcore/Worker.cpp

@ -41,7 +41,8 @@ void Worker::startWorking()
startedWorking();
while (!m_stop)
{
this_thread::sleep_for(chrono::milliseconds(30));
if (m_idleWaitMs)
this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs));
doWork();
}
cdebug << "Finishing up worker thread";

18
libdevcore/Worker.h

@ -31,7 +31,7 @@ namespace dev
class Worker
{
protected:
Worker(std::string const& _name = "anon"): m_name(_name) {}
Worker(std::string const& _name = "anon", unsigned _idleWaitMs = 30): m_name(_name), m_idleWaitMs(_idleWaitMs) {}
/// Move-constructor.
Worker(Worker&& _m) { std::swap(m_name, _m.m_name); }
@ -41,20 +41,34 @@ protected:
virtual ~Worker() { stopWorking(); }
/// Allows changing worker name if work is stopped.
void setName(std::string _n) { if (!isWorking()) m_name = _n; }
/// Starts worker thread; causes startedWorking() to be called.
void startWorking();
/// Stop worker thread; causes call to stopWorking().
void stopWorking();
/// Returns if worker thread is present.
bool isWorking() const { Guard l(x_work); return !!m_work; }
/// Called after thread is started from startWorking().
virtual void startedWorking() {}
/// Called continuously following sleep for m_idleWaitMs.
virtual void doWork() = 0;
/// Called when is to be stopped, just prior to thread being joined.
virtual void doneWorking() {}
private:
std::string m_name;
unsigned m_idleWaitMs;
mutable Mutex x_work; ///< Lock for the network existance.
std::unique_ptr<std::thread> m_work; ///< The network thread.
bool m_stop = false;
std::string m_name;
};
}

18
libethcore/BlockInfo.cpp

@ -58,9 +58,9 @@ h256 BlockInfo::headerHashWithoutNonce() const
void BlockInfo::streamRLP(RLPStream& _s, bool _nonce) const
{
_s.appendList(_nonce ? 15 : 14)
_s.appendList(_nonce ? 14 : 13)
<< parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << receiptsRoot << logBloom
<< difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData;
<< difficulty << number << gasLimit << gasUsed << timestamp << extraData;
if (_nonce)
_s << nonce;
}
@ -86,12 +86,11 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce)
logBloom = _header[field = 6].toHash<h512>();
difficulty = _header[field = 7].toInt<u256>();
number = _header[field = 8].toInt<u256>();
minGasPrice = _header[field = 9].toInt<u256>();
gasLimit = _header[field = 10].toInt<u256>();
gasUsed = _header[field = 11].toInt<u256>();
timestamp = _header[field = 12].toInt<u256>();
extraData = _header[field = 13].toBytes();
nonce = _header[field = 14].toHash<h256>();
gasLimit = _header[field = 9].toInt<u256>();
gasUsed = _header[field = 10].toInt<u256>();
timestamp = _header[field = 11].toInt<u256>();
extraData = _header[field = 12].toBytes();
nonce = _header[field = 13].toHash<h256>();
}
catch (Exception const& _e)
@ -147,9 +146,6 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
if (transactionsRoot != t.root())
BOOST_THROW_EXCEPTION(InvalidTransactionsHash(t.root(), transactionsRoot));
if (minGasPrice > mgp)
BOOST_THROW_EXCEPTION(InvalidMinGasPrice(minGasPrice, mgp));
if (sha3Uncles != sha3(root[2].data()))
BOOST_THROW_EXCEPTION(InvalidUnclesHash());
}

4
libethcore/BlockInfo.h

@ -66,7 +66,6 @@ public:
h512 logBloom; // TODO LogBloom - get include
u256 difficulty;
u256 number;
u256 minGasPrice;
u256 gasLimit;
u256 gasUsed;
u256 timestamp;
@ -95,7 +94,6 @@ public:
logBloom == _cmp.logBloom &&
difficulty == _cmp.difficulty &&
number == _cmp.number &&
minGasPrice == _cmp.minGasPrice &&
gasLimit == _cmp.gasLimit &&
gasUsed == _cmp.gasUsed &&
timestamp == _cmp.timestamp &&
@ -122,7 +120,7 @@ public:
inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi)
{
_out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " <<
_bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " <<
_bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.gasLimit << " " <<
_bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce;
return _out;
}

4
libethcore/CommonEth.cpp

@ -33,8 +33,8 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 43;
const unsigned c_databaseVersion = 4;
const unsigned c_protocolVersion = 46;
const unsigned c_databaseVersion = 5;
static const vector<pair<u256, string>> g_units =
{

21
libethereum/BlockChain.cpp

@ -101,9 +101,8 @@ bytes BlockChain::createGenesisBlock()
stateRoot = state.root();
}
block.appendList(15)
// TODO: maybe make logbloom correct?
<< h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendList(14)
<< h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
@ -169,8 +168,6 @@ void BlockChain::close()
delete m_db;
m_lastBlockHash = m_genesisHash;
m_details.clear();
m_blooms.clear();
m_traces.clear();
m_cache.clear();
}
@ -307,14 +304,10 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
State s(bi.coinbaseAddress, _db);
auto tdIncrease = s.enactOn(&_block, bi, *this);
auto b = s.oldBloom();
BlockBlooms bb;
BlockTraces bt;
BlockLogBlooms blb;
BlockReceipts br;
for (unsigned i = 0; i < s.pending().size(); ++i)
{
bb.blooms.push_back(s.changesFromPending(i).bloom());
bt.traces.push_back(s.changesFromPending(i));
blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i));
}
@ -330,14 +323,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}, b);
m_details[bi.parentHash].children.push_back(newHash);
}
{
WriteGuard l(x_blooms);
m_blooms[newHash] = bb;
}
{
WriteGuard l(x_traces);
m_traces[newHash] = bt;
}
{
WriteGuard l(x_logBlooms);
m_logBlooms[newHash] = blb;
@ -349,8 +334,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
m_extrasDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)dev::ref(m_details[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 1), (ldb::Slice)dev::ref(m_blooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 2), (ldb::Slice)dev::ref(m_traces[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 3), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 4), (ldb::Slice)dev::ref(m_receipts[newHash].rlp()));
m_db->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block));

12
libethereum/BlockChain.h

@ -101,14 +101,6 @@ public:
BlockDetails details(h256 _hash) const { return queryExtras<BlockDetails, 0>(_hash, m_details, x_details, NullBlockDetails); }
BlockDetails details() const { return details(currentHash()); }
/// Get the transactions' bloom filters of a block (or the most recent mined if none given). Thread-safe.
BlockBlooms blooms(h256 _hash) const { return queryExtras<BlockBlooms, 1>(_hash, m_blooms, x_blooms, NullBlockBlooms); }
BlockBlooms blooms() const { return blooms(currentHash()); }
/// Get the transactions' trace manifests of a block (or the most recent mined if none given). Thread-safe.
BlockTraces traces(h256 _hash) const { return queryExtras<BlockTraces, 2>(_hash, m_traces, x_traces, NullBlockTraces); }
BlockTraces traces() const { return traces(currentHash()); }
/// Get the transactions' log blooms of a block (or the most recent mined if none given). Thread-safe.
BlockLogBlooms logBlooms(h256 _hash) const { return queryExtras<BlockLogBlooms, 3>(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); }
BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); }
@ -193,10 +185,6 @@ private:
/// The caches of the disk DB and their locks.
mutable boost::shared_mutex x_details;
mutable BlockDetailsHash m_details;
mutable boost::shared_mutex x_blooms;
mutable BlockBloomsHash m_blooms;
mutable boost::shared_mutex x_traces;
mutable BlockTracesHash m_traces;
mutable boost::shared_mutex x_logBlooms;
mutable BlockLogBloomsHash m_logBlooms;
mutable boost::shared_mutex x_receipts;

22
libethereum/BlockDetails.h

@ -54,24 +54,6 @@ struct BlockDetails
h256 bloom;
};
struct BlockBlooms
{
BlockBlooms() {}
BlockBlooms(RLP const& _r) { blooms = _r.toVector<h256>(); }
bytes rlp() const { RLPStream s; s << blooms; return s.out(); }
h256s blooms;
};
struct BlockTraces
{
BlockTraces() {}
BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); }
bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamRLP(s); return s.out(); }
Manifests traces;
};
struct BlockLogBlooms
{
BlockLogBlooms() {}
@ -91,14 +73,10 @@ struct BlockReceipts
};
typedef std::map<h256, BlockDetails> BlockDetailsHash;
typedef std::map<h256, BlockBlooms> BlockBloomsHash;
typedef std::map<h256, BlockTraces> BlockTracesHash;
typedef std::map<h256, BlockLogBlooms> BlockLogBloomsHash;
typedef std::map<h256, BlockReceipts> BlockReceiptsHash;
static const BlockDetails NullBlockDetails;
static const BlockBlooms NullBlockBlooms;
static const BlockTraces NullBlockTraces;
static const BlockLogBlooms NullBlockLogBlooms;
static const BlockReceipts NullBlockReceipts;

1
libethereum/EthereumHost.h

@ -51,6 +51,7 @@ class BlockQueue;
/**
* @brief The EthereumHost class
* @warning None of this is thread-safe. You have been warned.
* @doWork Syncs to peers and sends new blocks and transactions.
*/
class EthereumHost: public p2p::HostCapability<EthereumPeer>, Worker
{

14
libethereum/Executive.cpp

@ -22,6 +22,7 @@
#include <boost/timer.hpp>
#include <libdevcore/CommonIO.h>
#include <libevm/VM.h>
#include "Interface.h"
#include "Executive.h"
#include "State.h"
#include "ExtVM.h"
@ -58,15 +59,8 @@ bool Executive::setup(bytesConstRef _rlp)
BOOST_THROW_EXCEPTION(InvalidNonce(nonceReq, m_t.nonce()));
}
// Don't like transactions whose gas price is too low. NOTE: this won't stay here forever - it's just until we get a proper gas price discovery protocol going.
if (m_t.gasPrice() < m_s.m_currentBlock.minGasPrice)
{
clog(StateDetail) << "Offered gas-price is too low: Require >" << m_s.m_currentBlock.minGasPrice << " Got" << m_t.gasPrice();
BOOST_THROW_EXCEPTION(GasPriceTooLow());
}
// Check gas cost is enough.
u256 gasCost = m_t.data().size() * c_txDataGas + c_txGas;
auto gasCost = Interface::txGas(m_t.data());
if (m_t.gas() < gasCost)
{
@ -106,9 +100,9 @@ bool Executive::setup(bytesConstRef _rlp)
}
if (m_t.isCreation())
return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - gasCost, &m_t.data(), m_sender);
return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasCost, &m_t.data(), m_sender);
else
return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - gasCost, m_sender);
return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_sender);
}
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)

2
libethereum/Interface.h

@ -128,7 +128,7 @@ public:
virtual Addresses addresses(int _block) const = 0;
/// Get the fee associated for a transaction with the given data.
static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; }
template <class T> static bigint txGas(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; }
/// Get the remaining gas limit in this block.
virtual u256 gasLimitRemaining() const = 0;

2
libethereum/MessageFilter.cpp

@ -198,7 +198,7 @@ LogEntries LogFilter::matches(TransactionReceipt const& _m) const
if (!m_addresses.empty() && !m_addresses.count(e.address))
continue;
for (auto const& t: m_topics)
if (!e.topics.count(t))
if (!std::count(e.topics.begin(), e.topics.end(), t))
continue;
ret.push_back(e);
}

9
libethereum/State.cpp

@ -51,14 +51,16 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out)
h256 s;
} in;
h256 ret;
memcpy(&in, _in.data(), min(_in.size(), sizeof(in)));
memset(_out.data(), 0, _out.size());
if ((u256)in.v > 28)
return;
SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)};
if (!sig.isValid() || in.v > 28)
if (!sig.isValid())
return;
h256 ret;
byte pubkey[65];
int pubkeylen = 65;
secp256k1_start();
@ -499,7 +501,6 @@ void State::resetCurrent()
m_currentBlock.timestamp = time(0);
m_currentBlock.transactionsRoot = h256();
m_currentBlock.sha3Uncles = h256();
m_currentBlock.minGasPrice = 10 * szabo;
m_currentBlock.populateFromParent(m_previousBlock);
// Update timestamp according to clock.

11
libethereum/Utility.h

@ -29,6 +29,17 @@ namespace dev
namespace eth
{
/**
* Takes a user-authorable string with several whitespace delimited arguments and builds a byte array
* from it. Arguments can be hex data/numerals, decimal numbers or ASCII strings. Literals are padded
* to 32 bytes if prefixed by a '@' (or not prefixed at all), and tightly packed if prefixed by a '$'.
* Currency multipliers can be provided.
*
* Example:
* @code
* parseData("$42 0x42 $\"Hello\""); // == bytes(1, 0x2a) + bytes(31, 0) + bytes(1, 0x42) + asBytes("Hello");
* @endcode
*/
bytes parseData(std::string const& _args);
}

6
libevm/ExtVMFace.h

@ -49,8 +49,8 @@ using LogBloom = h512;
struct LogEntry
{
LogEntry() {}
LogEntry(RLP const& _r) { address = (Address)_r[0]; topics = (h256Set)_r[1]; data = _r[2].toBytes(); }
LogEntry(Address const& _address, h256s const& _ts, bytes&& _d): address(_address), topics(toSet(_ts)), data(std::move(_d)) {}
LogEntry(RLP const& _r) { address = (Address)_r[0]; topics = (h256s)_r[1]; data = _r[2].toBytes(); }
LogEntry(Address const& _address, h256s const& _ts, bytes&& _d): address(_address), topics(_ts), data(std::move(_d)) {}
void streamRLP(RLPStream& _s) const { _s.appendList(3) << address << topics << data; }
@ -64,7 +64,7 @@ struct LogEntry
}
Address address;
h256Set topics;
h256s topics;
bytes data;
};

6
libevm/FeeStructure.cpp

@ -34,9 +34,13 @@ u256 const dev::eth::c_sstoreResetGas = 100;
u256 const dev::eth::c_sstoreRefundGas = 100;
u256 const dev::eth::c_createGas = 100;
u256 const dev::eth::c_callGas = 20;
u256 const dev::eth::c_expGas = 1;
u256 const dev::eth::c_expByteGas = 1;
u256 const dev::eth::c_memoryGas = 1;
u256 const dev::eth::c_txDataGas = 5;
u256 const dev::eth::c_txDataZeroGas = 1;
u256 const dev::eth::c_txDataNonZeroGas = 5;
u256 const dev::eth::c_txGas = 500;
u256 const dev::eth::c_logGas = 32;
u256 const dev::eth::c_logDataGas = 1;
u256 const dev::eth::c_logTopicGas = 32;
u256 const dev::eth::c_copyGas = 1;

6
libevm/FeeStructure.h

@ -37,12 +37,16 @@ extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeron
extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction.
extern u256 const c_callGas; ///< Once per CALL operation & message call transaction.
extern u256 const c_expGas; ///< Once per EXP instuction.
extern u256 const c_expByteGas; ///< Times ceil(log256(exponent)) for the EXP instruction.
extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
extern u256 const c_txDataGas; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions.
extern u256 const c_txDataZeroGas; ///< Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
extern u256 const c_txDataNonZeroGas; ///< Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
extern u256 const c_txGas; ///< Per transaction. NOTE: Not payable on data of calls between transactions.
extern u256 const c_logGas; ///< Per LOG* operation.
extern u256 const c_logDataGas; ///< Per byte in a LOG* operation's data.
extern u256 const c_logTopicGas; ///< Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
extern u256 const c_copyGas; ///< Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
}
}

30
libevm/VM.h

@ -53,9 +53,6 @@ inline Address asAddress(u256 _item)
inline u256 fromAddress(Address _a)
{
return (u160)_a;
// h256 ret;
// memcpy(&ret, &_a, sizeof(_a));
// return ret;
}
/**
@ -131,6 +128,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
// FEES...
bigint runGas = c_stepGas;
bigint newTempSize = m_temp.size();
bigint copySize = 0;
auto onOperation = [&]()
{
@ -172,15 +170,15 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
// These all operate on memory and therefore potentially expand it:
case Instruction::MSTORE:
require(2);
newTempSize = m_stack.back() + 32;
newTempSize = (bigint)m_stack.back() + 32;
break;
case Instruction::MSTORE8:
require(2);
newTempSize = m_stack.back() + 1;
newTempSize = (bigint)m_stack.back() + 1;
break;
case Instruction::MLOAD:
require(1);
newTempSize = m_stack.back() + 32;
newTempSize = (bigint)m_stack.back() + 32;
break;
case Instruction::RETURN:
require(2);
@ -193,14 +191,17 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::CALLDATACOPY:
require(3);
copySize = m_stack[m_stack.size() - 3];
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]);
break;
case Instruction::CODECOPY:
require(3);
copySize = m_stack[m_stack.size() - 3];
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]);
break;
case Instruction::EXTCODECOPY:
require(4);
copySize = m_stack[m_stack.size() - 4];
newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]);
break;
@ -232,10 +233,17 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::CREATE:
{
require(3);
auto inOff = m_stack[m_stack.size() - 2];
auto inSize = m_stack[m_stack.size() - 3];
newTempSize = inOff + inSize;
runGas = c_createGas;
u256 inOff = m_stack[m_stack.size() - 2];
u256 inSize = m_stack[m_stack.size() - 3];
newTempSize = (bigint)inOff + inSize;
runGas = c_createGas;
break;
}
case Instruction::EXP:
{
require(2);
auto expon = m_stack[m_stack.size() - 2];
runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8));
break;
}
@ -304,7 +312,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::SDIV:
case Instruction::MOD:
case Instruction::SMOD:
case Instruction::EXP:
case Instruction::LT:
case Instruction::GT:
case Instruction::SLT:
@ -365,6 +372,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
newTempSize = (newTempSize + 31) / 32 * 32;
if (newTempSize > m_temp.size())
runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32;
runGas += c_copyGas * (copySize + 31) / 32;
onOperation();
// if (_onOp)

117
libevmcore/Assembly.cpp

@ -27,6 +27,32 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
{
switch (m_type)
{
case Operation:
case Tag: // 1 byte for the JUMPDEST
return 1;
case PushString:
return 33;
case Push:
return 1 + max<unsigned>(1, dev::bytesRequired(m_data));
case PushSubSize:
return 4; // worst case: a 16MB program
case PushTag:
case PushData:
case PushSub:
return 1 + _addressLength;
case NoOptimizeBegin:
case NoOptimizeEnd:
return 0;
default:
break;
}
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
int AssemblyItem::deposit() const
{
switch (m_type)
@ -51,32 +77,7 @@ unsigned Assembly::bytesRequired() const
ret += i.second.size();
for (AssemblyItem const& i: m_items)
switch (i.m_type)
{
case Operation:
case Tag: // 1 byte for the JUMPDEST
ret++;
break;
case PushString:
ret += 33;
break;
case Push:
ret += 1 + max<unsigned>(1, dev::bytesRequired(i.m_data));
break;
case PushSubSize:
ret += 4; // worst case: a 16MB program
break;
case PushTag:
case PushData:
case PushSub:
ret += 1 + br;
break;
case NoOptimizeBegin:
case NoOptimizeEnd:
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
ret += i.bytesRequired(br);
if (dev::bytesRequired(ret) <= br)
return ret;
}
@ -243,6 +244,18 @@ inline bool popCountIncreased(AssemblyItemsConstRef _pre, AssemblyItems const& _
return count_if(begin(_post), end(_post), isPop) > count_if(begin(_pre), end(_pre), isPop);
}
//@todo this has to move to a special optimizer class soon
template<class Iterator>
unsigned bytesRequiredBySlice(Iterator _begin, Iterator _end)
{
// this is only used in the optimizer, so we can provide a guess for the address length
unsigned addressLength = 4;
unsigned size = 0;
for (; _begin != _end; ++_begin)
size += _begin->bytesRequired(addressLength);
return size;
}
struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; };
#define copt dev::LogOutputStream<OptimiserChannel, true>()
@ -258,7 +271,7 @@ Assembly& Assembly::optimise(bool _enable)
u256 mask = (u256(1) << testBit) - 1;
return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask;
};
map<Instruction, function<u256(u256, u256)>> c_simple =
map<Instruction, function<u256(u256, u256)>> const c_simple =
{
{ Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} },
{ Instruction::DIV, [](u256 a, u256 b)->u256{return a / b;} },
@ -273,7 +286,7 @@ Assembly& Assembly::optimise(bool _enable)
{ Instruction::SGT, [](u256 a, u256 b)->u256{return u2s(a) > u2s(b) ? 1 : 0;} },
{ Instruction::EQ, [](u256 a, u256 b)->u256{return a == b ? 1 : 0;} },
};
map<Instruction, function<u256(u256, u256)>> c_associative =
map<Instruction, function<u256(u256, u256)>> const c_associative =
{
{ Instruction::ADD, [](u256 a, u256 b)->u256{return a + b;} },
{ Instruction::MUL, [](u256 a, u256 b)->u256{return a * b;} },
@ -281,6 +294,8 @@ Assembly& Assembly::optimise(bool _enable)
{ Instruction::OR, [](u256 a, u256 b)->u256{return a | b;} },
{ Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} },
};
std::vector<pair<AssemblyItem, u256>> const c_identities =
{ { Instruction::ADD, 0}, { Instruction::MUL, 1}, { Instruction::MOD, 0}, { Instruction::OR, 0}, { Instruction::XOR, 0} };
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules =
{
{ { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
@ -299,8 +314,11 @@ Assembly& Assembly::optimise(bool _enable)
rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } });
rules.push_back({ { Push, i.first, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[2].data(), m[0].data()), i.first }; } });
}
for (auto const& i: c_identities)
rules.push_back({{Push, i.first}, [&](AssemblyItemsConstRef m) -> AssemblyItems
{ return m[0].data() == i.second ? AssemblyItems() : m.toVector(); }});
// jump to next instruction
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [&](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }});
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }});
// pop optimization, do not compute values that are popped again anyway
rules.push_back({ { AssemblyItem(UndefinedItem), Instruction::POP }, [](AssemblyItemsConstRef m) -> AssemblyItems
@ -315,6 +333,29 @@ Assembly& Assembly::optimise(bool _enable)
return m.toVector();
return AssemblyItems(info.args, Instruction::POP);
} });
// compute constants close to powers of two by expressions
auto computeConstants = [](AssemblyItemsConstRef m) -> AssemblyItems
{
u256 const& c = m[0].data();
unsigned const minBits = 4 * 8;
if (c < (bigint(1) << minBits))
return m.toVector(); // we need at least "PUSH1 <bits> PUSH1 <2> EXP"
if (c == u256(-1))
return {u256(0), Instruction::NOT};
for (unsigned bits = minBits; bits < 256; ++bits)
{
bigint const diff = c - (bigint(1) << bits);
if (abs(diff) > 0xff)
continue;
AssemblyItems powerOfTwo{u256(bits), u256(2), Instruction::EXP};
if (diff == 0)
return powerOfTwo;
return AssemblyItems{u256(abs(diff))} + powerOfTwo +
AssemblyItems{diff > 0 ? Instruction::ADD : Instruction::SUB};
}
return m.toVector();
};
rules.push_back({{Push}, computeConstants});
copt << *this;
@ -336,15 +377,27 @@ Assembly& Assembly::optimise(bool _enable)
if (matches(vr, &r.first))
{
auto rw = r.second(vr);
if (rw.size() < vr.size() || (rw.size() == vr.size() && popCountIncreased(vr, rw)))
unsigned const vrSize = bytesRequiredBySlice(vr.begin(), vr.end());
unsigned const rwSize = bytesRequiredBySlice(rw.begin(), rw.end());
//@todo check the actual size (including constant sizes)
if (rwSize < vrSize || (rwSize == vrSize && popCountIncreased(vr, rw)))
{
copt << vr << "matches" << AssemblyItemsConstRef(&r.first) << "becomes...";
for (unsigned j = 0; j < vr.size(); ++j)
copt << AssemblyItemsConstRef(&rw);
if (rw.size() > vr.size())
{
// create hole in the vector
unsigned sizeIncrease = rw.size() - vr.size();
m_items.resize(m_items.size() + sizeIncrease, AssemblyItem(UndefinedItem));
move_backward(m_items.begin() + i, m_items.end() - sizeIncrease, m_items.end());
}
for (unsigned j = 0; j < max(rw.size(), vr.size()); ++j)
if (j < rw.size())
m_items[i + j] = rw[j];
else
m_items.erase(m_items.begin() + i + rw.size());
copt << AssemblyItemsConstRef(&rw);
count++;
copt << "Now:\n" << m_items;
}

3
libevmcore/Assembly.h

@ -51,6 +51,9 @@ public:
AssemblyItemType type() const { return m_type; }
u256 data() const { return m_data; }
/// @returns an upper bound for the number of bytes required by this item, assuming that
/// the value of a jump tag takes @a _addressLength bytes.
unsigned bytesRequired(unsigned _addressLength) const;
int deposit() const;
bool match(AssemblyItem const& _i) const { return _i.m_type == UndefinedItem || (m_type == _i.m_type && (m_type != Operation || m_data == _i.m_data)); }

2
libevmcore/Instruction.h

@ -168,8 +168,8 @@ enum class Instruction: uint8_t
CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only
RETURN, ///< halt execution returning output data
CALLCODE,
SUICIDE = 0xff ///< halt execution and register account for later deletion
};

14
libp2p/Common.cpp

@ -53,6 +53,20 @@ bool p2p::isPrivateAddress(bi::address const& _addressToCheck)
return false;
}
// Helper function to determine if an address is localhost
bool p2p::isLocalHostAddress(bi::address const& _addressToCheck)
{
// @todo: ivp6 link-local adresses (macos), ex: fe80::1%lo0
static const set<bi::address> c_rejectAddresses = {
{bi::address_v4::from_string("127.0.0.1")},
{bi::address_v4::from_string("0.0.0.0")},
{bi::address_v6::from_string("::1")},
{bi::address_v6::from_string("::")}
};
return find(c_rejectAddresses.begin(), c_rejectAddresses.end(), _addressToCheck) != c_rejectAddresses.end();
}
std::string p2p::reasonOf(DisconnectReason _r)
{
switch (_r)

1
libp2p/Common.h

@ -47,6 +47,7 @@ namespace p2p
using NodeId = h512;
bool isPrivateAddress(bi::address const& _addressToCheck);
bool isLocalHostAddress(bi::address const& _addressToCheck);
class UPnP;
class Capability;

658
libp2p/Host.cpp

@ -46,103 +46,196 @@ using namespace std;
using namespace dev;
using namespace dev::p2p;
// Addresses we will skip during network interface discovery
// Use a vector as the list is small
// Why this and not names?
// Under MacOSX loopback (127.0.0.1) can be named lo0 and br0 are bridges (0.0.0.0)
static const set<bi::address> c_rejectAddresses = {
{bi::address_v4::from_string("127.0.0.1")},
{bi::address_v6::from_string("::1")},
{bi::address_v4::from_string("0.0.0.0")},
{bi::address_v6::from_string("::")}
};
Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool _start):
Worker("p2p"),
m_clientVersion(_clientVersion),
m_netPrefs(_n),
m_ioService(new ba::io_service),
m_acceptor(new bi::tcp::acceptor(*m_ioService)),
m_socket(new bi::tcp::socket(*m_ioService)),
m_key(KeyPair::create())
std::vector<bi::address> Host::getInterfaceAddresses()
{
populateAddresses();
clog(NetNote) << "Id:" << id().abridged();
if (_start)
start();
}
Host::~Host()
{
quit();
std::vector<bi::address> addresses;
#ifdef _WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
BOOST_THROW_EXCEPTION(NoNetworking());
char ac[80];
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR)
{
clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name.";
WSACleanup();
BOOST_THROW_EXCEPTION(NoNetworking());
}
struct hostent* phe = gethostbyname(ac);
if (phe == 0)
{
clog(NetWarn) << "Bad host lookup.";
WSACleanup();
BOOST_THROW_EXCEPTION(NoNetworking());
}
for (int i = 0; phe->h_addr_list[i] != 0; ++i)
{
struct in_addr addr;
memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
char *addrStr = inet_ntoa(addr);
bi::address address(bi::address::from_string(addrStr));
if (!isLocalHostAddress(address))
addresses.push_back(address.to_v4());
}
WSACleanup();
#else
ifaddrs* ifaddr;
if (getifaddrs(&ifaddr) == -1)
BOOST_THROW_EXCEPTION(NoNetworking());
for (auto ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr || string(ifa->ifa_name) == "lo0")
continue;
if (ifa->ifa_addr->sa_family == AF_INET)
{
in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr));
if (!isLocalHostAddress(address))
addresses.push_back(address);
}
else if (ifa->ifa_addr->sa_family == AF_INET6)
{
sockaddr_in6* sockaddr = ((struct sockaddr_in6 *)ifa->ifa_addr);
in6_addr addr = sockaddr->sin6_addr;
boost::asio::ip::address_v6::bytes_type bytes;
memcpy(&bytes[0], addr.s6_addr, 16);
boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id);
if (!isLocalHostAddress(address))
addresses.push_back(address);
}
}
if (ifaddr!=NULL)
freeifaddrs(ifaddr);
#endif
return std::move(addresses);
}
void Host::start()
int Host::listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort)
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
return;
if (isWorking())
stop();
int retport = -1;
for (unsigned i = 0; i < 2; ++i)
{
bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : m_netPrefs.listenPort);
// try to connect w/listenPort, else attempt net-allocated port
bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _listenPort);
try
{
m_acceptor->open(endpoint.protocol());
m_acceptor->set_option(ba::socket_base::reuse_address(true));
m_acceptor->bind(endpoint);
m_acceptor->listen();
m_listenPort = i ? m_acceptor->local_endpoint().port() : m_netPrefs.listenPort;
_acceptor->open(endpoint.protocol());
_acceptor->set_option(ba::socket_base::reuse_address(true));
_acceptor->bind(endpoint);
_acceptor->listen();
retport = _acceptor->local_endpoint().port();
break;
}
catch (...)
{
if (i)
{
// both attempts failed
cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information();
return;
}
m_acceptor->close();
// first attempt failed
_acceptor->close();
continue;
}
}
return retport;
}
for (auto const& h: m_capabilities)
h.second->onStarting();
bi::tcp::endpoint Host::traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr)
{
asserts(_listenPort != 0);
UPnP* upnp = nullptr;
try
{
upnp = new UPnP;
}
// let m_upnp continue as null - we handle it properly.
catch (NoUPnPDevice) {}
bi::tcp::endpoint upnpep;
if (upnp && upnp->isValid())
{
bi::address paddr;
int extPort = 0;
for (auto const& addr: _ifAddresses)
if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort)))
{
paddr = addr;
break;
}
startWorking();
auto eip = upnp->externalIP();
bi::address eipaddr(bi::address::from_string(eip));
if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr))
{
clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << ".";
clog(NetNote) << "External addr:" << eip;
o_upnpifaddr = paddr;
upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort);
}
else
clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place).";
if (upnp)
delete upnp;
}
return upnpep;
}
void Host::stop()
Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool _start):
Worker("p2p", 0),
m_clientVersion(_clientVersion),
m_netPrefs(_n),
m_ifAddresses(getInterfaceAddresses()),
m_ioService(new ba::io_service),
m_acceptor(new bi::tcp::acceptor(*m_ioService)),
m_socket(new bi::tcp::socket(*m_ioService)),
m_key(KeyPair::create())
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
return;
for (auto const& h: m_capabilities)
h.second->onStopping();
for (auto address: m_ifAddresses)
if (address.is_v4())
clog(NetNote) << "IP Address: " << address << " = " << (isPrivateAddress(address) ? "[LOCAL]" : "[PEER]");
clog(NetNote) << "Id:" << id().abridged();
if (_start)
start();
}
stopWorking();
Host::~Host()
{
quit();
}
if (m_acceptor->is_open())
{
if (m_accepting)
m_acceptor->cancel();
m_acceptor->close();
m_accepting = false;
}
if (m_socket->is_open())
m_socket->close();
disconnectPeers();
void Host::start()
{
startWorking();
}
if (!!m_ioService)
void Host::stop()
{
{
m_ioService->stop();
m_ioService->reset();
// prevent m_run from being set to false at same time as set to true by start()
lock_guard<mutex> l(x_runtimer);
// once m_run is false the scheduler will shutdown network and stopWorking()
m_run = false;
}
// we know shutdown is complete when m_timer is reset
while (m_timer)
this_thread::sleep_for(chrono::milliseconds(50));
stopWorking();
}
void Host::quit()
@ -150,7 +243,8 @@ void Host::quit()
// called to force io_service to kill any remaining tasks it might have -
// such tasks may involve socket reads from Capabilities that maintain references
// to resources we're about to free.
stop();
if (isWorking())
stop();
m_acceptor.reset();
m_socket.reset();
m_ioService.reset();
@ -183,33 +277,6 @@ void Host::registerPeer(std::shared_ptr<Session> _s, CapDescs const& _caps)
}
}
void Host::disconnectPeers()
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
return;
for (unsigned n = 0;; n = 0)
{
{
RecursiveGuard l(x_peers);
for (auto i: m_peers)
if (auto p = i.second.lock())
{
p->disconnect(ClientQuit);
n++;
}
}
if (!n)
break;
m_ioService->poll();
this_thread::sleep_for(chrono::milliseconds(100));
}
delete m_upnp;
m_upnp = nullptr;
}
void Host::seal(bytes& _b)
{
_b[0] = 0x22;
@ -223,165 +290,6 @@ void Host::seal(bytes& _b)
_b[7] = len & 0xff;
}
void Host::determinePublic(string const& _publicAddress, bool _upnp)
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
return;
if (_upnp)
try
{
m_upnp = new UPnP;
}
catch (NoUPnPDevice) {} // let m_upnp continue as null - we handle it properly.
bi::tcp::resolver r(*m_ioService);
if (m_upnp && m_upnp->isValid() && m_peerAddresses.size())
{
clog(NetNote) << "External addr:" << m_upnp->externalIP();
int p;
for (auto const& addr : m_peerAddresses)
if ((p = m_upnp->addRedirect(addr.to_string().c_str(), m_listenPort)))
break;
if (p)
clog(NetNote) << "Punched through NAT and mapped local port" << m_listenPort << "onto external port" << p << ".";
else
{
// couldn't map
clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place). Assuming" << m_listenPort << "is local & external port.";
p = m_listenPort;
}
auto eip = m_upnp->externalIP();
if (eip == string("0.0.0.0") && _publicAddress.empty())
m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p);
else
{
bi::address adr = adr = bi::address::from_string(eip);
try
{
adr = bi::address::from_string(_publicAddress);
}
catch (...) {}
m_public = bi::tcp::endpoint(adr, (unsigned short)p);
m_addresses.push_back(m_public.address());
}
}
else
{
// No UPnP - fallback on given public address or, if empty, the assumed peer address.
bi::address adr = m_peerAddresses.size() ? m_peerAddresses[0] : bi::address();
try
{
adr = bi::address::from_string(_publicAddress);
}
catch (...) {}
m_public = bi::tcp::endpoint(adr, m_listenPort);
m_addresses.push_back(adr);
}
}
void Host::populateAddresses()
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
return;
#ifdef _WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
BOOST_THROW_EXCEPTION(NoNetworking());
char ac[80];
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR)
{
clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name.";
WSACleanup();
BOOST_THROW_EXCEPTION(NoNetworking());
}
struct hostent* phe = gethostbyname(ac);
if (phe == 0)
{
clog(NetWarn) << "Bad host lookup.";
WSACleanup();
BOOST_THROW_EXCEPTION(NoNetworking());
}
for (int i = 0; phe->h_addr_list[i] != 0; ++i)
{
struct in_addr addr;
memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
char *addrStr = inet_ntoa(addr);
bi::address ad(bi::address::from_string(addrStr));
m_addresses.push_back(ad.to_v4());
bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end();
if (!isLocal)
m_peerAddresses.push_back(ad.to_v4());
clog(NetNote) << "Address: " << ac << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]");
}
WSACleanup();
#else
ifaddrs* ifaddr;
if (getifaddrs(&ifaddr) == -1)
BOOST_THROW_EXCEPTION(NoNetworking());
bi::tcp::resolver r(*m_ioService);
for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr)
continue;
if (ifa->ifa_addr->sa_family == AF_INET)
{
char host[NI_MAXHOST];
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST))
continue;
try
{
auto it = r.resolve({host, "30303"});
bi::tcp::endpoint ep = it->endpoint();
bi::address ad = ep.address();
m_addresses.push_back(ad.to_v4());
bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end();
if (!isLocal)
m_peerAddresses.push_back(ad.to_v4());
clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]");
}
catch (...)
{
clog(NetNote) << "Couldn't resolve: " << host;
}
}
else if (ifa->ifa_addr->sa_family == AF_INET6)
{
char host[NI_MAXHOST];
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST))
continue;
try
{
auto it = r.resolve({host, "30303"});
bi::tcp::endpoint ep = it->endpoint();
bi::address ad = ep.address();
m_addresses.push_back(ad.to_v6());
bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end();
if (!isLocal)
m_peerAddresses.push_back(ad);
clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]");
}
catch (...)
{
clog(NetNote) << "Couldn't resolve: " << host;
}
}
}
freeifaddrs(ifaddr);
#endif
}
shared_ptr<Node> Host::noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId)
{
RecursiveGuard l(x_peers);
@ -459,10 +367,73 @@ Nodes Host::potentialPeers(RangeMask<unsigned> const& _known)
return ret;
}
void Host::determinePublic(string const& _publicAddress, bool _upnp)
{
m_peerAddresses.clear();
// no point continuing if there are no interface addresses or valid listen port
if (!m_ifAddresses.size() || m_listenPort < 1)
return;
// populate interfaces we'll listen on (eth listens on all interfaces); ignores local
for (auto addr: m_ifAddresses)
if ((m_netPrefs.localNetworking || !isPrivateAddress(addr)) && !isLocalHostAddress(addr))
m_peerAddresses.insert(addr);
// if user supplied address is a public address then we use it
// if user supplied address is private, and localnetworking is enabled, we use it
bi::address reqpublicaddr(bi::address(_publicAddress.empty() ? bi::address() : bi::address::from_string(_publicAddress)));
bi::tcp::endpoint reqpublic(reqpublicaddr, m_listenPort);
bool isprivate = isPrivateAddress(reqpublicaddr);
bool ispublic = !isprivate && !isLocalHostAddress(reqpublicaddr);
if (!reqpublicaddr.is_unspecified() && (ispublic || (isprivate && m_netPrefs.localNetworking)))
{
if (!m_peerAddresses.count(reqpublicaddr))
m_peerAddresses.insert(reqpublicaddr);
m_public = reqpublic;
return;
}
// if address wasn't provided, then use first public ipv4 address found
for (auto addr: m_peerAddresses)
if (addr.is_v4() && !isPrivateAddress(addr))
{
m_public = bi::tcp::endpoint(*m_peerAddresses.begin(), m_listenPort);
return;
}
// or find address via upnp
if (_upnp)
{
bi::address upnpifaddr;
bi::tcp::endpoint upnpep = traverseNAT(m_ifAddresses, m_listenPort, upnpifaddr);
if (!upnpep.address().is_unspecified() && !upnpifaddr.is_unspecified())
{
if (!m_peerAddresses.count(upnpep.address()))
m_peerAddresses.insert(upnpep.address());
m_public = upnpep;
return;
}
}
// or if no address provided, use private ipv4 address if local networking is enabled
if (reqpublicaddr.is_unspecified())
if (m_netPrefs.localNetworking)
for (auto addr: m_peerAddresses)
if (addr.is_v4() && isPrivateAddress(addr))
{
m_public = bi::tcp::endpoint(addr, m_listenPort);
return;
}
// otherwise address is unspecified
m_public = bi::tcp::endpoint(bi::address(), m_listenPort);
}
void Host::ensureAccepting()
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
// return if there's no io-server (quit called) or we're not listening
if (!m_ioService || m_listenPort < 1)
return;
if (!m_accepting)
@ -654,12 +625,9 @@ void Host::growPeers()
return;
}
else
{
ensureAccepting();
for (auto const& i: m_peers)
if (auto p = i.second.lock())
p->ensureNodesRequested();
}
}
}
@ -717,46 +685,154 @@ PeerInfos Host::peers(bool _updatePing) const
ret.push_back(j->m_info);
return ret;
}
void Host::run(boost::system::error_code const& error)
{
static unsigned s_lasttick = 0;
s_lasttick += c_timerInterval;
if (error || !m_ioService)
{
// timer died or io service went away, so stop here
m_timer.reset();
return;
}
// network running
if (m_run)
{
if (s_lasttick >= c_timerInterval * 50)
{
growPeers();
prunePeers();
s_lasttick = 0;
}
if (m_hadNewNodes)
{
for (auto p: m_peers)
if (auto pp = p.second.lock())
pp->serviceNodesRequest();
m_hadNewNodes = false;
}
if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s.
{
for (auto p: m_peers)
if (auto pp = p.second.lock())
if (chrono::steady_clock::now() - pp->m_lastReceived > chrono::seconds(60))
pp->disconnect(PingTimeout);
pingAll();
}
auto runcb = [this](boost::system::error_code const& error) -> void { run(error); };
m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval));
m_timer->async_wait(runcb);
return;
}
// network stopping
if (!m_run)
{
// close acceptor
if (m_acceptor->is_open())
{
if (m_accepting)
m_acceptor->cancel();
m_acceptor->close();
m_accepting = false;
}
// stop capabilities (eth: stops syncing or block/tx broadcast)
for (auto const& h: m_capabilities)
h.second->onStopping();
// disconnect peers
for (unsigned n = 0;; n = 0)
{
{
RecursiveGuard l(x_peers);
for (auto i: m_peers)
if (auto p = i.second.lock())
if (p->isOpen())
{
p->disconnect(ClientQuit);
n++;
}
}
if (!n)
break;
this_thread::sleep_for(chrono::milliseconds(100));
}
if (m_socket->is_open())
m_socket->close();
// m_run is false, so we're stopping; kill timer
s_lasttick = 0;
// causes parent thread's stop() to continue which calls stopWorking()
m_timer.reset();
// stop ioservice (stops blocking worker thread, allowing thread to join)
if (!!m_ioService)
m_ioService->stop();
return;
}
}
void Host::startedWorking()
{
determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp);
ensureAccepting();
if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id()))
noteNode(id(), m_public, Origin::Perfect, false);
clog(NetNote) << "Id:" << id().abridged();
if (!m_timer)
{
// no timer means this is first run and network must be started
// (run once when host worker thread calls startedWorking())
{
// prevent m_run from being set to true at same time as set to false by stop()
// don't release mutex until m_timer is set so in case stop() is called at same
// time, stop will wait on m_timer and graceful network shutdown.
lock_guard<mutex> l(x_runtimer);
// reset io service and create deadline timer
m_timer.reset(new boost::asio::deadline_timer(*m_ioService));
m_run = true;
}
m_ioService->reset();
// try to open acceptor (todo: ipv6)
m_listenPort = listen4(m_acceptor.get(), m_netPrefs.listenPort);
// start capability threads
for (auto const& h: m_capabilities)
h.second->onStarting();
// determine public IP, but only if we're able to listen for connections
// todo: GUI when listen is unavailable in UI
if (m_listenPort)
{
determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp);
ensureAccepting();
}
// if m_public address is valid then add us to node list
// todo: abstract empty() and emplace logic
if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id()))
noteNode(id(), m_public, Origin::Perfect, false);
clog(NetNote) << "Id:" << id().abridged();
}
run(boost::system::error_code());
}
void Host::doWork()
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
// no ioService means we've had quit() called - bomb out - we're not allowed in here.
if (asserts(!!m_ioService))
return;
growPeers();
prunePeers();
if (m_hadNewNodes)
{
for (auto p: m_peers)
if (auto pp = p.second.lock())
pp->serviceNodesRequest();
m_hadNewNodes = false;
}
if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s.
{
for (auto p: m_peers)
if (auto pp = p.second.lock())
if (chrono::steady_clock::now() - pp->m_lastReceived > chrono::seconds(60))
pp->disconnect(PingTimeout);
pingAll();
}
m_ioService->poll();
m_ioService->run();
}
void Host::pingAll()

72
libp2p/Host.h

@ -121,16 +121,24 @@ class Host: public Worker
friend class HostCapabilityFace;
friend struct Node;
/// Static network interface methods
public:
/// @returns public and private interface addresses
static std::vector<bi::address> getInterfaceAddresses();
/// Try to bind and listen on _listenPort, else attempt net-allocated port.
static int listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort);
/// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port.
static bi::tcp::endpoint traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr);
public:
/// Start server, listening for connections on the given port.
Host(std::string const& _clientVersion, NetworkPreferences const& _n = NetworkPreferences(), bool _start = false);
/// Will block on network process events.
virtual ~Host();
/// Closes all peers.
void disconnectPeers();
/// Basic peer network protocol version.
unsigned protocolVersion() const;
@ -147,7 +155,7 @@ public:
void connect(bi::tcp::endpoint const& _ep);
void connect(std::shared_ptr<Node> const& _n);
/// @returns true iff we have the a peer of the given id.
/// @returns true iff we have a peer of the given id.
bool havePeer(NodeId _id) const;
/// Set ideal number of peers.
@ -175,10 +183,16 @@ public:
void setNetworkPreferences(NetworkPreferences const& _p) { auto had = isStarted(); if (had) stop(); m_netPrefs = _p; if (had) start(); }
/// Start network. @threadsafe
void start();
/// Stop network. @threadsafe
void stop();
bool isStarted() const { return isWorking(); }
/// @returns if network is running.
bool isStarted() const { return m_run; }
/// Reset acceptor, socket, and IO service. Called by deallocator. Maybe called by implementation when ordered deallocation is required.
void quit();
NodeId id() const { return m_key.pub(); }
@ -188,38 +202,48 @@ public:
std::shared_ptr<Node> node(NodeId _id) const { if (m_nodes.count(_id)) return m_nodes.at(_id); return std::shared_ptr<Node>(); }
private:
void seal(bytes& _b);
void populateAddresses();
/// Populate m_peerAddresses with available public addresses.
void determinePublic(std::string const& _publicAddress, bool _upnp);
void ensureAccepting();
void seal(bytes& _b);
void growPeers();
void prunePeers();
/// Called by Worker. Not thread-safe; to be called only by worker.
virtual void startedWorking();
/// Called by startedWorking. Not thread-safe; to be called only be worker callback.
void run(boost::system::error_code const& error); ///< Run network. Called serially via ASIO deadline timer. Manages connection state transitions.
/// Conduct I/O, polling, syncing, whatever.
/// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway.
/// This won't touch alter the blockchain.
/// Run network
virtual void doWork();
std::shared_ptr<Node> noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = NodeId());
Nodes potentialPeers(RangeMask<unsigned> const& _known);
bool m_run = false; ///< Whether network is running.
std::mutex x_runtimer; ///< Start/stop mutex.
std::string m_clientVersion; ///< Our version string.
NetworkPreferences m_netPrefs; ///< Network settings.
NetworkPreferences m_netPrefs; ///< Network settings.
/// Interface addresses (private, public)
std::vector<bi::address> m_ifAddresses; ///< Interface addresses.
static const int NetworkStopped = -1; ///< The value meaning we're not actually listening.
int m_listenPort = NetworkStopped; ///< What port are we listening on?
int m_listenPort = -1; ///< What port are we listening on. -1 means binding failed or acceptor hasn't been initialized.
std::unique_ptr<ba::io_service> m_ioService; ///< IOService for network stuff.
std::unique_ptr<bi::tcp::acceptor> m_acceptor; ///< Listening acceptor.
std::unique_ptr<bi::tcp::socket> m_socket; ///< Listening socket.
std::unique_ptr<bi::tcp::acceptor> m_acceptor; ///< Listening acceptor.
std::unique_ptr<bi::tcp::socket> m_socket; ///< Listening socket.
std::unique_ptr<boost::asio::deadline_timer> m_timer; ///< Timer which, when network is running, calls scheduler() every c_timerInterval ms.
static const unsigned c_timerInterval = 100; ///< Interval which m_timer is run when network is connected.
UPnP* m_upnp = nullptr; ///< UPnP helper.
bi::tcp::endpoint m_public; ///< Our public listening endpoint.
KeyPair m_key; ///< Our unique ID.
bi::tcp::endpoint m_public; ///< Our public listening endpoint.
KeyPair m_key; ///< Our unique ID.
bool m_hadNewNodes = false;
@ -237,13 +261,11 @@ private:
std::vector<NodeId> m_nodesList;
RangeMask<unsigned> m_ready; ///< Indices into m_nodesList over to which nodes we are not currently connected, connecting or otherwise ignoring.
RangeMask<unsigned> m_private; ///< Indices into m_nodesList over to which nodes are private.
unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to.
RangeMask<unsigned> m_private; ///< Indices into m_nodesList over to which nodes are private.
// Our addresses.
std::vector<bi::address> m_addresses; ///< Addresses for us.
std::vector<bi::address> m_peerAddresses; ///< Addresses that peers (can) know us by.
unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to.
std::set<bi::address> m_peerAddresses; ///< Public addresses that peers (can) know us by.
// Our capabilities.
std::map<CapDesc, std::shared_ptr<HostCapabilityFace>> m_capabilities; ///< Each of the capabilities we support.

2
libp2p/Session.cpp

@ -344,7 +344,7 @@ bool Session::interpret(RLP const& _r)
// goto CONTINUE; // Wierd port.
// Avoid our random other addresses that they might end up giving us.
for (auto i: m_server->m_addresses)
for (auto i: m_server->m_peerAddresses)
if (ep.address() == i && ep.port() == m_server->listenPort())
goto CONTINUE;

83
libsolidity/AST.cpp

@ -51,6 +51,40 @@ void StructDefinition::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this);
}
void StructDefinition::checkValidityOfMembers()
{
checkMemberTypes();
checkRecursion();
}
void StructDefinition::checkMemberTypes()
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
if (!member->getType()->canBeStored())
BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
}
void StructDefinition::checkRecursion()
{
set<StructDefinition const*> definitionsSeen;
vector<StructDefinition const*> queue = {this};
while (!queue.empty())
{
StructDefinition const* def = queue.back();
queue.pop_back();
if (definitionsSeen.count(def))
BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation())
<< errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT)
{
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
}
}
}
void ParameterList::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
@ -258,7 +292,7 @@ void Literal::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this);
}
TypeError ASTNode::createTypeError(string const& _description)
TypeError ASTNode::createTypeError(string const& _description) const
{
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
@ -338,14 +372,15 @@ void VariableDefinition::checkTypeRequirements()
m_variable->setType(m_value->getType());
}
}
if (m_variable->getType() && !m_variable->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(m_variable->createTypeError("Type is required to live outside storage."));
}
void Assignment::checkTypeRequirements()
{
m_leftHandSide->checkTypeRequirements();
m_leftHandSide->requireLValue();
//@todo later, assignments to structs might be possible, but not to mappings
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
m_rightHandSide->expectType(*m_leftHandSide->getType());
m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN)
@ -371,7 +406,7 @@ void Expression::expectType(Type const& _expectedType)
void Expression::requireLValue()
{
if (!isLvalue())
if (!isLValue())
BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue."));
m_lvalueRequested = true;
}
@ -432,24 +467,22 @@ void FunctionCall::checkTypeRequirements()
}
else
{
m_expression->requireLValue();
//@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
FunctionDefinition const& fun = dynamic_cast<FunctionType const&>(*expressionType).getFunction();
vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters();
if (parameters.size() != m_arguments.size())
FunctionType const& functionType = dynamic_cast<FunctionType const&>(*expressionType);
TypePointers const& parameterTypes = functionType.getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType()))
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
// @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs
if (fun.getReturnParameters().empty())
if (functionType.getReturnParameterTypes().empty())
m_type = make_shared<VoidType>();
else
m_type = fun.getReturnParameters().front()->getType();
m_type = functionType.getReturnParameterTypes().front();
}
}
@ -461,29 +494,24 @@ bool FunctionCall::isTypeConversion() const
void MemberAccess::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
m_expression->requireLValue();
if (m_expression->getType()->getCategory() != Type::Category::STRUCT)
BOOST_THROW_EXCEPTION(createTypeError("Member access to a non-struct (is " +
m_expression->getType()->toString() + ")"));
StructType const& type = dynamic_cast<StructType const&>(*m_expression->getType());
unsigned memberIndex = type.memberNameToIndex(*m_memberName);
if (memberIndex >= type.getMemberCount())
Type const& type = *m_expression->getType();
m_type = type.getMemberType(*m_memberName);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
m_type = type.getMemberByIndex(memberIndex).getType();
m_isLvalue = true;
//@todo later, this will not always be STORAGE
m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE;
}
void IndexAccess::checkTypeRequirements()
{
m_base->checkTypeRequirements();
m_base->requireLValue();
if (m_base->getType()->getCategory() != Type::Category::MAPPING)
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
m_base->getType()->toString() + ")"));
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_isLvalue = true;
m_lvalue = LValueType::STORAGE;
}
void Identifier::checkTypeRequirements()
@ -497,7 +525,7 @@ void Identifier::checkTypeRequirements()
if (!variable->getType())
BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined."));
m_type = variable->getType();
m_isLvalue = true;
m_lvalue = variable->isLocalVariable() ? LValueType::LOCAL : LValueType::STORAGE;
return;
}
//@todo can we unify these with TypeName::toType()?
@ -515,7 +543,6 @@ void Identifier::checkTypeRequirements()
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion.
m_type = make_shared<FunctionType>(*functionDef);
m_isLvalue = true;
return;
}
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
@ -524,6 +551,12 @@ void Identifier::checkTypeRequirements()
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
return;
}
MagicVariableDeclaration* magicVariable = dynamic_cast<MagicVariableDeclaration*>(m_referencedDeclaration);
if (magicVariable)
{
m_type = magicVariable->getType();
return;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
}

90
libsolidity/AST.h

@ -66,7 +66,7 @@ public:
/// Creates a @ref TypeError exception and decorates it with the location of the node and
/// the given description
TypeError createTypeError(std::string const& _description);
TypeError createTypeError(std::string const& _description) const;
///@{
///@name equality operators
@ -88,11 +88,16 @@ public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name) {}
/// Returns the declared name.
/// @returns the declared name.
ASTString const& getName() const { return *m_name; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
Declaration* getScope() const { return m_scope; }
void setScope(Declaration* const& _scope) { m_scope = _scope; }
private:
ASTPointer<ASTString> m_name;
Declaration* m_scope;
};
/**
@ -122,6 +127,7 @@ public:
/// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
@ -139,7 +145,14 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
/// Checks that the members do not include any recursive structs and have valid types
/// (e.g. no functions).
void checkValidityOfMembers();
private:
void checkMemberTypes();
void checkRecursion();
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
@ -165,14 +178,21 @@ private:
class FunctionDefinition: public Declaration
{
public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, bool _isPublic,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters),
m_body(_body) {}
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
bool _isPublic,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_returnParameters(_returnParameters),
m_body(_body),
m_documentation(_documentation)
{}
virtual void accept(ASTVisitor& _visitor) override;
bool isPublic() const { return m_isPublic; }
@ -182,18 +202,23 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block& getBody() { return *m_body; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() { return m_documentation; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
void checkTypeRequirements();
private:
bool m_isPublic;
ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst;
ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body;
ASTPointer<ASTString> m_documentation;
std::vector<VariableDeclaration const*> m_localVariables;
};
@ -210,7 +235,6 @@ public:
Declaration(_location, _name), m_typeName(_type) {}
virtual void accept(ASTVisitor& _visitor) override;
bool isTypeGivenExplicitly() const { return bool(m_typeName); }
TypeName* getTypeName() const { return m_typeName.get(); }
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
@ -218,12 +242,32 @@ public:
std::shared_ptr<Type const> const& getType() const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition*>(getScope()); }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
/**
* Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
* functions when such an identifier is encountered. Will never have a valid location in the source code.
*/
class MagicVariableDeclaration: public Declaration
{
public:
MagicVariableDeclaration(ASTString const& _name, std::shared_ptr<Type const> const& _type):
Declaration(Location(), std::make_shared<ASTString>(_name)), m_type(_type) {}
virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
std::shared_ptr<Type const> const& getType() const { return m_type; }
private:
std::shared_ptr<Type const> m_type;
};
/// Types
/// @{
@ -238,6 +282,7 @@ public:
/// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
/// pointer until the types have been resolved using the @ref NameAndTypeResolver.
/// If it returns an empty shared pointer after that, this indicates that the type was not found.
virtual std::shared_ptr<Type> toType() const = 0;
};
@ -263,8 +308,7 @@ private:
};
/**
* Name referring to a user-defined type (i.e. a struct).
* @todo some changes are necessary if this is also used to refer to contract types later
* Name referring to a user-defined type (i.e. a struct, contract, etc.).
*/
class UserDefinedTypeName: public TypeName
{
@ -275,13 +319,13 @@ public:
virtual std::shared_ptr<Type> toType() const override { return Type::fromUserDefinedTypeName(*this); }
ASTString const& getName() const { return *m_name; }
void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; }
StructDefinition const* getReferencedStruct() const { return m_referencedStruct; }
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
private:
ASTPointer<ASTString> m_name;
StructDefinition* m_referencedStruct;
Declaration* m_referencedDeclaration;
};
/**
@ -484,12 +528,16 @@ private:
*/
class Expression: public ASTNode
{
protected:
enum class LValueType { NONE, LOCAL, STORAGE };
public:
Expression(Location const& _location): ASTNode(_location), m_isLvalue(false), m_lvalueRequested(false) {}
Expression(Location const& _location): ASTNode(_location), m_lvalue(LValueType::NONE), m_lvalueRequested(false) {}
virtual void checkTypeRequirements() = 0;
std::shared_ptr<Type const> const& getType() const { return m_type; }
bool isLvalue() const { return m_isLvalue; }
bool isLValue() const { return m_lvalue != LValueType::NONE; }
bool isLocalLValue() const { return m_lvalue == LValueType::LOCAL; }
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it
/// is implicitly convertible to @a _expectedType. If not, throw exception.
@ -504,9 +552,9 @@ public:
protected:
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type;
//! Whether or not this expression is an lvalue, i.e. something that can be assigned to.
//! This is set during calls to @a checkTypeRequirements()
bool m_isLvalue;
//! If this expression is an lvalue (i.e. something that can be assigned to) and is stored
//! locally or in storage. This is set during calls to @a checkTypeRequirements()
LValueType m_lvalue;
//! Whether the outer expression requested the address (true) or the value (false) of this expression.
bool m_lvalueRequested;
};

1
libsolidity/ASTForward.h

@ -40,6 +40,7 @@ class StructDefinition;
class ParameterList;
class FunctionDefinition;
class VariableDeclaration;
class MagicVariableDeclaration;
class TypeName;
class ElementaryTypeName;
class UserDefinedTypeName;

15
libsolidity/Compiler.cpp

@ -32,17 +32,13 @@ using namespace std;
namespace dev {
namespace solidity {
bytes Compiler::compile(ContractDefinition& _contract, bool _optimize)
{
Compiler compiler;
compiler.compileContract(_contract);
return compiler.m_context.getAssembledBytecode(_optimize);
}
void Compiler::compileContract(ContractDefinition& _contract)
void Compiler::compileContract(ContractDefinition& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
{
m_context = CompilerContext(); // clear it just in case
for (MagicVariableDeclaration const* variable: _magicGlobals)
m_context.addMagicGlobal(*variable);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
m_context.addFunction(*function);
@ -328,7 +324,8 @@ bool Compiler::visit(ExpressionStatement& _expressionStatement)
{
Expression& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression);
if (expression.getType()->getCategory() != Type::Category::VOID)
// Type::Category category = expression.getType()->getCategory();
for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i)
m_context << eth::Instruction::POP;
return false;
}

5
libsolidity/Compiler.h

@ -32,13 +32,10 @@ class Compiler: private ASTVisitor
public:
Compiler(): m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition& _contract);
void compileContract(ContractDefinition& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
/// Compile the given contract and return the EVM bytecode.
static bytes compile(ContractDefinition& _contract, bool _optimize);
private:
/// Creates a new compiler context / assembly, packs the current code into the data part and
/// adds the constructor code.

5
libsolidity/CompilerContext.cpp

@ -30,6 +30,11 @@ using namespace std;
namespace dev {
namespace solidity {
void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration)
{
m_magicGlobals.insert(&_declaration);
}
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
{
m_stateVariables[&_declaration] = m_stateVariablesSize;

4
libsolidity/CompilerContext.h

@ -40,6 +40,7 @@ class CompilerContext
public:
CompilerContext(): m_stateVariablesSize(0) {}
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void initializeLocalVariables(unsigned _numVariables);
@ -48,6 +49,7 @@ public:
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); }
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); }
@ -90,6 +92,8 @@ public:
private:
eth::Assembly m_asm;
/// Magic global variables like msg, tx or this, distinguished by type.
std::set<Declaration const*> m_magicGlobals;
/// Size of the state variables, offset of next variable to be added.
u256 m_stateVariablesSize;
/// Storage offsets of state variables

7
libsolidity/CompilerStack.cpp

@ -23,6 +23,7 @@
#include <libsolidity/AST.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/GlobalContext.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/CompilerStack.h>
@ -45,7 +46,9 @@ void CompilerStack::parse()
if (!m_scanner)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available."));
m_contractASTNode = Parser().parse(m_scanner);
NameAndTypeResolver().resolveNamesAndTypes(*m_contractASTNode);
m_globalContext = make_shared<GlobalContext>();
m_globalContext->setCurrentContract(*m_contractASTNode);
NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode);
m_parseSuccessful = true;
}
@ -61,7 +64,7 @@ bytes const& CompilerStack::compile(bool _optimize)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
m_bytecode.clear();
m_compiler = make_shared<Compiler>();
m_compiler->compileContract(*m_contractASTNode);
m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables());
return m_bytecode = m_compiler->getAssembledBytecode(_optimize);
}

9
libsolidity/CompilerStack.h

@ -30,9 +30,11 @@
namespace dev {
namespace solidity {
class Scanner; // forward
class ContractDefinition; // forward
class Compiler; // forward
// forward declarations
class Scanner;
class ContractDefinition;
class Compiler;
class GlobalContext;
/**
* Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
@ -71,6 +73,7 @@ public:
private:
std::shared_ptr<Scanner> m_scanner;
std::shared_ptr<GlobalContext> m_globalContext;
std::shared_ptr<ContractDefinition> m_contractASTNode;
bool m_parseSuccessful;
std::string m_interface;

10
libsolidity/Scope.cpp → libsolidity/DeclarationContainer.cpp

@ -20,7 +20,7 @@
* Scope - object that holds declaration of names.
*/
#include <libsolidity/Scope.h>
#include <libsolidity/DeclarationContainer.h>
#include <libsolidity/AST.h>
namespace dev
@ -28,7 +28,7 @@ namespace dev
namespace solidity
{
bool Scope::registerDeclaration(Declaration& _declaration)
bool DeclarationContainer::registerDeclaration(Declaration& _declaration)
{
if (m_declarations.find(_declaration.getName()) != m_declarations.end())
return false;
@ -36,13 +36,13 @@ bool Scope::registerDeclaration(Declaration& _declaration)
return true;
}
Declaration* Scope::resolveName(ASTString const& _name, bool _recursive) const
Declaration* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{
auto result = m_declarations.find(_name);
if (result != m_declarations.end())
return result->second;
if (_recursive && m_enclosingScope)
return m_enclosingScope->resolveName(_name, true);
if (_recursive && m_enclosingContainer)
return m_enclosingContainer->resolveName(_name, true);
return nullptr;
}

10
libsolidity/Scope.h → libsolidity/DeclarationContainer.h

@ -36,18 +36,20 @@ namespace solidity
* Container that stores mappings betwee names and declarations. It also contains a link to the
* enclosing scope.
*/
class Scope
class DeclarationContainer
{
public:
explicit Scope(Scope* _enclosingScope = nullptr): m_enclosingScope(_enclosingScope) {}
explicit DeclarationContainer(Declaration* _enclosingDeclaration = nullptr, DeclarationContainer* _enclosingContainer = nullptr):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff
/// it was not yet declared.
bool registerDeclaration(Declaration& _declaration);
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const;
Scope* getEnclosingScope() const { return m_enclosingScope; }
Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
private:
Scope* m_enclosingScope;
Declaration* m_enclosingDeclaration;
DeclarationContainer* m_enclosingContainer;
std::map<ASTString, Declaration*> m_declarations;
};

235
libsolidity/ExpressionCompiler.cpp

@ -160,67 +160,181 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
bool ExpressionCompiler::visit(FunctionCall& _functionCall)
{
using Location = FunctionType::Location;
if (_functionCall.isTypeConversion())
{
//@todo we only have integers and bools for now which cannot be explicitly converted
//@todo struct construction
if (asserts(_functionCall.getArguments().size() == 1))
BOOST_THROW_EXCEPTION(InternalCompilerError());
Expression& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this);
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT &&
_functionCall.getType()->getCategory() == Type::Category::INTEGER)
{
// explicit type conversion contract -> address, nothing to do.
}
else
{
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
}
}
else
{
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
FunctionDefinition const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()).getFunction();
eth::AssemblyItem returnLabel = m_context.pushNewTag();
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments();
if (asserts(arguments.size() == function.getParameters().size()))
if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
for (unsigned i = 0; i < arguments.size(); ++i)
if (function.getLocation() == Location::INTERNAL)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType());
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
eth::AssemblyItem returnLabel = m_context.pushNewTag();
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i]);
}
_functionCall.getExpression().accept(*this);
m_context.appendJump();
m_context << returnLabel;
// callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(function.getReturnParameterTypes().size() - arguments.size() - 1);
// @todo for now, the return value of a function is its first return value, so remove
// all others
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
m_context << eth::Instruction::POP;
}
else if (function.getLocation() == Location::EXTERNAL)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet."));
else
{
switch (function.getLocation())
{
case Location::SEND:
m_context << u256(0) << u256(0) << u256(0) << u256(0);
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
_functionCall.getExpression().accept(*this);
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP;
break;
case Location::SUICIDE:
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
{
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
u256 contractAddress = contractAddresses.find(function.getLocation())->second;
// @todo later, combine this code with external function call
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
// @todo move this once we actually use memory
m_context << u256(i * 32) << eth::Instruction::MSTORE;
}
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
<< contractAddress << u256(500) //@todo determine actual gas requirement
<< eth::Instruction::CALL
<< eth::Instruction::POP
<< u256(0) << eth::Instruction::MLOAD;
break;
}
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented."));
}
}
_functionCall.getExpression().accept(*this);
if (asserts(m_currentLValue.isInCode()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Code reference expected."));
m_currentLValue.reset();
m_context.appendJump();
m_context << returnLabel;
// callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(function.getReturnParameters().size() - arguments.size() - 1);
// @todo for now, the return value of a function is its first return value, so remove
// all others
for (unsigned i = 1; i < function.getReturnParameters().size(); ++i)
m_context << eth::Instruction::POP;
}
return false;
}
void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
{
if (asserts(m_currentLValue.isInStorage()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to a non-storage value."));
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
unsigned memberIndex = type.memberNameToIndex(_memberAccess.getMemberName());
if (asserts(memberIndex <= type.getMemberCount()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member not found in struct during compilation."));
m_context << type.getStorageOffsetOfMember(memberIndex) << eth::Instruction::ADD;
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
ASTString const& member = _memberAccess.getMemberName();
switch (_memberAccess.getExpression().getType()->getCategory())
{
case Type::Category::INTEGER:
if (member == "balance")
{
appendTypeConversion(*_memberAccess.getExpression().getType(),
IntegerType(0, IntegerType::Modifier::ADDRESS), true);
m_context << eth::Instruction::BALANCE;
}
else if (member == "send")
{
appendTypeConversion(*_memberAccess.getExpression().getType(),
IntegerType(0, IntegerType::Modifier::ADDRESS), true);
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
case Type::Category::CONTRACT:
// call function
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented."));
break;
case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase")
m_context << eth::Instruction::COINBASE;
else if (member == "timestamp")
m_context << eth::Instruction::TIMESTAMP;
else if (member == "prevhash")
m_context << eth::Instruction::PREVHASH;
else if (member == "difficulty")
m_context << eth::Instruction::DIFFICULTY;
else if (member == "number")
m_context << eth::Instruction::NUMBER;
else if (member == "gaslimit")
m_context << eth::Instruction::GASLIMIT;
else if (member == "sender")
m_context << eth::Instruction::CALLER;
else if (member == "value")
m_context << eth::Instruction::CALLVALUE;
else if (member == "origin")
m_context << eth::Instruction::ORIGIN;
else if (member == "gas")
m_context << eth::Instruction::GAS;
else if (member == "gasprice")
m_context << eth::Instruction::GASPRICE;
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
break;
case Type::Category::STRUCT:
{
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::STORAGE);
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break;
}
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type."));
}
}
bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
{
m_currentLValue.reset();
_indexAccess.getBaseExpression().accept(*this);
if (asserts(m_currentLValue.isInStorage()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index access to a non-storage value."));
_indexAccess.getIndexExpression().accept(*this);
appendTypeConversion(*_indexAccess.getIndexExpression().getType(),
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
@ -237,8 +351,25 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
void ExpressionCompiler::endVisit(Identifier& _identifier)
{
m_currentLValue.fromDeclaration(_identifier, *_identifier.getReferencedDeclaration());
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
Declaration* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration* magicVar = dynamic_cast<MagicVariableDeclaration*>(declaration))
{
if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
m_context << eth::Instruction::ADDRESS;
return;
}
if (FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(declaration))
{
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
return;
}
if (/*VariableDeclaration* varDef = */dynamic_cast<VariableDeclaration*>(declaration))
{
m_currentLValue.fromIdentifier(_identifier, *_identifier.getReferencedDeclaration());
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
return;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
}
void ExpressionCompiler::endVisit(Literal& _literal)
@ -405,9 +536,6 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
{
switch (m_type)
{
case CODE:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Tried to retrieve value of a function."));
break;
case STACK:
{
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
@ -418,11 +546,15 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break;
}
case STORAGE:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
if (!_remove)
*m_context << eth::Instruction::DUP1;
*m_context << eth::Instruction::SLOAD;
break;
case MEMORY:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented."));
break;
@ -450,15 +582,15 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
break;
}
case LValue::STORAGE:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
if (!_move)
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
*m_context << eth::Instruction::SSTORE;
break;
case LValue::CODE:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type does not support assignment."));
break;
case LValue::MEMORY:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented."));
break;
@ -469,7 +601,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
}
}
void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(const Expression& _expression)
void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression)
{
if (!_expression.lvalueRequested())
{
@ -478,7 +610,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(const Express
}
}
void ExpressionCompiler::LValue::fromDeclaration( Expression const& _expression, Declaration const& _declaration)
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
if (m_context->isLocalVariable(&_declaration))
{
@ -490,13 +622,8 @@ void ExpressionCompiler::LValue::fromDeclaration( Expression const& _expression,
m_type = STORAGE;
*m_context << m_context->getStorageLocationOfVariable(_declaration);
}
else if (m_context->isFunctionDefinition(&_declaration))
{
m_type = CODE;
*m_context << m_context->getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(_declaration)).pushTag();
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Identifier type not supported or identifier not found."));
}

17
libsolidity/ExpressionCompiler.h

@ -83,31 +83,30 @@ private:
/**
* Helper class to store and retrieve lvalues to and from various locations.
* All types except STACK store a reference in a slot on the stack, STACK just stores the
* base stack offset of the variable in @a m_baseStackOffset.
* All types except STACK store a reference in a slot on the stack, STACK just
* stores the base stack offset of the variable in @a m_baseStackOffset.
*/
class LValue
{
public:
enum LValueType { NONE, CODE, STACK, MEMORY, STORAGE };
enum LValueType { NONE, STACK, MEMORY, STORAGE };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
/// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression, used for error reporting.
void fromDeclaration(Expression const& _expression, Declaration const& _declaration);
/// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
void reset() { m_type = NONE; m_baseStackOffset = 0; }
bool isValid() const { return m_type != NONE; }
bool isInCode() const { return m_type == CODE; }
bool isInOnStack() const { return m_type == STACK; }
bool isInMemory() const { return m_type == MEMORY; }
bool isInStorage() const { return m_type == STORAGE; }
/// @returns true if this lvalue reference type occupies a slot on the stack.
bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY || m_type == CODE; }
bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY; }
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
/// also removes the reference from the stack (note that is does not reset the type to @a NONE).
@ -133,6 +132,10 @@ private:
CompilerContext& m_context;
LValue m_currentLValue;
/// If a "virtual" function (i.e. a bulit-in function without jump tag) is encountered, the
/// actual function is stored here. @todo prevent assignment or store it with assignment
enum class SpecialFunction { NONE, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
SpecialFunction m_currentSpecialFunction;
};

101
libsolidity/GlobalContext.cpp

@ -0,0 +1,101 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Container of the (implicit and explicit) global objects.
*/
#include <memory>
#include <libsolidity/GlobalContext.h>
#include <libsolidity/AST.h>
#include <libsolidity/Types.h>
using namespace std;
namespace dev
{
namespace solidity
{
GlobalContext::GlobalContext():
m_magicVariables{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::BLOCK)),
make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::MSG)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::TX)),
make_shared<MagicVariableDeclaration>("suicide",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(0,
IntegerType::Modifier::ADDRESS)}),
TypePointers(),
FunctionType::Location::SUICIDE)),
make_shared<MagicVariableDeclaration>("sha3",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
FunctionType::Location::SHA3)),
make_shared<MagicVariableDeclaration>("sha256",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
FunctionType::Location::SHA256)),
make_shared<MagicVariableDeclaration>("ecrecover",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH),
std::make_shared<IntegerType>(8, IntegerType::Modifier::HASH),
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH),
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)}),
FunctionType::Location::ECRECOVER)),
make_shared<MagicVariableDeclaration>("ripemd160",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(160, IntegerType::Modifier::HASH)}),
FunctionType::Location::RIPEMD160))}
{
}
void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
{
m_currentContract = &_contract;
}
vector<Declaration*> GlobalContext::getDeclarations() const
{
vector<Declaration*> declarations;
declarations.reserve(m_magicVariables.size() + 1);
for (ASTPointer<Declaration> const& variable: m_magicVariables)
declarations.push_back(variable.get());
declarations.push_back(getCurrentThis());
return declarations;
}
MagicVariableDeclaration*GlobalContext::getCurrentThis() const
{
if (!m_thisPointer[m_currentContract])
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
"this", make_shared<ContractType>(*m_currentContract));
return m_thisPointer[m_currentContract].get();
}
vector<MagicVariableDeclaration const*> GlobalContext::getMagicVariables() const
{
vector<MagicVariableDeclaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1);
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get());
declarations.push_back(getCurrentThis());
return declarations;
}
}
}

62
libsolidity/GlobalContext.h

@ -0,0 +1,62 @@
/*
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/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Container of the (implicit and explicit) global objects.
*/
#pragma once
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <boost/noncopyable.hpp>
#include <libsolidity/ASTForward.h>
namespace dev
{
namespace solidity
{
class Type; // forward
/**
* Container for all global objects which look like AST nodes, but are not part of the AST
* that is currently being compiled.
* @note must not be destroyed or moved during compilation as its objects can be referenced from
* other objects.
*/
class GlobalContext: private boost::noncopyable
{
public:
GlobalContext();
void setCurrentContract(ContractDefinition const& _contract);
std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
std::vector<Declaration*> getDeclarations() const;
private:
MagicVariableDeclaration* getCurrentThis() const;
std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables;
ContractDefinition const* m_currentContract;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration>> mutable m_thisPointer;
};
}
}

62
libsolidity/NameAndTypeResolver.cpp

@ -32,15 +32,20 @@ namespace solidity
{
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration*> const& _globals)
{
for (Declaration* declaration: _globals)
m_scopes[nullptr].registerDeclaration(*declaration);
}
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{
reset();
DeclarationRegistrationHelper registrar(m_scopes, _contract);
m_currentScope = &m_scopes[&_contract];
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, nullptr);
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
checkForRecursion(*structDef);
structDef->checkValidityOfMembers();
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, nullptr);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
@ -73,33 +78,9 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name
return m_currentScope->resolveName(_name, _recursive);
}
void NameAndTypeResolver::checkForRecursion(StructDefinition const& _struct)
{
set<StructDefinition const*> definitionsSeen;
vector<StructDefinition const*> queue = {&_struct};
while (!queue.empty())
{
StructDefinition const* def = queue.back();
queue.pop_back();
if (definitionsSeen.count(def))
BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation())
<< errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT)
queue.push_back(dynamic_cast<UserDefinedTypeName&>(*member->getTypeName()).getReferencedStruct());
}
}
void NameAndTypeResolver::reset()
{
m_scopes.clear();
m_currentScope = nullptr;
}
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, Scope>& _scopes,
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes,
ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(&m_scopes[nullptr])
m_scopes(_scopes), m_currentScope(nullptr)
{
_astRoot.accept(*this);
}
@ -154,31 +135,30 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
return true;
}
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node)
void DeclarationRegistrationHelper::enterNewSubScope(Declaration& _declaration)
{
map<ASTNode const*, Scope>::iterator iter;
map<ASTNode const*, DeclarationContainer>::iterator iter;
bool newlyAdded;
tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope));
tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope]));
if (asserts(newlyAdded))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to add new scope."));
m_currentScope = &iter->second;
m_currentScope = &_declaration;
}
void DeclarationRegistrationHelper::closeCurrentScope()
{
if (asserts(m_currentScope))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Closed non-existing scope."));
m_currentScope = m_currentScope->getEnclosingScope();
m_currentScope = m_scopes[m_currentScope].getEnclosingDeclaration();
}
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{
if (asserts(m_currentScope))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration registered without scope."));
if (!m_currentScope->registerDeclaration(_declaration))
if (!m_scopes[m_currentScope].registerDeclaration(_declaration))
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation())
<< errinfo_comment("Identifier already declared."));
//@todo the exception should also contain the location of the first declaration
_declaration.setScope(m_currentScope);
if (_opensScope)
enterNewSubScope(_declaration);
}
@ -195,7 +175,11 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
// endVisit because the internal type needs resolving if it is a user defined type
// or mapping
if (_variable.getTypeName())
{
_variable.setType(_variable.getTypeName()->toType());
if (!_variable.getType())
BOOST_THROW_EXCEPTION(_variable.getTypeName()->createTypeError("Invalid type name"));
}
else if (!m_allowLazyTypes)
BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed."));
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
@ -221,11 +205,7 @@ bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation())
<< errinfo_comment("Undeclared identifier."));
StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration);
//@todo later, contracts are also valid types
if (!referencedStruct)
BOOST_THROW_EXCEPTION(_typeName.createTypeError("Identifier does not name a type name."));
_typeName.setReferencedStruct(*referencedStruct);
_typeName.setReferencedDeclaration(*declaration);
return false;
}

17
libsolidity/NameAndTypeResolver.h

@ -25,7 +25,7 @@
#include <map>
#include <boost/noncopyable.hpp>
#include <libsolidity/Scope.h>
#include <libsolidity/DeclarationContainer.h>
#include <libsolidity/ASTVisitor.h>
namespace dev
@ -41,8 +41,7 @@ namespace solidity
class NameAndTypeResolver: private boost::noncopyable
{
public:
NameAndTypeResolver() {}
explicit NameAndTypeResolver(std::vector<Declaration*> const& _globals);
void resolveNamesAndTypes(ContractDefinition& _contract);
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
@ -62,9 +61,9 @@ private:
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
/// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code.
std::map<ASTNode const*, Scope> m_scopes;
std::map<ASTNode const*, DeclarationContainer> m_scopes;
Scope* m_currentScope;
DeclarationContainer* m_currentScope;
};
/**
@ -74,7 +73,7 @@ private:
class DeclarationRegistrationHelper: private ASTVisitor
{
public:
DeclarationRegistrationHelper(std::map<ASTNode const*, Scope>& _scopes, ASTNode& _astRoot);
DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot);
private:
bool visit(ContractDefinition& _contract);
@ -86,12 +85,12 @@ private:
void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration);
void enterNewSubScope(ASTNode& _node);
void enterNewSubScope(Declaration& _declaration);
void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope);
std::map<ASTNode const*, Scope>& m_scopes;
Scope* m_currentScope;
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration* m_currentScope;
FunctionDefinition* m_currentFunction;
};

9
libsolidity/Parser.cpp

@ -117,6 +117,10 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = std::make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters(parseParameterList());
@ -142,8 +146,9 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
}
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, parameters,
isDeclaredConst, returnParameters, block);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, docstring,
parameters,
isDeclaredConst, returnParameters, block);
}
ASTPointer<StructDefinition> Parser::parseStructDefinition()

291
libsolidity/Scanner.cpp

@ -102,18 +102,55 @@ int hexValue(char c)
}
} // end anonymous namespace
/// Scoped helper for literal recording. Automatically drops the literal
/// if aborting the scanning before it's complete.
enum LiteralType {
LITERAL_TYPE_STRING,
LITERAL_TYPE_NUMBER, // not really different from string type in behaviour
LITERAL_TYPE_COMMENT
};
class LiteralScope
{
public:
explicit LiteralScope(Scanner* _self, enum LiteralType _type): m_type(_type)
, m_scanner(_self)
, m_complete(false)
{
if (_type == LITERAL_TYPE_COMMENT)
m_scanner->m_nextSkippedComment.literal.clear();
else
m_scanner->m_nextToken.literal.clear();
}
~LiteralScope()
{
if (!m_complete)
{
if (m_type == LITERAL_TYPE_COMMENT)
m_scanner->m_nextSkippedComment.literal.clear();
else
m_scanner->m_nextToken.literal.clear();
}
}
void complete() { m_complete = true; }
private:
enum LiteralType m_type;
Scanner* m_scanner;
bool m_complete;
}; // end of LiteralScope class
void Scanner::reset(CharStream const& _source)
{
bool foundDocComment;
m_source = _source;
m_char = m_source.get();
skipWhitespace();
foundDocComment = scanToken();
scanToken();
// special version of Scanner:next() taking the previous scanToken() result into account
m_currentToken = m_nextToken;
if (scanToken() || foundDocComment)
m_skippedComment = m_nextSkippedComment;
next();
}
@ -142,8 +179,9 @@ BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100);
Token::Value Scanner::next()
{
m_currentToken = m_nextToken;
if (scanToken())
m_skippedComment = m_nextSkippedComment;
m_skippedComment = m_nextSkippedComment;
scanToken();
return m_currentToken.token;
}
@ -156,7 +194,6 @@ Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _
return _else;
}
bool Scanner::skipWhitespace()
{
int const startPosition = getSourcePos();
@ -166,7 +203,6 @@ bool Scanner::skipWhitespace()
return getSourcePos() != startPosition;
}
Token::Value Scanner::skipSingleLineComment()
{
// The line terminator at the end of the line is not considered
@ -177,13 +213,28 @@ Token::Value Scanner::skipSingleLineComment()
return Token::WHITESPACE;
}
/// For the moment this function simply consumes a single line triple slash doc comment
Token::Value Scanner::scanDocumentationComment()
{
LiteralScope literal(this);
LiteralScope literal(this, LITERAL_TYPE_COMMENT);
advance(); //consume the last '/'
while (!isSourcePastEndOfInput() && !isLineTerminator(m_char))
while (!isSourcePastEndOfInput())
{
if (isLineTerminator(m_char))
{
// check if next line is also a documentation comment
skipWhitespace();
if (!m_source.isPastEndOfInput(3) &&
m_source.get(0) == '/' &&
m_source.get(1) == '/' &&
m_source.get(2) == '/')
{
addCommentLiteralChar('\n');
m_char = m_source.advanceAndGet(3);
}
else
break; // next line is not a documentation comment, we are done
}
addCommentLiteralChar(m_char);
advance();
}
@ -214,10 +265,10 @@ Token::Value Scanner::skipMultiLineComment()
return Token::ILLEGAL;
}
bool Scanner::scanToken()
void Scanner::scanToken()
{
bool foundDocComment = false;
m_nextToken.literal.clear();
m_nextSkippedComment.literal.clear();
Token::Value token;
do
{
@ -329,7 +380,6 @@ bool Scanner::scanToken()
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
token = Token::WHITESPACE;
foundDocComment = true;
}
else
token = skipSingleLineComment();
@ -425,8 +475,6 @@ bool Scanner::scanToken()
while (token == Token::WHITESPACE);
m_nextToken.location.end = getSourcePos();
m_nextToken.token = token;
return foundDocComment;
}
bool Scanner::scanEscape()
@ -474,7 +522,7 @@ Token::Value Scanner::scanString()
{
char const quote = m_char;
advance(); // consume quote
LiteralScope literal(this);
LiteralScope literal(this, LITERAL_TYPE_STRING);
while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char))
{
char c = m_char;
@ -494,18 +542,16 @@ Token::Value Scanner::scanString()
return Token::STRING_LITERAL;
}
void Scanner::scanDecimalDigits()
{
while (isDecimalDigit(m_char))
addLiteralCharAndAdvance();
}
Token::Value Scanner::scanNumber(char _charSeen)
{
enum { DECIMAL, HEX, BINARY } kind = DECIMAL;
LiteralScope literal(this);
LiteralScope literal(this, LITERAL_TYPE_NUMBER);
if (_charSeen == '.')
{
// we have already seen a decimal point of the float
@ -572,210 +618,41 @@ Token::Value Scanner::scanNumber(char _charSeen)
// ----------------------------------------------------------------------------
// Keyword Matcher
#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
KEYWORD_GROUP('a') \
KEYWORD("address", Token::ADDRESS) \
KEYWORD_GROUP('b') \
KEYWORD("break", Token::BREAK) \
KEYWORD("bool", Token::BOOL) \
KEYWORD_GROUP('c') \
KEYWORD("case", Token::CASE) \
KEYWORD("const", Token::CONST) \
KEYWORD("continue", Token::CONTINUE) \
KEYWORD("contract", Token::CONTRACT) \
KEYWORD_GROUP('d') \
KEYWORD("default", Token::DEFAULT) \
KEYWORD("delete", Token::DELETE) \
KEYWORD("do", Token::DO) \
KEYWORD_GROUP('e') \
KEYWORD("else", Token::ELSE) \
KEYWORD("extends", Token::EXTENDS) \
KEYWORD_GROUP('f') \
KEYWORD("false", Token::FALSE_LITERAL) \
KEYWORD("for", Token::FOR) \
KEYWORD("function", Token::FUNCTION) \
KEYWORD_GROUP('h') \
KEYWORD("hash", Token::HASH) \
KEYWORD("hash8", Token::HASH8) \
KEYWORD("hash16", Token::HASH16) \
KEYWORD("hash24", Token::HASH24) \
KEYWORD("hash32", Token::HASH32) \
KEYWORD("hash40", Token::HASH40) \
KEYWORD("hash48", Token::HASH48) \
KEYWORD("hash56", Token::HASH56) \
KEYWORD("hash64", Token::HASH64) \
KEYWORD("hash72", Token::HASH72) \
KEYWORD("hash80", Token::HASH80) \
KEYWORD("hash88", Token::HASH88) \
KEYWORD("hash96", Token::HASH96) \
KEYWORD("hash104", Token::HASH104) \
KEYWORD("hash112", Token::HASH112) \
KEYWORD("hash120", Token::HASH120) \
KEYWORD("hash128", Token::HASH128) \
KEYWORD("hash136", Token::HASH136) \
KEYWORD("hash144", Token::HASH144) \
KEYWORD("hash152", Token::HASH152) \
KEYWORD("hash160", Token::HASH160) \
KEYWORD("hash168", Token::HASH168) \
KEYWORD("hash178", Token::HASH176) \
KEYWORD("hash184", Token::HASH184) \
KEYWORD("hash192", Token::HASH192) \
KEYWORD("hash200", Token::HASH200) \
KEYWORD("hash208", Token::HASH208) \
KEYWORD("hash216", Token::HASH216) \
KEYWORD("hash224", Token::HASH224) \
KEYWORD("hash232", Token::HASH232) \
KEYWORD("hash240", Token::HASH240) \
KEYWORD("hash248", Token::HASH248) \
KEYWORD("hash256", Token::HASH256) \
KEYWORD_GROUP('i') \
KEYWORD("if", Token::IF) \
KEYWORD("in", Token::IN) \
KEYWORD("int", Token::INT) \
KEYWORD("int8", Token::INT8) \
KEYWORD("int16", Token::INT16) \
KEYWORD("int24", Token::INT24) \
KEYWORD("int32", Token::INT32) \
KEYWORD("int40", Token::INT40) \
KEYWORD("int48", Token::INT48) \
KEYWORD("int56", Token::INT56) \
KEYWORD("int64", Token::INT64) \
KEYWORD("int72", Token::INT72) \
KEYWORD("int80", Token::INT80) \
KEYWORD("int88", Token::INT88) \
KEYWORD("int96", Token::INT96) \
KEYWORD("int104", Token::INT104) \
KEYWORD("int112", Token::INT112) \
KEYWORD("int120", Token::INT120) \
KEYWORD("int128", Token::INT128) \
KEYWORD("int136", Token::INT136) \
KEYWORD("int144", Token::INT144) \
KEYWORD("int152", Token::INT152) \
KEYWORD("int160", Token::INT160) \
KEYWORD("int168", Token::INT168) \
KEYWORD("int178", Token::INT176) \
KEYWORD("int184", Token::INT184) \
KEYWORD("int192", Token::INT192) \
KEYWORD("int200", Token::INT200) \
KEYWORD("int208", Token::INT208) \
KEYWORD("int216", Token::INT216) \
KEYWORD("int224", Token::INT224) \
KEYWORD("int232", Token::INT232) \
KEYWORD("int240", Token::INT240) \
KEYWORD("int248", Token::INT248) \
KEYWORD("int256", Token::INT256) \
KEYWORD_GROUP('l') \
KEYWORD_GROUP('m') \
KEYWORD("mapping", Token::MAPPING) \
KEYWORD_GROUP('n') \
KEYWORD("new", Token::NEW) \
KEYWORD("null", Token::NULL_LITERAL) \
KEYWORD_GROUP('p') \
KEYWORD("private", Token::PRIVATE) \
KEYWORD("public", Token::PUBLIC) \
KEYWORD_GROUP('r') \
KEYWORD("real", Token::REAL) \
KEYWORD("return", Token::RETURN) \
KEYWORD("returns", Token::RETURNS) \
KEYWORD_GROUP('s') \
KEYWORD("string", Token::STRING_TYPE) \
KEYWORD("struct", Token::STRUCT) \
KEYWORD("switch", Token::SWITCH) \
KEYWORD_GROUP('t') \
KEYWORD("text", Token::TEXT) \
KEYWORD("this", Token::THIS) \
KEYWORD("true", Token::TRUE_LITERAL) \
KEYWORD_GROUP('u') \
KEYWORD("uint", Token::UINT) \
KEYWORD("uint8", Token::UINT8) \
KEYWORD("uint16", Token::UINT16) \
KEYWORD("uint24", Token::UINT24) \
KEYWORD("uint32", Token::UINT32) \
KEYWORD("uint40", Token::UINT40) \
KEYWORD("uint48", Token::UINT48) \
KEYWORD("uint56", Token::UINT56) \
KEYWORD("uint64", Token::UINT64) \
KEYWORD("uint72", Token::UINT72) \
KEYWORD("uint80", Token::UINT80) \
KEYWORD("uint88", Token::UINT88) \
KEYWORD("uint96", Token::UINT96) \
KEYWORD("uint104", Token::UINT104) \
KEYWORD("uint112", Token::UINT112) \
KEYWORD("uint120", Token::UINT120) \
KEYWORD("uint128", Token::UINT128) \
KEYWORD("uint136", Token::UINT136) \
KEYWORD("uint144", Token::UINT144) \
KEYWORD("uint152", Token::UINT152) \
KEYWORD("uint160", Token::UINT160) \
KEYWORD("uint168", Token::UINT168) \
KEYWORD("uint178", Token::UINT176) \
KEYWORD("uint184", Token::UINT184) \
KEYWORD("uint192", Token::UINT192) \
KEYWORD("uint200", Token::UINT200) \
KEYWORD("uint208", Token::UINT208) \
KEYWORD("uint216", Token::UINT216) \
KEYWORD("uint224", Token::UINT224) \
KEYWORD("uint232", Token::UINT232) \
KEYWORD("uint240", Token::UINT240) \
KEYWORD("uint248", Token::UINT248) \
KEYWORD("uint256", Token::UINT256) \
KEYWORD("ureal", Token::UREAL) \
KEYWORD_GROUP('v') \
KEYWORD("var", Token::VAR) \
KEYWORD_GROUP('w') \
KEYWORD("while", Token::WHILE) \
static Token::Value KeywordOrIdentifierToken(string const& _input)
static Token::Value keywordOrIdentifierToken(string const& _input)
{
if (asserts(!_input.empty()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
int const kMinLength = 2;
int const kMaxLength = 10;
if (_input.size() < kMinLength || _input.size() > kMaxLength)
return Token::IDENTIFIER;
switch (_input[0])
{
default:
#define KEYWORD_GROUP_CASE(ch) \
break; \
case ch:
#define KEYWORD(keyword, token) \
{ \
/* 'keyword' is a char array, so sizeof(keyword) is */ \
/* strlen(keyword) plus 1 for the NUL char. */ \
int const keywordLength = sizeof(keyword) - 1; \
BOOST_STATIC_ASSERT(keywordLength >= kMinLength); \
BOOST_STATIC_ASSERT(keywordLength <= kMaxLength); \
if (_input == keyword) \
return token; \
}
KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
}
return Token::IDENTIFIER;
// The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
// and keywords to be put inside the keywords variable.
#define KEYWORD(name, string, precedence) {string, Token::name},
#define TOKEN(name, string, precedence)
static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
#undef KEYWORD
#undef TOKEN
auto it = keywords.find(_input);
return it == keywords.end() ? Token::IDENTIFIER : it->second;
}
Token::Value Scanner::scanIdentifierOrKeyword()
{
if (asserts(isIdentifierStart(m_char)))
BOOST_THROW_EXCEPTION(InternalCompilerError());
LiteralScope literal(this);
LiteralScope literal(this, LITERAL_TYPE_STRING);
addLiteralCharAndAdvance();
// Scan the rest of the identifier characters.
while (isIdentifierPart(m_char))
addLiteralCharAndAdvance();
literal.complete();
return KeywordOrIdentifierToken(m_nextToken.literal);
return keywordOrIdentifierToken(m_nextToken.literal);
}
char CharStream::advanceAndGet()
char CharStream::advanceAndGet(size_t _chars)
{
if (isPastEndOfInput())
return 0;
++m_pos;
m_pos += _chars;
if (isPastEndOfInput())
return 0;
return get();
return m_source[m_pos];
}
char CharStream::rollback(size_t _amount)

32
libsolidity/Scanner.h

@ -74,9 +74,9 @@ public:
CharStream(): m_pos(0) {}
explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {}
int getPos() const { return m_pos; }
bool isPastEndOfInput() const { return m_pos >= m_source.size(); }
char get() const { return m_source[m_pos]; }
char advanceAndGet();
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_pos + _charsForward) >= m_source.size(); }
char get(size_t _charsForward = 0) const { return m_source[m_pos + _charsForward]; }
char advanceAndGet(size_t _chars=1);
char rollback(size_t _amount);
///@{
@ -93,22 +93,11 @@ private:
};
class Scanner
{
friend class LiteralScope;
public:
/// Scoped helper for literal recording. Automatically drops the literal
/// if aborting the scanning before it's complete.
class LiteralScope
{
public:
explicit LiteralScope(Scanner* self): m_scanner(self), m_complete(false) { m_scanner->startNewLiteral(); }
~LiteralScope() { if (!m_complete) m_scanner->dropLiteral(); }
void complete() { m_complete = true; }
private:
Scanner* m_scanner;
bool m_complete;
};
Scanner() { reset(CharStream()); }
explicit Scanner(CharStream const& _source) { reset(_source); }
@ -133,8 +122,12 @@ public:
///@{
///@name Information about the current comment token
Location getCurrentCommentLocation() const { return m_skippedComment.location; }
std::string const& getCurrentCommentLiteral() const { return m_skippedComment.literal; }
/// Called by the parser during FunctionDefinition parsing to clear the current comment
void clearCurrentCommentLiteral() { m_skippedComment.literal.clear(); }
///@}
///@{
@ -165,10 +158,8 @@ private:
///@{
///@name Literal buffer support
inline void startNewLiteral() { m_nextToken.literal.clear(); }
inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); }
inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); }
inline void dropLiteral() { m_nextToken.literal.clear(); }
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
///@}
@ -181,9 +172,8 @@ private:
bool scanHexByte(char& o_scannedByte);
/// Scans a single Solidity token. Returns true if the scanned token was
/// a skipped documentation comment. False in all other cases.
bool scanToken();
/// Scans a single Solidity token.
void scanToken();
/// Skips all whitespace and @returns true if something was skipped.
bool skipWhitespace();

15
libsolidity/Token.h

@ -160,7 +160,6 @@ namespace solidity
K(RETURNS, "returns", 0) \
K(STRUCT, "struct", 0) \
K(SWITCH, "switch", 0) \
K(THIS, "this", 0) \
K(VAR, "var", 0) \
K(WHILE, "while", 0) \
\
@ -315,25 +314,11 @@ public:
}
// Predicates
static bool isKeyword(Value tok) { return m_tokenType[tok] == 'K'; }
static bool isIdentifier(Value tok) { return tok == IDENTIFIER; }
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
static bool isTruncatingBinaryOp(Value op) { return BIT_OR <= op && op <= SHR; }
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }
static bool isOrderedRelationalCompareOp(Value op)
{
return op == LT || op == LTE || op == GT || op == GTE;
}
static bool isEqualityOp(Value op) { return op == EQ; }
static bool isInequalityOp(Value op) { return op == NE; }
static bool isArithmeticCompareOp(Value op)
{
return isOrderedRelationalCompareOp(op) ||
isEqualityOp(op) || isInequalityOp(op);
}
static Value AssignmentToBinaryOp(Value op)
{

180
libsolidity/Types.cpp

@ -60,13 +60,24 @@ shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{
return make_shared<StructType>(*_typeName.getReferencedStruct());
Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
return make_shared<StructType>(*structDef);
else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration))
return make_shared<FunctionType>(*function);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
return make_shared<ContractType>(*contract);
return shared_ptr<Type>();
}
shared_ptr<Type> Type::fromMapping(Mapping const& _typeName)
{
shared_ptr<Type const> keyType = _typeName.getKeyType().toType();
if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
shared_ptr<Type const> valueType = _typeName.getValueType().toType();
if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name"));
return make_shared<MappingType>(keyType, valueType);
}
@ -86,6 +97,8 @@ shared_ptr<Type> Type::forLiteral(Literal const& _literal)
}
}
const MemberList Type::EmptyMemberList = MemberList();
shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(string const& _literal)
{
bigint value(_literal);
@ -176,6 +189,11 @@ u256 IntegerType::literalValue(Literal const& _literal) const
return u256(value);
}
const MemberList IntegerType::AddressMemberList =
MemberList({{"balance", make_shared<IntegerType const>(256)},
{"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
TypePointers(), FunctionType::Location::SEND)}});
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
// conversion to integer is fine, but not to address
@ -199,6 +217,15 @@ u256 BoolType::literalValue(Literal const& _literal) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal."));
}
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (isImplicitlyConvertibleTo(_convertTo))
return true;
if (_convertTo.getCategory() == Category::INTEGER)
return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
return false;
}
bool ContractType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -215,6 +242,11 @@ u256 ContractType::getStorageSize() const
return max<u256>(1, size);
}
string ContractType::toString() const
{
return "contract " + m_contract.getName();
}
bool StructType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -226,15 +258,15 @@ bool StructType::operator==(Type const& _other) const
u256 StructType::getStorageSize() const
{
u256 size = 0;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
size += variable->getType()->getStorageSize();
for (pair<string, shared_ptr<Type const>> const& member: getMembers())
size += member.second->getStorageSize();
return max<u256>(1, size);
}
bool StructType::canLiveOutsideStorage() const
{
for (unsigned i = 0; i < getMemberCount(); ++i)
if (!getMemberByIndex(i).getType()->canLiveOutsideStorage())
for (pair<string, shared_ptr<Type const>> const& member: getMembers())
if (!member.second->canLiveOutsideStorage())
return false;
return true;
}
@ -244,33 +276,45 @@ string StructType::toString() const
return string("struct ") + m_struct.getName();
}
unsigned StructType::getMemberCount() const
{
return m_struct.getMembers().size();
}
unsigned StructType::memberNameToIndex(string const& _name) const
MemberList const& StructType::getMembers() const
{
vector<ASTPointer<VariableDeclaration>> const& members = m_struct.getMembers();
for (unsigned index = 0; index < members.size(); ++index)
if (members[index]->getName() == _name)
return index;
return unsigned(-1);
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
map<string, shared_ptr<Type const>> members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
members[variable->getName()] = variable->getType();
m_members.reset(new MemberList(members));
}
return *m_members;
}
VariableDeclaration const& StructType::getMemberByIndex(unsigned _index) const
u256 StructType::getStorageOffsetOfMember(string const& _name) const
{
return *m_struct.getMembers()[_index];
//@todo cache member offset?
u256 offset;
for (ASTPointer<VariableDeclaration> variable: m_struct.getMembers())
{
if (variable->getName() == _name)
return offset;
offset += variable->getType()->getStorageSize();
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
}
u256 StructType::getStorageOffsetOfMember(unsigned _index) const
FunctionType::FunctionType(FunctionDefinition const& _function)
{
//@todo cache member offset?
u256 offset;
// vector<ASTPointer<VariableDeclaration>> const& members = m_struct.getMembers();
for (unsigned index = 0; index < _index; ++index)
offset += getMemberByIndex(index).getType()->getStorageSize();
return offset;
TypePointers params;
TypePointers retParams;
params.reserve(_function.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
params.push_back(var->getType());
retParams.reserve(_function.getReturnParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getReturnParameters())
retParams.push_back(var->getType());
swap(params, m_parameterTypes);
swap(retParams, m_returnParameterTypes);
m_location = Location::INTERNAL;
}
bool FunctionType::operator==(Type const& _other) const
@ -278,13 +322,43 @@ bool FunctionType::operator==(Type const& _other) const
if (_other.getCategory() != getCategory())
return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
return other.m_function == m_function;
if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false;
auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(),
other.m_parameterTypes.cbegin(), typeCompare))
return false;
if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
other.m_returnParameterTypes.cbegin(), typeCompare))
return false;
return true;
}
string FunctionType::toString() const
{
//@todo nice string for function types
return "function(...)returns(...)";
string name = "function (";
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ",");
name += ") returns (";
for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it)
name += (*it)->toString() + (it + 1 == m_returnParameterTypes.end() ? "" : ",");
return name + ")";
}
unsigned FunctionType::getSizeOnStack() const
{
switch (m_location)
{
case Location::INTERNAL:
return 1;
case Location::EXTERNAL:
return 2;
default:
return 0;
}
}
bool MappingType::operator==(Type const& _other) const
@ -308,5 +382,55 @@ bool TypeType::operator==(Type const& _other) const
return *getActualType() == *other.getActualType();
}
MagicType::MagicType(MagicType::Kind _kind):
m_kind(_kind)
{
switch (m_kind)
{
case Kind::BLOCK:
m_members = MemberList({{"coinbase", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
{"timestamp", make_shared<IntegerType const>(256)},
{"prevhash", make_shared<IntegerType const>(256, IntegerType::Modifier::HASH)},
{"difficulty", make_shared<IntegerType const>(256)},
{"number", make_shared<IntegerType const>(256)},
{"gaslimit", make_shared<IntegerType const>(256)}});
break;
case Kind::MSG:
m_members = MemberList({{"sender", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
{"gas", make_shared<IntegerType const>(256)},
{"value", make_shared<IntegerType const>(256)}});
break;
case Kind::TX:
m_members = MemberList({{"origin", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
{"gasprice", make_shared<IntegerType const>(256)}});
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
}
}
bool MagicType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
MagicType const& other = dynamic_cast<MagicType const&>(_other);
return other.m_kind == m_kind;
}
string MagicType::toString() const
{
switch (m_kind)
{
case Kind::BLOCK:
return "block";
case Kind::MSG:
return "msg";
case Kind::TX:
return "tx";
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
}
}
}
}

160
libsolidity/Types.h

@ -24,6 +24,7 @@
#include <memory>
#include <string>
#include <map>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
#include <libsolidity/Exceptions.h>
@ -37,6 +38,34 @@ namespace solidity
// @todo realMxN, string<N>
class Type; // forward
using TypePointer = std::shared_ptr<Type const>;
using TypePointers = std::vector<TypePointer>;
/**
* List of members of a type.
*/
class MemberList
{
public:
using MemberMap = std::map<std::string, TypePointer>;
MemberList() {}
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
TypePointer getMemberType(std::string const& _name) const
{
auto it = m_memberTypes.find(_name);
return it != m_memberTypes.end() ? it->second : TypePointer();
}
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
private:
MemberMap m_memberTypes;
};
/**
* Abstract base class that forms the root of the type hierarchy.
*/
@ -45,7 +74,7 @@ class Type: private boost::noncopyable
public:
enum class Category
{
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC
};
///@{
@ -54,6 +83,7 @@ public:
static std::shared_ptr<Type> fromElementaryTypeName(Token::Value _typeToken);
static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName);
static std::shared_ptr<Type> fromFunction(FunctionDefinition const& _function);
/// @}
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
@ -78,8 +108,19 @@ public:
/// @returns number of bytes required to hold this value in storage.
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
virtual u256 getStorageSize() const { return 1; }
/// Returns true if the type can be stored in storage.
virtual bool canBeStored() const { return true; }
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
virtual bool canLiveOutsideStorage() const { return true; }
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
/// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; }
virtual unsigned getSizeOnStack() const { return 1; }
/// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; }
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
TypePointer getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); }
virtual std::string toString() const = 0;
virtual u256 literalValue(Literal const&) const
@ -87,6 +128,10 @@ public:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested "
"for type without literals."));
}
protected:
/// Convenience object used when returning an empty member list.
static const MemberList EmptyMemberList;
};
/**
@ -114,7 +159,10 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const { return m_bits / 8; }
virtual unsigned getCalldataEncodedSize() const override { return m_bits / 8; }
virtual bool isValueType() const override { return true; }
virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
virtual std::string toString() const override;
virtual u256 literalValue(Literal const& _literal) const override;
@ -127,6 +175,7 @@ public:
private:
int m_bits;
Modifier m_modifier;
static const MemberList AddressMemberList;
};
/**
@ -147,6 +196,7 @@ public:
}
virtual unsigned getCalldataEncodedSize() const { return 1; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "bool"; }
virtual u256 literalValue(Literal const& _literal) const override;
@ -160,10 +210,11 @@ class ContractType: public Type
public:
virtual Category getCategory() const override { return Category::CONTRACT; }
ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
/// Contracts can be converted to themselves and to addresses.
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const;
virtual std::string toString() const override { return "contract{...}"; }
virtual u256 getStorageSize() const override;
virtual std::string toString() const override;
private:
ContractDefinition const& m_contract;
@ -183,38 +234,57 @@ public:
}
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const;
virtual bool canLiveOutsideStorage() const;
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override;
virtual unsigned getSizeOnStack() const override { return 1; /*@todo*/ }
virtual std::string toString() const override;
unsigned getMemberCount() const;
/// Returns the index of the member with name @a _name or unsigned(-1) if it does not exist.
unsigned memberNameToIndex(std::string const& _name) const;
VariableDeclaration const& getMemberByIndex(unsigned _index) const;
u256 getStorageOffsetOfMember(unsigned _index) const;
virtual MemberList const& getMembers() const override;
u256 getStorageOffsetOfMember(std::string const& _name) const;
private:
StructDefinition const& m_struct;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
/**
* The type of a function, there is one distinct type per function definition.
* The type of a function, identified by its (return) parameter types.
* @todo the return parameters should also have names, i.e. return parameters should be a struct
* type.
*/
class FunctionType: public Type
{
public:
/// The meaning of the value(s) on the stack referencing the function:
/// INTERNAL: jump tag, EXTERNAL: contract address + function index,
/// OTHERS: special virtual function, nothing on the stack
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
virtual Category getCategory() const override { return Category::FUNCTION; }
FunctionType(FunctionDefinition const& _function): m_function(_function) {}
explicit FunctionType(FunctionDefinition const& _function);
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::INTERNAL):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
m_location(_location) {}
FunctionDefinition const& getFunction() const { return m_function; }
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
virtual bool canLiveOutsideStorage() const { return false; }
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override;
Location getLocation() const { return m_location; }
private:
FunctionDefinition const& m_function;
TypePointers m_parameterTypes;
TypePointers m_returnParameterTypes;
Location m_location;
};
/**
@ -224,19 +294,19 @@ class MappingType: public Type
{
public:
virtual Category getCategory() const override { return Category::MAPPING; }
MappingType(std::shared_ptr<Type const> _keyType, std::shared_ptr<Type const> _valueType):
MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
m_keyType(_keyType), m_valueType(_valueType) {}
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
virtual bool canLiveOutsideStorage() const { return false; }
virtual bool canLiveOutsideStorage() const override { return false; }
std::shared_ptr<Type const> getKeyType() const { return m_keyType; }
std::shared_ptr<Type const> getValueType() const { return m_valueType; }
TypePointer getKeyType() const { return m_keyType; }
TypePointer getValueType() const { return m_valueType; }
private:
std::shared_ptr<Type const> m_keyType;
std::shared_ptr<Type const> m_valueType;
TypePointer m_keyType;
TypePointer m_valueType;
};
/**
@ -250,8 +320,10 @@ public:
VoidType() {}
virtual std::string toString() const override { return "void"; }
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
virtual bool canLiveOutsideStorage() const { return false; }
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 0; }
};
/**
@ -262,19 +334,45 @@ class TypeType: public Type
{
public:
virtual Category getCategory() const override { return Category::TYPE; }
TypeType(std::shared_ptr<Type const> const& _actualType): m_actualType(_actualType) {}
TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
std::shared_ptr<Type const> const& getActualType() const { return m_actualType; }
TypePointer const& getActualType() const { return m_actualType; }
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const { return false; }
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
private:
std::shared_ptr<Type const> m_actualType;
TypePointer m_actualType;
};
/**
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference
* (it always references a global singleton by name).
*/
class MagicType: public Type
{
public:
enum class Kind { BLOCK, MSG, TX };
virtual Category getCategory() const override { return Category::MAGIC; }
MagicType(Kind _kind);
virtual bool operator==(Type const& _other) const;
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; }
virtual unsigned getSizeOnStack() const override { return 0; }
virtual MemberList const& getMembers() const override { return m_members; }
virtual std::string toString() const override;
private:
Kind m_kind;
MemberList m_members;
};
}
}

1
libweb3jsonrpc/WebThreeStubServer.cpp

@ -51,7 +51,6 @@ static Json::Value toJson(dev::eth::BlockInfo const& _bi)
res["transactionsRoot"] = toJS(_bi.transactionsRoot);
res["difficulty"] = toJS(_bi.difficulty);
res["number"] = (int)_bi.number;
res["minGasPrice"] = toJS(_bi.minGasPrice);
res["gasLimit"] = (int)_bi.gasLimit;
res["timestamp"] = (int)_bi.timestamp;
res["extraData"] = jsFromBinary(_bi.extraData);

6
libwhisper/WhisperHost.cpp

@ -156,6 +156,12 @@ void WhisperHost::uninstallWatch(unsigned _i)
m_filters.erase(fit);
}
void WhisperHost::doWork()
{
for (auto& i: peers())
i->cap<WhisperPeer>()->sendMessages();
}
void WhisperHost::cleanup()
{
// remove old messages.

9
libwhisper/WhisperHost.h

@ -27,6 +27,7 @@
#include <memory>
#include <utility>
#include <libdevcore/RLP.h>
#include <libdevcore/Worker.h>
#include <libdevcore/Guards.h>
#include <libdevcrypto/SHA3.h>
#include "Common.h"
@ -38,7 +39,7 @@ namespace dev
namespace shh
{
class WhisperHost: public HostCapability<WhisperPeer>, public Interface
class WhisperHost: public HostCapability<WhisperPeer>, public Interface, public Worker
{
friend class WhisperPeer;
@ -64,7 +65,13 @@ public:
void cleanup();
protected:
void doWork();
private:
virtual void onStarting() { startWorking(); }
virtual void onStopping() { stopWorking(); }
void streamMessage(h256 _m, RLPStream& _s) const;
void noteChanged(h256 _messageHash, h256 _filter);

14
libwhisper/WhisperPeer.cpp

@ -72,7 +72,6 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r)
for (auto i: _r)
if (n++)
host()->inject(Envelope(i), this);
sendMessages();
break;
}
default:
@ -97,10 +96,15 @@ void WhisperPeer::sendMessages()
}
}
if (!n)
// pause for a bit if no messages to send - this is horrible and broken.
// the message subsystem should really just keep pumping out messages while m_unseen.size() and there's bandwidth for them.
this_thread::sleep_for(chrono::milliseconds(20));
// the message subsystem should really just keep pumping out messages while m_unseen.size() and there's bandwidth for them.
auto diff = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - m_timer);
if (n || diff.count() > 0)
{
RLPStream s;
prep(s, MessagesPacket, n).appendRaw(amalg.out(), n);
sealAndSend(s);
m_timer = chrono::system_clock::now();
}
{
RLPStream s;

2
libwhisper/WhisperPeer.h

@ -68,6 +68,8 @@ private:
mutable dev::Mutex x_unseen;
std::map<unsigned, h256> m_unseen; ///< Rated according to what they want.
std::chrono::system_clock::time_point m_timer = std::chrono::system_clock::now();
};
}

1
mix/.gitignore

@ -0,0 +1 @@
*.pro

36
mix/ApplicationCtx.cpp

@ -0,0 +1,36 @@
/*
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 ApplicationCtx.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Provide an access to the current QQmlApplicationEngine which is used to add QML file on the fly.
* In the future this class can be extended to add more variable related to the context of the application.
*/
#include <QQmlApplicationEngine>
#include "ApplicationCtx.h"
using namespace dev::mix;
ApplicationCtx* ApplicationCtx::Instance = nullptr;
QQmlApplicationEngine* ApplicationCtx::appEngine()
{
return m_applicationEngine;
}
void ApplicationCtx::setApplicationContext(QQmlApplicationEngine* _engine)
{
if (Instance == nullptr)
Instance = new ApplicationCtx(_engine);
}

55
mix/ApplicationCtx.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 ApplicationCtx.h
* @author Yann yann@ethdev.com
* @date 2014
* Provide an access to the current QQmlApplicationEngine which is used to add QML file on the fly.
* In the future this class can be extended to add more variable related to the context of the application.
*/
#pragma once
#include <QQmlApplicationEngine>
namespace dev
{
namespace mix
{
class ApplicationCtx: public QObject
{
Q_OBJECT
public:
ApplicationCtx(QQmlApplicationEngine* _engine) { m_applicationEngine = _engine; }
~ApplicationCtx() { delete m_applicationEngine; }
static ApplicationCtx* getInstance() { return Instance; }
static void setApplicationContext(QQmlApplicationEngine* _engine);
QQmlApplicationEngine* appEngine();
private:
static ApplicationCtx* Instance;
QQmlApplicationEngine* m_applicationEngine;
public slots:
void quitApplication() { delete Instance; }
};
}
}

94
mix/CMakeLists.txt

@ -0,0 +1,94 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST)
include_directories(..)
if (APPLE)
# Add homebrew path for qt5
set(CMAKE_PREFIX_PATH /usr/local/opt/qt5)
include_directories(/usr/local/opt/qt5/include /usr/local/include)
elseif ("${TARGET_PLATFORM}" STREQUAL "w64")
set(SRC_LIST ${SRC_LIST} ../windows/qt_plugin_import.cpp)
include_directories(/usr/x86_64-w64-mingw32/include /usr/x86_64-w64-mingw32/include/QtCore /usr/x86_64-w64-mingw32/include/QtGui /usr/x86_64-w64-mingw32/include/QtQuick /usr/x86_64-w64-mingw32/include/QtQml /usr/x86_64-w64-mingw32/include/QtNetwork /usr/x86_64-w64-mingw32/include/QtWidgets /usr/x86_64-w64-mingw32/include/QtWebKit /usr/x86_64-w64-mingw32/include/QtWebKitWidgets)
elseif (UNIX)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ";$ENV{QTDIR}/lib/cmake")
endif ()
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Quick REQUIRED)
find_package(Qt5Qml REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5WebKit REQUIRED)
find_package(Qt5WebKitWidgets REQUIRED)
#qt5_wrap_ui(ui_Main.h Main.ui)
qt5_add_resources(UI_RESOURCES qml.qrc)
# Set name of binary and add_executable()
file(GLOB HEADERS "*.h")
if (APPLE)
set(EXECUTEABLE mix)
set(BIN_INSTALL_DIR ".")
set(DOC_INSTALL_DIR ".")
set(PROJECT_VERSION "${ETH_VERSION}")
set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}")
set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}")
set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTEABLE})
set(MACOSX_BUNDLE_ICON_FILE mix)
include(BundleUtilities)
add_executable(${EXECUTEABLE} MACOSX_BUNDLE ${SRC_LIST} ${HEADERS} ${UI_RESOURCES})
set_target_properties(${EXECUTEABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in")
SET_SOURCE_FILES_PROPERTIES(${EXECUTEABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS)
SET_SOURCE_FILES_PROPERTIES(${MACOSX_BUNDLE_ICON_FILE}.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
else ()
set(EXECUTEABLE mix)
add_executable(${EXECUTEABLE} ${SRC_LIST} ${HEADERS} ${UI_RESOURCES})
endif ()
qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets)
target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp ${CRYPTOPP_LS} serpent lll solidity evmcore devcore web3jsonrpc jsqrc)
if (APPLE)
# First have qt5 install plugins and frameworks
add_custom_command(TARGET ${EXECUTEABLE} POST_BUILD
COMMAND /usr/local/opt/qt5/bin/macdeployqt ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTEABLE}.app
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# This tool and next will inspect linked libraries in order to determine which dependencies are required
if (${CMAKE_CFG_INTDIR} STREQUAL ".")
set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXECUTEABLE}.app")
else ()
set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTEABLE}.app")
endif ()
install(CODE "
include(BundleUtilities)
set(BU_CHMOD_BUNDLE_ITEMS 1)
fixup_bundle(\"${APP_BUNDLE_PATH}\" \"${BUNDLELIBS}\" \"../libqethereum ../libethereum ../secp256k1\")
" COMPONENT RUNTIME )
# Cleanup duplicate libs from macdeployqt
install(CODE "
file(GLOB LINGER_RM \"${APP_BUNDLE_PATH}/Contents/Frameworks/*.dylib\")
if (LINGER_RM)
file(REMOVE \${LINGER_RM})
endif ()
")
elseif (UNIX)
else ()
target_link_libraries(${EXECUTEABLE} boost_system)
target_link_libraries(${EXECUTEABLE} boost_filesystem)
find_package(Threads REQUIRED)
target_link_libraries(${EXECUTEABLE} ${CMAKE_THREAD_LIBS_INIT})
install( TARGETS ${EXECUTEABLE} RUNTIME DESTINATION bin )
endif ()
qt5_use_modules(${EXECUTEABLE} Core Gui)

88
mix/CodeEditorExtensionManager.cpp

@ -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 CodeEditorExtensionMan.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QQuickItem>
#include <QGraphicsObject>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickTextDocument>
#include <libevm/VM.h>
#include "ConstantCompilationCtrl.h"
#include "ApplicationCtx.h"
#include "CodeEditorExtensionManager.h"
using namespace dev::mix;
CodeEditorExtensionManager::~CodeEditorExtensionManager()
{
m_features.clear();
}
void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
{
if (!_editor)
return;
try
{
QVariant doc = _editor->property("textDocument");
if (doc.canConvert<QQuickTextDocument*>())
{
QQuickTextDocument* qqdoc = doc.value<QQuickTextDocument*>();
if (qqdoc)
m_doc = qqdoc->textDocument();
}
}
catch (...)
{
qDebug() << "unable to load editor: ";
}
}
void CodeEditorExtensionManager::initExtensions()
{
//only one for now
std::shared_ptr<ConstantCompilationCtrl> constantCompilation = std::make_shared<ConstantCompilationCtrl>(m_doc);
if (constantCompilation.get()->contentUrl() != "")
{
try
{
constantCompilation.get()->addContentOn(m_tabView);
}
catch (...)
{
qDebug() << "Exception when adding content into view.";
return;
}
}
constantCompilation.get()->start();
m_features.append(constantCompilation);
}
void CodeEditorExtensionManager::setEditor(QQuickItem* _editor)
{
this->loadEditor(_editor);
this->initExtensions();
}
void CodeEditorExtensionManager::setTabView(QQuickItem* _tabView)
{
m_tabView = _tabView;
}

61
mix/CodeEditorExtensionManager.h

@ -0,0 +1,61 @@
/*
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 CodeEditorExtensionMan.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include "memory"
#include <QQuickItem>
#include <QTextDocument>
#include <QVector>
#include "ConstantCompilationCtrl.h"
namespace dev
{
namespace mix
{
class CodeEditorExtensionManager: public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickItem* editor MEMBER m_editor WRITE setEditor)
Q_PROPERTY(QQuickItem* tabView MEMBER m_tabView WRITE setTabView)
public:
CodeEditorExtensionManager() {}
~CodeEditorExtensionManager();
void initExtensions();
void setEditor(QQuickItem*);
void setTabView(QQuickItem*);
private:
QQuickItem* m_editor;
QVector<std::shared_ptr<ConstantCompilationCtrl>> m_features;
QQuickItem* m_tabView;
QTextDocument* m_doc;
void loadEditor(QQuickItem*);
};
}
}

97
mix/ConstantCompilationCtrl.cpp

@ -0,0 +1,97 @@
/*
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 ConstantCompilation.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QQuickItem>
#include <QtCore/QFileInfo>
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtCore/QtCore>
#include <QDebug>
#include "ConstantCompilationCtrl.h"
#include "ConstantCompilationModel.h"
using namespace dev::mix;
ConstantCompilationCtrl::ConstantCompilationCtrl(QTextDocument* _doc)
{
m_editor = _doc;
m_compilationModel = new ConstantCompilationModel();
}
ConstantCompilationCtrl::~ConstantCompilationCtrl()
{
delete m_compilationModel;
}
QString ConstantCompilationCtrl::contentUrl() const
{
return QStringLiteral("qrc:/qml/BasicContent.qml");
}
QString ConstantCompilationCtrl::title() const
{
return "compiler";
}
void ConstantCompilationCtrl::start() const
{
connect(m_editor, SIGNAL(contentsChange(int,int,int)), this, SLOT(compile()));
}
void ConstantCompilationCtrl::compile()
{
QString codeContent = m_editor->toPlainText().replace("\n", "");
if (codeContent.isEmpty())
{
resetOutPut();
return;
}
CompilerResult res = m_compilationModel->compile(m_editor->toPlainText());
writeOutPut(res);
}
void ConstantCompilationCtrl::resetOutPut()
{
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
status->setProperty("text", "");
content->setProperty("text", "");
}
void ConstantCompilationCtrl::writeOutPut(CompilerResult const& _res)
{
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
if (_res.success)
{
status->setProperty("text", "succeeded");
status->setProperty("color", "green");
content->setProperty("text", _res.hexCode);
qDebug() << QString("compile succeeded " + _res.hexCode);
}
else
{
status->setProperty("text", "failure");
status->setProperty("color", "red");
content->setProperty("text", _res.comment);
qDebug() << QString("compile failed " + _res.comment);
}
}

55
mix/ConstantCompilationCtrl.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 ConstantCompilation.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QTextDocument>
#include "ConstantCompilationModel.h"
#include "Extension.h"
namespace dev
{
namespace mix
{
class ConstantCompilationCtrl: public Extension
{
Q_OBJECT
public:
ConstantCompilationCtrl(QTextDocument*);
~ConstantCompilationCtrl();
void start() const override;
QString title() const override;
QString contentUrl() const override;
private:
QTextDocument* m_editor;
ConstantCompilationModel* m_compilationModel;
void writeOutPut(CompilerResult const&);
void resetOutPut();
public Q_SLOTS:
void compile();
};
}
}

61
mix/ConstantCompilationModel.cpp

@ -0,0 +1,61 @@
/*
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 ApplicationCtx.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QObject>
#include <libevm/VM.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include "ConstantCompilationModel.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
CompilerResult ConstantCompilationModel::compile(QString _code)
{
dev::solidity::CompilerStack compiler;
dev::bytes m_data;
CompilerResult res;
try
{
m_data = compiler.compile(_code.toStdString(), true);
res.success = true;
res.comment = "ok";
res.hexCode = QString::fromStdString(dev::eth::disassemble(m_data));
}
catch (dev::Exception const& _exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler.getScanner());
res.success = false;
res.comment = QString::fromStdString(error.str()).toHtmlEscaped();
res.hexCode = "";
}
catch (...)
{
res.success = false;
res.comment = "Uncaught exception.";
res.hexCode = "";
}
return res;
}

51
mix/ConstantCompilationModel.h

@ -0,0 +1,51 @@
/*
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 ApplicationCtx.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QObject>
namespace dev
{
namespace mix
{
struct CompilerResult
{
QString hexCode;
QString comment;
bool success;
};
class ConstantCompilationModel
{
public:
ConstantCompilationModel() {}
~ConstantCompilationModel() {}
CompilerResult compile(QString code);
};
}
}

38
mix/EthereumMacOSXBundleInfo.plist.in

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>

44
mix/Extension.cpp

@ -0,0 +1,44 @@
/*
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 Feature.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QMessageBox>
#include <QDebug>
#include <libevm/VM.h>
#include "Extension.h"
#include "ApplicationCtx.h"
using namespace dev;
using namespace dev::mix;
void Extension::addContentOn(QObject* _tabView)
{
if (contentUrl() == "")
return;
QVariant returnValue;
QQmlComponent* component = new QQmlComponent(
ApplicationCtx::getInstance()->appEngine(),
QUrl(this->contentUrl()), _tabView);
QMetaObject::invokeMethod(_tabView, "addTab",
Q_RETURN_ARG(QVariant, returnValue),
Q_ARG(QVariant, this->title()),
Q_ARG(QVariant, QVariant::fromValue(component)));
m_view = qvariant_cast<QObject*>(returnValue);
}

48
mix/Extension.h

@ -0,0 +1,48 @@
/*
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 Feature.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QApplication>
#include <QQmlComponent>
namespace dev
{
namespace mix
{
class Extension: public QObject
{
Q_OBJECT
public:
Extension() {}
virtual QString contentUrl() const { return ""; }
virtual QString title() const { return ""; }
virtual void start() const {}
void addContentOn(QObject* tabView);
protected:
QObject* m_view;
};
}
}

45
mix/MixApplication.cpp

@ -0,0 +1,45 @@
/*
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 main.cpp
* @author Yann yann@ethdev.com
* @date 2014
*/
#include <QDebug>
#include "MixApplication.h"
using namespace dev::mix;
MixApplication::MixApplication(int _argc, char *_argv[]): QApplication(_argc, _argv)
{
}
bool MixApplication::notify(QObject* _receiver, QEvent* _event)
{
try
{
return MixApplication::notify(_receiver, _event);
}
catch (std::exception& _ex)
{
qDebug() << "std::exception was caught " << _ex.what();
}
catch (...)
{
qDebug() << "uncaught exception ";
}
return false;
}

46
mix/MixApplication.h

@ -0,0 +1,46 @@
/*
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 main.cpp
* @author Yann yann@ethdev.com
* @date 2014
* This class will be use instead of QApplication to launch the application. the method 'notify' allows to catch all exceptions.
* Not use for now: TODO.
*/
#pragma once
#include <QApplication>
namespace dev
{
namespace mix
{
class MixApplication: public QApplication
{
Q_OBJECT
public:
MixApplication(int _argc, char* _argv[]);
virtual ~MixApplication() {}
virtual bool notify(QObject* _receiver, QEvent* _event);
};
}
}

40
mix/main.cpp

@ -0,0 +1,40 @@
/*
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 main.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include "CodeEditorExtensionManager.h"
#include "ApplicationCtx.h"
#include "MixApplication.h"
using namespace dev::mix;
int main(int _argc, char *_argv[])
{
QApplication app(_argc, _argv);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
QQmlApplicationEngine* engine = new QQmlApplicationEngine();
ApplicationCtx::setApplicationContext(engine);
QObject::connect(&app, SIGNAL(lastWindowClosed()), ApplicationCtx::getInstance(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
engine->load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
return app.exec();
}

8
mix/qml.qrc

@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file>qml/BasicContent.qml</file>
<file>qml/main.qml</file>
<file>qml/MainContent.qml</file>
<file>qml/TabStyle.qml</file>
</qresource>
</RCC>

35
mix/qml/BasicContent.qml

@ -0,0 +1,35 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
Rectangle {
anchors.fill: parent
width: parent.width
height: parent.height
color: "lightgray"
Text {
font.pointSize: 7
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 3
anchors.leftMargin: 3
height: 9
font.family: "Sego UI light"
objectName: "status"
id: status
}
TextArea {
readOnly: true
anchors.left: parent.left
anchors.leftMargin: 10
anchors.top: status.bottom
anchors.topMargin: 3
font.pointSize: 7
font.family: "Sego UI light"
height: parent.height * 0.8
width: parent.width - 20
wrapMode: Text.Wrap
backgroundVisible: false
objectName: "content"
id: content
}
}

54
mix/qml/MainContent.qml

@ -0,0 +1,54 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0
Rectangle {
anchors.fill: parent
height: parent.height
width: parent.width;
id:root
SplitView {
anchors.fill: parent
orientation: Qt.Vertical
Rectangle {
anchors.top: parent.top
id: contentView
width: parent.width
height: parent.height * 0.7
TextArea {
id: codeEditor
height: parent.height
font.family: "Verdana"
font.pointSize: 9
width: parent.width
anchors.centerIn: parent
tabChangesFocus: false
Keys.onPressed: {
if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true;
}
}
}
}
Rectangle {
anchors.bottom: parent.bottom
id: contextualView
width: parent.width
Layout.minimumHeight: 20
height: parent.height * 0.3
TabView {
id: contextualTabs
antialiasing: true
anchors.fill: parent
style: TabStyle {}
}
}
CodeEditorExtensionManager{
tabView: contextualTabs
editor: codeEditor
}
}
}

23
mix/qml/TabStyle.qml

@ -0,0 +1,23 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
TabViewStyle {
frameOverlap: 1
tabBar: Rectangle {
color: "lightgray"
}
tab: Rectangle {
color: "lightsteelblue"
implicitWidth: Math.max(text.width + 4, 80)
implicitHeight: 20
radius: 2
Text {
id: text
anchors.centerIn: parent
text: styleData.title
color: styleData.selected ? "white" : "black"
}
}
frame: Rectangle { color: "steelblue" }
}

24
mix/qml/main.qml

@ -0,0 +1,24 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0
ApplicationWindow {
visible: true
width: 1000
height: 480
minimumWidth: 400
minimumHeight: 300
title: qsTr("mix")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
MainContent{
}
}

30
neth/main.cpp

@ -26,16 +26,12 @@
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim_all.hpp>
#if ETH_JSONRPC
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include <libdevcrypto/FileSystem.h>
#include <libevmcore/Instruction.h>
#include <libethereum/All.h>
#if ETH_JSONRPC
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <libweb3jsonrpc/abstractwebthreestubserver.h>
#include <libdevcore/CommonJS.h>
#include <libweb3jsonrpc/CorsHttpServer.h>
#endif
#include <libwebthree/WebThree.h>
#include "BuildInfo.h"
@ -475,12 +471,12 @@ int main(int argc, char** argv)
c.startMining();
#if ETH_JSONRPC
auto_ptr<WebThreeStubServer> jsonrpcServer;
shared_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<jsonrpc::AbstractServerConnector> jsonrpcConnector;
if (jsonrpc > -1)
{
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc));
jsonrpcServer = auto_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, {us}));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
@ -555,7 +551,7 @@ int main(int argc, char** argv)
if (jsonrpc < 0)
jsonrpc = 8080;
jsonrpcConnector = unique_ptr<jsonrpc::AbstractServerConnector>(new jsonrpc::HttpServer(jsonrpc));
jsonrpcServer = auto_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, {us}));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector<KeyPair>({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
@ -606,7 +602,7 @@ int main(int argc, char** argv)
vector<string> l;
l.push_back("Amount");
stringstream label;
label << "Gas price (" << info.minGasPrice << ")";
label << "Gas price";
l.push_back(label.str());
l.push_back("Gas");
vector<string> b;
@ -649,14 +645,12 @@ int main(int argc, char** argv)
ssbd << bbd;
cnote << ssbd.str();
int ssize = fields[4].length();
u256 minGas = (u256)Client::txGas(data.size(), 0);
u256 minGas = (u256)Client::txGas(data, 0);
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else if (gasPrice < info.minGasPrice)
cwarn << "Minimum gas price is" << info.minGasPrice;
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
@ -705,9 +699,9 @@ int main(int argc, char** argv)
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(0, 0);
u256 minGas = (u256)Client::txGas(bytes(), 0);
Address dest = h160(fromHex(fields[0]));
c.transact(us.secret(), amount, dest, bytes(), minGas, info.minGasPrice);
c.transact(us.secret(), amount, dest, bytes(), minGas);
}
}
}
@ -721,7 +715,7 @@ int main(int argc, char** argv)
vector<string> l;
l.push_back("Endowment");
stringstream label;
label << "Gas price (" << info.minGasPrice << ")";
label << "Gas price";
l.push_back(label.str());
l.push_back("Gas");
vector<string> b;
@ -766,16 +760,14 @@ int main(int argc, char** argv)
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Client::txGas(init.size(), 0);
u256 minGas = (u256)Client::txGas(init, 0);
if (endowment < 0)
cwarn << "Invalid endowment";
else if (gasPrice < info.minGasPrice)
cwarn << "Minimum gas price is" << info.minGasPrice;
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else
{
c.transact(us.secret(), endowment, init, gas, gasPrice);
c.transact(us.secret(), endowment, init, gas);
}
}
}

73
test/TestHelper.cpp

@ -72,6 +72,7 @@ ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller): m_TestObject(_o
if (!isFiller)
{
importState(_o["post"].get_obj(), m_statePost);
m_environment.sub.logs = importLog(_o["logs"].get_obj());
}
}
@ -148,6 +149,9 @@ void ImportTest::exportTest(bytes _output, State& _statePost)
// export output
m_TestObject["out"] = "0x" + toHex(_output);
// export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());
// export post state
json_spirit::mObject postState;
@ -255,6 +259,44 @@ bytes importCode(json_spirit::mObject& _o)
return code;
}
LogEntries importLog(json_spirit::mObject& _o)
{
LogEntries logEntries;
for (auto const& l: _o)
{
json_spirit::mObject o = l.second.get_obj();
// cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest)
assert(o.count("address") > 0);
assert(o.count("topics") > 0);
assert(o.count("data") > 0);
LogEntry log;
log.address = Address(o["address"].get_str());
for (auto const& t: o["topics"].get_array())
log.topics.push_back(h256(t.get_str()));
log.data = importData(o);
logEntries.push_back(log);
}
return logEntries;
}
json_spirit::mObject exportLog(eth::LogEntries _logs)
{
json_spirit::mObject ret;
if (_logs.size() == 0) return ret;
for (LogEntry const& l: _logs)
{
json_spirit::mObject o;
o["address"] = toString(l.address);
json_spirit::mArray topics;
for (auto const& t: l.topics)
topics.push_back(toString(t));
o["topics"] = topics;
o["data"] = "0x" + toHex(l.data);
ret[toString(l.bloom())] = o;
}
return ret;
}
void checkOutput(bytes const& _output, json_spirit::mObject& _o)
{
int j = 0;
@ -287,6 +329,18 @@ void checkStorage(map<u256, u256> _expectedStore, map<u256, u256> _resultStore,
}
}
void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs)
{
BOOST_REQUIRE_EQUAL(_resultLogs.size(), _expectedLogs.size());
for (size_t i = 0; i < _resultLogs.size(); ++i)
{
BOOST_CHECK_EQUAL(_resultLogs[i].address, _expectedLogs[i].address);
BOOST_CHECK_EQUAL(_resultLogs[i].topics, _expectedLogs[i].topics);
BOOST_CHECK(_resultLogs[i].data == _expectedLogs[i].data);
}
}
std::string getTestPath()
{
string testPath;
@ -310,12 +364,13 @@ void userDefinedTest(string testTypeFlag, std::function<void(json_spirit::mValue
string arg = boost::unit_test::framework::master_test_suite().argv[i];
if (arg == testTypeFlag)
{
if (i + 1 >= boost::unit_test::framework::master_test_suite().argc)
if (boost::unit_test::framework::master_test_suite().argc <= i + 2)
{
cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " <filename>\n";
cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " <filename> <testname>\n";
return;
}
string filename = boost::unit_test::framework::master_test_suite().argv[i + 1];
string testname = boost::unit_test::framework::master_test_suite().argv[i + 2];
int currentVerbosity = g_logVerbosity;
g_logVerbosity = 12;
try
@ -325,7 +380,19 @@ void userDefinedTest(string testTypeFlag, std::function<void(json_spirit::mValue
string s = asString(contents(filename));
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. ");
json_spirit::read_string(s, v);
doTests(v, false);
json_spirit::mObject oSingleTest;
json_spirit::mObject::const_iterator pos = v.get_obj().find(testname);
if (pos == v.get_obj().end())
{
cnote << "Could not find test: " << testname << " in " << filename << "\n";
return;
}
else
oSingleTest[pos->first] = pos->second;
json_spirit::mValue v_singleTest(oSingleTest);
doTests(v_singleTest, false);
}
catch (Exception const& _e)
{

4
test/TestHelper.h

@ -25,6 +25,7 @@
#include <boost/test/unit_test.hpp>
#include "JsonSpiritHeaders.h"
#include <libethereum/State.h>
#include <libevm/ExtVMFace.h>
namespace dev
{
@ -67,8 +68,11 @@ u256 toInt(json_spirit::mValue const& _v);
byte toByte(json_spirit::mValue const& _v);
bytes importCode(json_spirit::mObject& _o);
bytes importData(json_spirit::mObject& _o);
eth::LogEntries importLog(json_spirit::mObject& _o);
json_spirit::mObject exportLog(eth::LogEntries _logs);
void checkOutput(bytes const& _output, json_spirit::mObject& _o);
void checkStorage(std::map<u256, u256> _expectedStore, std::map<u256, u256> _resultStore, Address _expectedAddr);
void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs);
void executeTests(const std::string& _name, const std::string& _testPathAppendix, std::function<void(json_spirit::mValue&, bool)> doTests);
std::string getTestPath();
void userDefinedTest(std::string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests);

25
test/createRandomTest.cpp

@ -121,14 +121,14 @@ void doMyTests(json_spirit::mValue& v)
{
for (auto& i: v.get_obj())
{
cnote << i.first;
mObject& o = i.second.get_obj();
assert(o.count("env") > 0);
assert(o.count("pre") > 0);
assert(o.count("exec") > 0);
eth::VM vm;
test::FakeExtVM fev;
dev::test::FakeExtVM fev;
fev.importEnv(o["env"].get_obj());
fev.importState(o["pre"].get_obj());
@ -141,17 +141,20 @@ void doMyTests(json_spirit::mValue& v)
fev.code = fev.thisTxCode;
}
vm.reset(fev.gas);
bytes output;
eth::VM vm(fev.gas);
u256 gas;
bool vmExceptionOccured = false;
try
{
output = vm.go(fev).toBytes();
output = vm.go(fev, fev.simpleTrace()).toBytes();
gas = vm.gas();
}
catch (eth::VMException const& _e)
{
cnote << "VM did throw an exception: " << diagnostic_information(_e);
gas = 0;
vmExceptionOccured = true;
}
catch (Exception const& _e)
{
@ -180,9 +183,13 @@ void doMyTests(json_spirit::mValue& v)
o["env"] = mValue(fev.exportEnv());
o["exec"] = mValue(fev.exportExec());
o["post"] = mValue(fev.exportState());
o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas);
if (!vmExceptionOccured)
{
o["post"] = mValue(fev.exportState());
o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas);
o["logs"] = mValue(test::exportLog(fev.sub.logs));
}
}
}

4
test/solidityCompiler.cpp

@ -48,11 +48,11 @@ bytes compileContract(const string& _sourceCode)
Parser parser;
ASTPointer<ContractDefinition> contract;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver;
NameAndTypeResolver resolver({});
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
Compiler compiler;
compiler.compileContract(*contract);
compiler.compileContract(*contract, {});
// debug
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode();

196
test/solidityEndToEndTest.cpp

@ -27,12 +27,14 @@
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h>
#include <libdevcrypto/SHA3.h>
using namespace std;
namespace dev
{
/// Provider another overload for toBigEndian to encode arguments and return values.
/// Provides additional overloads for toBigEndian to encode arguments and return values.
inline bytes toBigEndian(byte _value) { return bytes({_value}); }
inline bytes toBigEndian(bool _value) { return bytes({byte(_value)}); }
namespace solidity
@ -45,17 +47,17 @@ class ExecutionFramework
public:
ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRun(string const& _sourceCode)
bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0)
{
bytes code = dev::solidity::CompilerStack::staticCompile(_sourceCode);
sendMessage(code, true);
sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty());
return m_output;
}
bytes const& callContractFunction(byte _index, bytes const& _data = bytes())
bytes const& callContractFunction(byte _index, bytes const& _data = bytes(), u256 const& _value = 0)
{
sendMessage(bytes(1, _index) + _data, false);
sendMessage(bytes(1, _index) + _data, false, _value);
return m_output;
}
@ -111,11 +113,11 @@ private:
return toBigEndian(_cppFunction(_arguments...));
}
void sendMessage(bytes const& _data, bool _isCreation)
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
{
eth::Executive executive(m_state);
eth::Transaction t = _isCreation ? eth::Transaction(0, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec())
: eth::Transaction(0, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec())
: eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
bytes transactionRLP = t.rlp();
try
{
@ -125,7 +127,7 @@ private:
catch (...) {}
if (_isCreation)
{
BOOST_REQUIRE(!executive.create(Address(), 0, m_gasPrice, m_gas, &_data, Address()));
BOOST_REQUIRE(!executive.create(Address(), _value, m_gasPrice, m_gas, &_data, Address()));
m_contractAddress = executive.newAddress();
BOOST_REQUIRE(m_contractAddress);
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
@ -133,7 +135,7 @@ private:
else
{
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), 0, m_gasPrice, &_data, m_gas, Address()));
BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), _value, m_gasPrice, &_data, m_gas, Address()));
}
BOOST_REQUIRE(executive.go());
executive.finalize();
@ -700,6 +702,34 @@ BOOST_AUTO_TEST_CASE(structs)
BOOST_CHECK(callContractFunction(0) == bytes({0x01}));
}
BOOST_AUTO_TEST_CASE(struct_reference)
{
char const* sourceCode = "contract test {\n"
" struct s2 {\n"
" uint32 z;\n"
" mapping(uint8 => s2) recursive;\n"
" }\n"
" s2 data;\n"
" function check() returns (bool ok) {\n"
" return data.z == 2 && \n"
" data.recursive[0].z == 3 && \n"
" data.recursive[0].recursive[1].z == 0 && \n"
" data.recursive[0].recursive[0].z == 1;\n"
" }\n"
" function set() {\n"
" data.z = 2;\n"
" var map = data.recursive;\n"
" s2 inner = map[0];\n"
" inner.z = 3;\n"
" inner.recursive[0].z = inner.recursive[1].z + 1;\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0) == bytes({0x00}));
BOOST_CHECK(callContractFunction(1) == bytes());
BOOST_CHECK(callContractFunction(0) == bytes({0x01}));
}
BOOST_AUTO_TEST_CASE(constructor)
{
char const* sourceCode = "contract test {\n"
@ -722,6 +752,152 @@ BOOST_AUTO_TEST_CASE(constructor)
testSolidityAgainstCpp(0, get, u256(7));
}
BOOST_AUTO_TEST_CASE(balance)
{
char const* sourceCode = "contract test {\n"
" function getBalance() returns (uint256 balance) {\n"
" return address(this).balance;\n"
" }\n"
"}\n";
compileAndRun(sourceCode, 23);
BOOST_CHECK(callContractFunction(0) == toBigEndian(u256(23)));
}
BOOST_AUTO_TEST_CASE(blockchain)
{
char const* sourceCode = "contract test {\n"
" function someInfo() returns (uint256 value, address coinbase, uint256 blockNumber) {\n"
" value = msg.value;\n"
" coinbase = block.coinbase;\n"
" blockNumber = block.number;\n"
" }\n"
"}\n";
compileAndRun(sourceCode, 27);
BOOST_CHECK(callContractFunction(0, bytes{0}, u256(28)) == toBigEndian(u256(28)) + bytes(20, 0) + toBigEndian(u256(1)));
}
BOOST_AUTO_TEST_CASE(function_types)
{
char const* sourceCode = "contract test {\n"
" function a(bool selector) returns (uint b) {\n"
" var f = fun1;\n"
" if (selector) f = fun2;\n"
" return f(9);\n"
" }\n"
" function fun1(uint x) returns (uint b) {\n"
" return 11;\n"
" }\n"
" function fun2(uint x) returns (uint b) {\n"
" return 12;\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes{0}) == toBigEndian(u256(11)));
BOOST_CHECK(callContractFunction(0, bytes{1}) == toBigEndian(u256(12)));
}
BOOST_AUTO_TEST_CASE(send_ether)
{
char const* sourceCode = "contract test {\n"
" function a(address addr, uint amount) returns (uint ret) {\n"
" addr.send(amount);\n"
" return address(this).balance;\n"
" }\n"
"}\n";
u256 amount(130);
compileAndRun(sourceCode, amount + 1);
u160 address(23);
BOOST_CHECK(callContractFunction(0, address, amount) == toBigEndian(u256(1)));
BOOST_CHECK_EQUAL(m_state.balance(address), amount);
}
BOOST_AUTO_TEST_CASE(suicide)
{
char const* sourceCode = "contract test {\n"
" function a(address receiver) returns (uint ret) {\n"
" suicide(receiver);\n"
" return 10;\n"
" }\n"
"}\n";
u256 amount(130);
compileAndRun(sourceCode, amount);
u160 address(23);
BOOST_CHECK(callContractFunction(0, address) == bytes());
BOOST_CHECK(!m_state.addressHasCode(m_contractAddress));
BOOST_CHECK_EQUAL(m_state.balance(address), amount);
}
BOOST_AUTO_TEST_CASE(sha3)
{
char const* sourceCode = "contract test {\n"
" function a(hash input) returns (hash sha3hash) {\n"
" return sha3(input);\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
auto f = [&](u256 const& _x) -> u256
{
return dev::sha3(toBigEndian(_x));
};
testSolidityAgainstCpp(0, f, u256(4));
testSolidityAgainstCpp(0, f, u256(5));
testSolidityAgainstCpp(0, f, u256(-1));
}
BOOST_AUTO_TEST_CASE(sha256)
{
char const* sourceCode = "contract test {\n"
" function a(hash input) returns (hash sha256hash) {\n"
" return sha256(input);\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
auto f = [&](u256 const& _input) -> u256
{
h256 ret;
dev::sha256(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32));
return ret;
};
testSolidityAgainstCpp(0, f, u256(4));
testSolidityAgainstCpp(0, f, u256(5));
testSolidityAgainstCpp(0, f, u256(-1));
}
BOOST_AUTO_TEST_CASE(ripemd)
{
char const* sourceCode = "contract test {\n"
" function a(hash input) returns (hash sha256hash) {\n"
" return ripemd160(input);\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
auto f = [&](u256 const& _input) -> u256
{
h256 ret;
dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32));
return u256(ret) >> (256 - 160);
};
testSolidityAgainstCpp(0, f, u256(4));
testSolidityAgainstCpp(0, f, u256(5));
testSolidityAgainstCpp(0, f, u256(-1));
}
BOOST_AUTO_TEST_CASE(ecrecover)
{
char const* sourceCode = "contract test {\n"
" function a(hash h, uint8 v, hash r, hash s) returns (address addr) {\n"
" return ecrecover(h, v, r, s);\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
u256 h("0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c");
byte v = 28;
u256 r("0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f");
u256 s("0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549");
u160 addr("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr));
}
BOOST_AUTO_TEST_SUITE_END()
}

2
test/solidityExpressionCompiler.cpp

@ -88,7 +88,7 @@ bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _
Parser parser;
ASTPointer<ContractDefinition> contract;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver;
NameAndTypeResolver resolver({});
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
FirstExpressionExtractor extractor(*contract);
BOOST_REQUIRE(extractor.getExpression() != nullptr);

52
test/solidityNameAndTypeResolution.cpp

@ -43,7 +43,7 @@ void parseTextAndResolveNames(std::string const& _source)
Parser parser;
ASTPointer<ContractDefinition> contract = parser.parse(
std::make_shared<Scanner>(CharStream(_source)));
NameAndTypeResolver resolver;
NameAndTypeResolver resolver({});
resolver.resolveNamesAndTypes(*contract);
}
}
@ -224,6 +224,56 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion)
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(balance)
{
char const* text = "contract test {\n"
" function fun() {\n"
" uint256 x = address(0).balance;\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(balance_invalid)
{
char const* text = "contract test {\n"
" function fun() {\n"
" address(0).balance = 7;\n"
" }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(assignment_to_mapping)
{
char const* text = "contract test {\n"
" struct str {\n"
" mapping(uint=>uint) map;\n"
" }\n"
" str data;"
" function fun() {\n"
" var a = data.map;\n"
" data.map = a;\n"
" }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(assignment_to_struct)
{
char const* text = "contract test {\n"
" struct str {\n"
" mapping(uint=>uint) map;\n"
" }\n"
" str data;"
" function fun() {\n"
" var a = data;\n"
" data = a;\n"
" }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END()
}

161
test/solidityParser.cpp

@ -37,13 +37,14 @@ namespace test
namespace
{
ASTPointer<ASTNode> parseText(std::string const& _source)
ASTPointer<ContractDefinition> parseText(std::string const& _source)
{
Parser parser;
return parser.parse(std::make_shared<Scanner>(CharStream(_source)));
}
}
BOOST_AUTO_TEST_SUITE(SolidityParser)
BOOST_AUTO_TEST_CASE(smoke_test)
@ -91,6 +92,164 @@ BOOST_AUTO_TEST_CASE(single_function_param)
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(function_natspec_documentation)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" /// This is a test function\n"
" function functionName(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is a test function");
}
BOOST_AUTO_TEST_CASE(function_normal_comments)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" // We won't see this comment\n"
" function functionName(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_MESSAGE(function->getDocumentation() == nullptr,
"Should not have gotten a Natspect comment for this function");
}
BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" /// This is test function 1\n"
" function functionName1(hash hashin) returns (hash hashout) {}\n"
" /// This is test function 2\n"
" function functionName2(hash hashin) returns (hash hashout) {}\n"
" // nothing to see here\n"
" function functionName3(hash hashin) returns (hash hashout) {}\n"
" /// This is test function 4\n"
" function functionName4(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 1");
BOOST_REQUIRE_NO_THROW(function = functions.at(1));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 2");
BOOST_REQUIRE_NO_THROW(function = functions.at(2));
BOOST_CHECK_MESSAGE(function->getDocumentation() == nullptr,
"Should not have gotten natspec comment for functionName3()");
BOOST_REQUIRE_NO_THROW(function = functions.at(3));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 4");
}
BOOST_AUTO_TEST_CASE(multiline_function_documentation)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" /// This is a test function\n"
" /// and it has 2 lines\n"
" function functionName1(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(),
" This is a test function\n"
" and it has 2 lines");
}
BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" /// fun1 description\n"
" function fun1(uint256 a) {\n"
" var b;\n"
" /// I should not interfere with actual natspec comments\n"
" uint256 c;\n"
" mapping(address=>hash) d;\n"
" string name = \"Solidity\";"
" }\n"
" uint256 stateVar;\n"
" /// This is a test function\n"
" /// and it has 2 lines\n"
" function fun(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " fun1 description");
BOOST_REQUIRE_NO_THROW(function = functions.at(1));
BOOST_CHECK_EQUAL(*function->getDocumentation(),
" This is a test function\n"
" and it has 2 lines");
}
BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" function ///I am in the wrong place \n"
" fun1(uint256 a) {\n"
" var b;\n"
" /// I should not interfere with actual natspec comments\n"
" uint256 c;\n"
" mapping(address=>hash) d;\n"
" string name = \"Solidity\";"
" }\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_MESSAGE(!function->getDocumentation(),
"Shouldn't get natspec docstring for this function");
}
BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" function fun1(uint256 a) {\n"
" /// I should have been above the function signature\n"
" var b;\n"
" /// I should not interfere with actual natspec comments\n"
" uint256 c;\n"
" mapping(address=>hash) d;\n"
" string name = \"Solidity\";"
" }\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_MESSAGE(!function->getDocumentation(),
"Shouldn't get natspec docstring for this function");
}
BOOST_AUTO_TEST_CASE(struct_definition)
{
char const* text = "contract test {\n"

37
test/stExampleFiller.json

@ -0,0 +1,37 @@
{
"add11" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "10000",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
}
}

68
test/stPreCompiledContractsFiller.json

@ -33,6 +33,40 @@
}
},
"CallEcrecover0_completeReturnValue": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "10000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "20000000",
"nonce" : 0,
"code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 128 32) [[ 0 ]] (MLOAD 128) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "365224",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"CallEcrecover0_gas500": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
@ -305,6 +339,40 @@
}
},
"CallSha256_1_nonzeroValue": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "10000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "20000000",
"nonce" : 0,
"code" : "{ [[ 2 ]] (CALL 500 2 0x13 0 0 0 32) [[ 0 ]] (MLOAD 0)}",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "365224",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"CallSha256_2": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

41
test/stSpecialTestFiller.json

@ -0,0 +1,41 @@
{
"makeMoney" : {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) (CALL 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec 0xaaaaaaaaace5edbc8e2a8697c15331677e6ebf0b 23 0 0 0 0) }",
"storage": {}
},
"aaaaaaaaace5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "0x600160015532600255",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "850",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "10",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
}
}

12
test/stSystemOperationsTestFiller.json

@ -12,7 +12,7 @@
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 28) }",
"code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 28) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -46,7 +46,7 @@
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 1000 4 28) }",
"code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 1000 4 28) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -80,7 +80,7 @@
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 23 0xfffffffffff 28) }",
"code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 0xfffffffffff 28) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -114,7 +114,7 @@
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 0xfffffffffff) }",
"code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 0xfffffffffff) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -195,7 +195,7 @@
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23",
"code" : "0x6001600155603760005360026000f2",
"code" : "0x6001600155603760005360026000f3",
"nonce" : "0",
"storage" : {
}
@ -321,7 +321,7 @@
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23",
"code" : "0x6001600155603760005360026000f2",
"code" : "0x6001600155603760005360026000f3",
"nonce" : "0",
"storage" : {
}

8
test/state.cpp

@ -81,6 +81,9 @@ void doStateTests(json_spirit::mValue& v, bool _fillin)
// check output
checkOutput(output, o);
// check logs
checkLog(theState.pending().size() ? theState.log(0) : LogEntries(), importer.m_environment.sub.logs);
// check addresses
auto expectedAddrs = importer.m_statePost.addresses();
auto resultAddrs = theState.addresses();
@ -122,6 +125,11 @@ BOOST_AUTO_TEST_CASE(stPreCompiledContracts)
dev::test::executeTests("stPreCompiledContracts", "/StateTests", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stSpecialTest)
{
dev::test::executeTests("stSpecialTest", "/StateTests", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stCreateTest)
{
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)

47
test/vm.cpp

@ -120,6 +120,41 @@ void FakeExtVM::importEnv(mObject& _o)
currentBlock.coinbaseAddress = Address(_o["currentCoinbase"].get_str());
}
mObject FakeExtVM::exportLog()
{
mObject ret;
for (LogEntry const& l: sub.logs)
{
mObject o;
o["address"] = toString(l.address);
mArray topics;
for (auto const& t: l.topics)
topics.push_back(toString(t));
o["topics"] = topics;
o["data"] = "0x" + toHex(l.data);
ret[toString(l.bloom())] = o;
}
return ret;
}
void FakeExtVM::importLog(mObject& _o)
{
for (auto const& l: _o)
{
mObject o = l.second.get_obj();
// cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest)
assert(o.count("address") > 0);
assert(o.count("topics") > 0);
assert(o.count("data") > 0);
LogEntry log;
log.address = Address(o["address"].get_str());
for (auto const& t: o["topics"].get_array())
log.topics.push_back(h256(t.get_str()));
log.data = importData(o);
sub.logs.push_back(log);
}
}
mObject FakeExtVM::exportState()
{
mObject ret;
@ -303,7 +338,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
bool vmExceptionOccured = false;
try
{
output = vm.go(fev, fev.simpleTrace()).toVector();
output = vm.go(fev, fev.simpleTrace()).toBytes();
gas = vm.gas();
}
catch (VMException const& _e)
@ -349,6 +384,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas);
o["logs"] = mValue(exportLog(fev.sub.logs));
}
}
else
@ -361,10 +397,12 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
BOOST_REQUIRE(o.count("callcreates") > 0);
BOOST_REQUIRE(o.count("out") > 0);
BOOST_REQUIRE(o.count("gas") > 0);
BOOST_REQUIRE(o.count("logs") > 0);
dev::test::FakeExtVM test;
test.importState(o["post"].get_obj());
test.importCallCreates(o["callcreates"].get_array());
test.sub.logs = importLog(o["logs"].get_obj());
checkOutput(output, o);
@ -392,6 +430,8 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
BOOST_CHECK(test.callcreates == fev.callcreates);
checkLog(fev.sub.logs, test.sub.logs);
}
else // Exception expected
BOOST_CHECK(vmExceptionOccured);
@ -443,6 +483,11 @@ BOOST_AUTO_TEST_CASE(vmPushDupSwapTest)
dev::test::executeTests("vmPushDupSwapTest", "/VMTests", dev::test::doVMTests);
}
BOOST_AUTO_TEST_CASE(vmLogTest)
{
dev::test::executeTests("vmLogTest", "/VMTests", dev::test::doVMTests);
}
BOOST_AUTO_TEST_CASE(vmRandom)
{
string testPath = getTestPath();

2
test/vm.h

@ -72,6 +72,8 @@ public:
void importExec(json_spirit::mObject& _o);
json_spirit::mArray exportCallCreates();
void importCallCreates(json_spirit::mArray& _callcreates);
json_spirit::mObject exportLog();
void importLog(json_spirit::mObject& _o);
eth::OnOpFunc simpleTrace();

4
test/vmIOandFlowOperationsTestFiller.json

@ -55,7 +55,7 @@
}
},
"dupAt51doesNotExistAnymore": {
"dupAt51becameMload": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
@ -83,7 +83,7 @@
}
},
"swapAt52doesNotExistAnymore": {
"swapAt52becameMstore": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",

1266
test/vmLogTestFiller.json

File diff suppressed because it is too large

31
test/vmPushDupSwapTestFiller.json

@ -924,7 +924,7 @@
}
},
"push32error": {
"push32AndSuicide": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
@ -952,6 +952,35 @@
}
},
"push32FillUpInputWithZerosAtTheEnd": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "0x7fff10112233445566778899aabbccddeeff00112233445566778899aabbccdd",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"dup1": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

Loading…
Cancel
Save