diff --git a/CMakeLists.txt b/CMakeLists.txt
index 73dc83d80..81b279761 100644
--- a/CMakeLists.txt
+++ b/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)
+
diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index 1df9c6e91..0e7b0799b 100644
--- a/alethzero/MainWin.cpp
+++ b/alethzero/MainWin.cpp
@@ -1259,9 +1259,11 @@ void Main::on_blocks_currentItemChanged()
s << "
D/TD: 2^" << log2((double)info.difficulty) << "/2^" << log2((double)details.totalDifficulty) << "";
s << " Children: " << details.children.size() << "";
s << "
Gas used/limit: " << info.gasUsed << "/" << info.gasLimit << "";
- s << " Minimum gas price: " << formatBalance(info.minGasPrice) << "";
s << "
Coinbase: " << pretty(info.coinbaseAddress).toHtmlEscaped().toStdString() << " " << info.coinbaseAddress;
s << "
Nonce: " << info.nonce << "";
+ s << "
Hash w/o nonce: " << info.headerHashWithoutNonce() << "";
+ s << "
Difficulty: " << info.difficulty << "";
+ s << "
Proof-of-Work: " << ProofOfWork::eval(info.headerHashWithoutNonce(), info.nonce) << " <= " << (h256)u256((bigint(1) << 256) / info.difficulty) << "";
s << "
Parent: " << info.parentHash << "";
// s << "
Bloom: " << details.bloom << "";
s << "
Log Bloom: " << info.logBloom << "";
@@ -1282,6 +1284,7 @@ void Main::on_blocks_currentItemChanged()
for (auto const& i: block[1])
s << "
" << sha3(i.data()).abridged();// << ": " << i[1].toHash() << " [" << i[2].toInt() << " used]";
s << "
Post: " << info.stateRoot << "";
+ s << "
Dump: " << toHex(block[0].data()) << "";
}
else
{
@@ -1665,7 +1668,7 @@ void Main::on_data_textChanged()
errs.append("
" + QString::fromStdString(i).toHtmlEscaped() + "
");
}
ui->code->setHtml(errs + lll + solidity + "Code
" + 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);
}
}
diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake
index bf7ff487a..db8b6a29e 100644
--- a/cmake/EthDependencies.cmake
+++ b/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)
diff --git a/eth/main.cpp b/eth/main.cpp
index 05290c905..abfc1bfa0 100644
--- a/eth/main.cpp
+++ b/eth/main.cpp
@@ -27,9 +27,6 @@
#include
#include
#include
-#if ETH_JSONRPC
-#include
-#endif
#include
#include
#include
@@ -41,6 +38,7 @@
#endif
#if ETH_JSONRPC
#include
+#include
#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 jsonrpcServer;
+ shared_ptr jsonrpcServer;
unique_ptr jsonrpcConnector;
if (jsonrpc > -1)
{
- jsonrpcConnector = unique_ptr(new jsonrpc::CorsHttpServer(jsonrpc));
- jsonrpcServer = unique_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, {us}));
+ jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc));
+ jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us})));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
@@ -429,8 +427,9 @@ int main(int argc, char** argv)
{
if (jsonrpc < 0)
jsonrpc = 8080;
+
jsonrpcConnector = unique_ptr(new jsonrpc::CorsHttpServer(jsonrpc));
- jsonrpcServer = auto_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, {us}));
+ jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({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
diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp
index ae073b9b1..55250b418 100644
--- a/libdevcore/Common.cpp
+++ b/libdevcore/Common.cpp
@@ -27,7 +27,7 @@ using namespace dev;
namespace dev
{
-char const* Version = "0.7.10";
+char const* Version = "0.7.12";
}
diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp
index b2660305a..014780a66 100644
--- a/libdevcore/Worker.cpp
+++ b/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";
diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h
index a4a998dd7..f73a0f4aa 100644
--- a/libdevcore/Worker.h
+++ b/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 m_work; ///< The network thread.
bool m_stop = false;
- std::string m_name;
};
}
diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp
index 44da9603c..015f8dad6 100644
--- a/libethcore/BlockInfo.cpp
+++ b/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();
difficulty = _header[field = 7].toInt();
number = _header[field = 8].toInt();
- minGasPrice = _header[field = 9].toInt();
- gasLimit = _header[field = 10].toInt();
- gasUsed = _header[field = 11].toInt();
- timestamp = _header[field = 12].toInt();
- extraData = _header[field = 13].toBytes();
- nonce = _header[field = 14].toHash();
+ gasLimit = _header[field = 9].toInt();
+ gasUsed = _header[field = 10].toInt();
+ timestamp = _header[field = 11].toInt();
+ extraData = _header[field = 12].toBytes();
+ nonce = _header[field = 13].toHash();
}
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());
}
diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h
index d91ff244d..aa7456f72 100644
--- a/libethcore/BlockInfo.h
+++ b/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;
}
diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp
index 19522d5de..744e85a27 100644
--- a/libethcore/CommonEth.cpp
+++ b/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> g_units =
{
diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp
index 765b54627..531005fb2 100644
--- a/libethereum/BlockChain.cpp
+++ b/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));
diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h
index 74d94e164..d03818bd3 100644
--- a/libethereum/BlockChain.h
+++ b/libethereum/BlockChain.h
@@ -101,14 +101,6 @@ public:
BlockDetails details(h256 _hash) const { return queryExtras(_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(_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(_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(_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;
diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h
index 973e93070..0c3af5b33 100644
--- a/libethereum/BlockDetails.h
+++ b/libethereum/BlockDetails.h
@@ -54,24 +54,6 @@ struct BlockDetails
h256 bloom;
};
-struct BlockBlooms
-{
- BlockBlooms() {}
- BlockBlooms(RLP const& _r) { blooms = _r.toVector(); }
- 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 BlockDetailsHash;
-typedef std::map BlockBloomsHash;
-typedef std::map BlockTracesHash;
typedef std::map BlockLogBloomsHash;
typedef std::map BlockReceiptsHash;
static const BlockDetails NullBlockDetails;
-static const BlockBlooms NullBlockBlooms;
-static const BlockTraces NullBlockTraces;
static const BlockLogBlooms NullBlockLogBlooms;
static const BlockReceipts NullBlockReceipts;
diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h
index 18ba765aa..c732f2dc1 100644
--- a/libethereum/EthereumHost.h
+++ b/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, Worker
{
diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp
index c3a8b2a80..6a123875c 100644
--- a/libethereum/Executive.cpp
+++ b/libethereum/Executive.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#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)
diff --git a/libethereum/Interface.h b/libethereum/Interface.h
index add9a1bda..d598e1f9b 100644
--- a/libethereum/Interface.h
+++ b/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 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;
diff --git a/libethereum/MessageFilter.cpp b/libethereum/MessageFilter.cpp
index b04d213f9..0519fe28b 100644
--- a/libethereum/MessageFilter.cpp
+++ b/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);
}
diff --git a/libethereum/State.cpp b/libethereum/State.cpp
index 1ac1507fe..9a0426e18 100644
--- a/libethereum/State.cpp
+++ b/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.
diff --git a/libethereum/Utility.h b/libethereum/Utility.h
index 93c3d54c8..893604139 100644
--- a/libethereum/Utility.h
+++ b/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);
}
diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h
index 65761e410..9e6601d0a 100644
--- a/libevm/ExtVMFace.h
+++ b/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;
};
diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp
index 6d868cac5..3e957118e 100644
--- a/libevm/FeeStructure.cpp
+++ b/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;
diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h
index e57f7ccf8..3fb8175e6 100644
--- a/libevm/FeeStructure.h
+++ b/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.
}
}
diff --git a/libevm/VM.h b/libevm/VM.h
index 425eab8c8..5fb46fc68 100644
--- a/libevm/VM.h
+++ b/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 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 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 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 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 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 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)
diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp
index 4725c8c1a..059810af8 100644
--- a/libevmcore/Assembly.cpp
+++ b/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(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(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
+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()
@@ -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> c_simple =
+ map> 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> c_associative =
+ map> 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> const c_identities =
+ { { Instruction::ADD, 0}, { Instruction::MUL, 1}, { Instruction::MOD, 0}, { Instruction::OR, 0}, { Instruction::XOR, 0} };
std::vector>> 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 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;
}
diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h
index b8e59a474..b144dd8d9 100644
--- a/libevmcore/Assembly.h
+++ b/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)); }
diff --git a/libevmcore/Instruction.h b/libevmcore/Instruction.h
index eb85c0610..f8a0478f1 100644
--- a/libevmcore/Instruction.h
+++ b/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
};
diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp
index 8a3b27ef0..e0f3b5629 100644
--- a/libp2p/Common.cpp
+++ b/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 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)
diff --git a/libp2p/Common.h b/libp2p/Common.h
index 0df7f14fe..34f47b236 100644
--- a/libp2p/Common.h
+++ b/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;
diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp
index 996e219db..cad1b179c 100644
--- a/libp2p/Host.cpp
+++ b/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 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 Host::getInterfaceAddresses()
{
- populateAddresses();
- clog(NetNote) << "Id:" << id().abridged();
- if (_start)
- start();
-}
-
-Host::~Host()
-{
- quit();
+ std::vector 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 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 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 _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 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 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 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()
diff --git a/libp2p/Host.h b/libp2p/Host.h
index 7722905ab..c82ecf84c 100644
--- a/libp2p/Host.h
+++ b/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 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 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 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(NodeId _id) const { if (m_nodes.count(_id)) return m_nodes.at(_id); return std::shared_ptr(); }
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 noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = NodeId());
Nodes potentialPeers(RangeMask 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 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 m_ioService; ///< IOService for network stuff.
- std::unique_ptr m_acceptor; ///< Listening acceptor.
- std::unique_ptr m_socket; ///< Listening socket.
+ std::unique_ptr m_acceptor; ///< Listening acceptor.
+ std::unique_ptr m_socket; ///< Listening socket.
+
+ std::unique_ptr 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 m_nodesList;
RangeMask m_ready; ///< Indices into m_nodesList over to which nodes we are not currently connected, connecting or otherwise ignoring.
- RangeMask 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 m_private; ///< Indices into m_nodesList over to which nodes are private.
- // Our addresses.
- std::vector m_addresses; ///< Addresses for us.
- std::vector m_peerAddresses; ///< Addresses that peers (can) know us by.
+ unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to.
+
+ std::set m_peerAddresses; ///< Public addresses that peers (can) know us by.
// Our capabilities.
std::map> m_capabilities; ///< Each of the capabilities we support.
diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp
index c4385df5f..958473870 100644
--- a/libp2p/Session.cpp
+++ b/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;
diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp
index e8bdecf31..4bd0b2c0e 100644
--- a/libsolidity/AST.cpp
+++ b/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 const& member: getMembers())
+ if (!member->getType()->canBeStored())
+ BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
+}
+
+void StructDefinition::checkRecursion()
+{
+ set definitionsSeen;
+ vector 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 const& member: def->getMembers())
+ if (member->getType()->getCategory() == Type::Category::STRUCT)
+ {
+ UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName());
+ queue.push_back(&dynamic_cast(*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(*expressionType).getFunction();
- vector> const& parameters = fun.getParameters();
- if (parameters.size() != m_arguments.size())
+ FunctionType const& functionType = dynamic_cast(*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();
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(*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(*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(*functionDef);
- m_isLvalue = true;
return;
}
ContractDefinition* contractDef = dynamic_cast(m_referencedDeclaration);
@@ -524,6 +551,12 @@ void Identifier::checkTypeRequirements()
m_type = make_shared(make_shared(*contractDef));
return;
}
+ MagicVariableDeclaration* magicVariable = dynamic_cast(m_referencedDeclaration);
+ if (magicVariable)
+ {
+ m_type = magicVariable->getType();
+ return;
+ }
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
}
diff --git a/libsolidity/AST.h b/libsolidity/AST.h
index 80c7dd198..87bc3cd40 100644
--- a/libsolidity/AST.h
+++ b/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 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 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 getInterfaceFunctions() const;
+
private:
std::vector> m_definedStructs;
std::vector> m_stateVariables;
@@ -139,7 +145,14 @@ public:
std::vector> 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> m_members;
};
@@ -165,14 +178,21 @@ private:
class FunctionDefinition: public Declaration
{
public:
- FunctionDefinition(Location const& _location, ASTPointer const& _name, bool _isPublic,
- ASTPointer const& _parameters,
- bool _isDeclaredConst,
- ASTPointer const& _returnParameters,
- ASTPointer 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 const& _name,
+ bool _isPublic,
+ ASTPointer const& _documentation,
+ ASTPointer const& _parameters,
+ bool _isDeclaredConst,
+ ASTPointer const& _returnParameters,
+ ASTPointer 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> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer 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 const& getDocumentation() { return m_documentation; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector 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 m_parameters;
bool m_isDeclaredConst;
ASTPointer m_returnParameters;
ASTPointer m_body;
+ ASTPointer m_documentation;
std::vector 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 const& getType() const { return m_type; }
void setType(std::shared_ptr const& _type) { m_type = _type; }
+ bool isLocalVariable() const { return !!dynamic_cast(getScope()); }
+
private:
ASTPointer m_typeName; ///< can be empty ("var")
std::shared_ptr 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 const& _type):
+ Declaration(Location(), std::make_shared(_name)), m_type(_type) {}
+ virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError()
+ << errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
+
+ std::shared_ptr const& getType() const { return m_type; }
+
+private:
+ std::shared_ptr 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 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 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 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 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 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;
};
diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h
index 2b0bd8869..a369c8a79 100644
--- a/libsolidity/ASTForward.h
+++ b/libsolidity/ASTForward.h
@@ -40,6 +40,7 @@ class StructDefinition;
class ParameterList;
class FunctionDefinition;
class VariableDeclaration;
+class MagicVariableDeclaration;
class TypeName;
class ElementaryTypeName;
class UserDefinedTypeName;
diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp
index a3865bc30..17ad4fd16 100644
--- a/libsolidity/Compiler.cpp
+++ b/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 const& _magicGlobals)
{
m_context = CompilerContext(); // clear it just in case
+ for (MagicVariableDeclaration const* variable: _magicGlobals)
+ m_context.addMagicGlobal(*variable);
+
for (ASTPointer 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;
}
diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h
index 3887d3b5b..70e6c44f2 100644
--- a/libsolidity/Compiler.h
+++ b/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 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.
diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp
index 3c1acdfa7..b89a8e5b5 100644
--- a/libsolidity/CompilerContext.cpp
+++ b/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;
diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h
index e624222dd..6a48e1485 100644
--- a/libsolidity/CompilerContext.h
+++ b/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 m_magicGlobals;
/// Size of the state variables, offset of next variable to be added.
u256 m_stateVariablesSize;
/// Storage offsets of state variables
diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp
index d87c27916..6535e00d4 100644
--- a/libsolidity/CompilerStack.cpp
+++ b/libsolidity/CompilerStack.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -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();
+ 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();
- m_compiler->compileContract(*m_contractASTNode);
+ m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables());
return m_bytecode = m_compiler->getAssembledBytecode(_optimize);
}
diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h
index 2fb505897..6cae8660f 100644
--- a/libsolidity/CompilerStack.h
+++ b/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 m_scanner;
+ std::shared_ptr m_globalContext;
std::shared_ptr m_contractASTNode;
bool m_parseSuccessful;
std::string m_interface;
diff --git a/libsolidity/Scope.cpp b/libsolidity/DeclarationContainer.cpp
similarity index 78%
rename from libsolidity/Scope.cpp
rename to libsolidity/DeclarationContainer.cpp
index 540c41204..6ea9c28c5 100644
--- a/libsolidity/Scope.cpp
+++ b/libsolidity/DeclarationContainer.cpp
@@ -20,7 +20,7 @@
* Scope - object that holds declaration of names.
*/
-#include
+#include
#include
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;
}
diff --git a/libsolidity/Scope.h b/libsolidity/DeclarationContainer.h
similarity index 77%
rename from libsolidity/Scope.h
rename to libsolidity/DeclarationContainer.h
index 637c2d5ce..db6812890 100644
--- a/libsolidity/Scope.h
+++ b/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 m_declarations;
};
diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp
index f37ce39ce..c3c7116e4 100644
--- a/libsolidity/ExpressionCompiler.cpp
+++ b/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(*_functionCall.getExpression().getType()).getFunction();
-
- eth::AssemblyItem returnLabel = m_context.pushNewTag();
+ FunctionType const& function = dynamic_cast(*_functionCall.getExpression().getType());
std::vector> 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 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(*_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(*_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(*_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(declaration))
+ {
+ if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
+ m_context << eth::Instruction::ADDRESS;
+ return;
+ }
+ if (FunctionDefinition* functionDef = dynamic_cast(declaration))
+ {
+ m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
+ return;
+ }
+ if (/*VariableDeclaration* varDef = */dynamic_cast(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(_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."));
}
diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h
index f52da29ec..83d7cdc6c 100644
--- a/libsolidity/ExpressionCompiler.h
+++ b/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;
};
diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp
new file mode 100644
index 000000000..d8b637076
--- /dev/null
+++ b/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 .
+*/
+/**
+ * @author Christian
+ * @date 2014
+ * Container of the (implicit and explicit) global objects.
+ */
+
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+GlobalContext::GlobalContext():
+ m_magicVariables{make_shared("block", make_shared(MagicType::Kind::BLOCK)),
+ make_shared("msg", make_shared(MagicType::Kind::MSG)),
+ make_shared("tx", make_shared(MagicType::Kind::TX)),
+ make_shared("suicide",
+ make_shared(TypePointers({std::make_shared(0,
+ IntegerType::Modifier::ADDRESS)}),
+ TypePointers(),
+ FunctionType::Location::SUICIDE)),
+ make_shared("sha3",
+ make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}),
+ TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}),
+ FunctionType::Location::SHA3)),
+ make_shared("sha256",
+ make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}),
+ TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}),
+ FunctionType::Location::SHA256)),
+ make_shared("ecrecover",
+ make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH),
+ std::make_shared(8, IntegerType::Modifier::HASH),
+ std::make_shared(256, IntegerType::Modifier::HASH),
+ std::make_shared(256, IntegerType::Modifier::HASH)}),
+ TypePointers({std::make_shared(0, IntegerType::Modifier::ADDRESS)}),
+ FunctionType::Location::ECRECOVER)),
+ make_shared("ripemd160",
+ make_shared(TypePointers({std::make_shared(256, IntegerType::Modifier::HASH)}),
+ TypePointers({std::make_shared(160, IntegerType::Modifier::HASH)}),
+ FunctionType::Location::RIPEMD160))}
+{
+}
+
+void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
+{
+ m_currentContract = &_contract;
+}
+
+vector GlobalContext::getDeclarations() const
+{
+ vector declarations;
+ declarations.reserve(m_magicVariables.size() + 1);
+ for (ASTPointer 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(
+ "this", make_shared(*m_currentContract));
+ return m_thisPointer[m_currentContract].get();
+
+}
+
+vector GlobalContext::getMagicVariables() const
+{
+ vector declarations;
+ declarations.reserve(m_magicVariables.size() + 1);
+ for (ASTPointer const& variable: m_magicVariables)
+ declarations.push_back(variable.get());
+ declarations.push_back(getCurrentThis());
+ return declarations;
+}
+
+}
+}
diff --git a/libsolidity/GlobalContext.h b/libsolidity/GlobalContext.h
new file mode 100644
index 000000000..0166734ca
--- /dev/null
+++ b/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 .
+*/
+/**
+ * @author Christian
+ * @date 2014
+ * Container of the (implicit and explicit) global objects.
+ */
+
+#pragma once
+
+#include
+#include
+#include