const& _h) { return (*this |= _h.nbloom()); }
+
+ template inline FixedHash nbloom() const
+ {
+ static const unsigned c_bloomBytes = (M + 7) / 8;
+ unsigned mask = (1 << c_bloomBytes) - 1;
+ FixedHash ret;
+ byte const* p = data();
+ for (unsigned i = 0; i < P; ++i)
+ {
+ unsigned index = 0;
+ for (unsigned j = 0; j < c_bloomBytes; ++j, ++p)
+ index = (index << 8) | *p;
+ index &= mask;
+ ret[N - 1 - index / 8] |= (1 << (index % 8));
+ }
+ return ret;
+ }
+
/// Returns the index of the first bit set to one, or size() * 8 if no bits are set.
inline unsigned firstBitSet() const
{
diff --git a/libdevcrypto/All.h b/libdevcrypto/All.h
index 6d1844857..db6d7c615 100644
--- a/libdevcrypto/All.h
+++ b/libdevcrypto/All.h
@@ -1,9 +1,12 @@
#pragma once
#include "Common.h"
+#include "CryptoPP.h"
+#include "EC.h"
#include "FileSystem.h"
#include "MemoryDB.h"
#include "OverlayDB.h"
#include "SHA3.h"
+#include "SHA3MAC.h"
#include "TrieCommon.h"
#include "TrieDB.h"
diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp
index 6d57bf2b2..b4623ba24 100644
--- a/libdevcrypto/Common.cpp
+++ b/libdevcrypto/Common.cpp
@@ -144,7 +144,9 @@ Public dev::recover(Signature _sig, h256 _message)
cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl;
#endif
- return *(Public const*)&(pubkey[1]);
+ Public ret;
+ memcpy(&ret, &(pubkey[1]), sizeof(Public));
+ return ret;
}
inline h256 kFromMessage(h256 _msg, h256 _priv)
diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h
index d7e4181ee..6070b651b 100644
--- a/libdevcrypto/CryptoPP.h
+++ b/libdevcrypto/CryptoPP.h
@@ -23,12 +23,11 @@
#pragma once
-// need to leave this one disabled
-//#pragma GCC diagnostic ignored "-Wunused-function"
+// need to leave this one disabled for link-time. blame cryptopp.
+#pragma GCC diagnostic ignored "-Wunused-function"
#pragma warning(push)
#pragma warning(disable:4100 4244)
#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
@@ -58,6 +57,7 @@ namespace crypto
namespace pp
{
+
/// RNG used by CryptoPP
inline CryptoPP::AutoSeededRandomPool& PRNG() { static CryptoPP::AutoSeededRandomPool prng; return prng; }
diff --git a/libdevcrypto/EC.cpp b/libdevcrypto/EC.cpp
index b38703ac3..5086e3203 100644
--- a/libdevcrypto/EC.cpp
+++ b/libdevcrypto/EC.cpp
@@ -52,7 +52,7 @@ void dev::crypto::encrypt(Public const& _key, bytes& io_cipher)
c.resize(e.CiphertextLength(plen));
// todo: use StringSource with _plain as input and output.
e.Encrypt(pp::PRNG(), io_cipher.data(), plen, c.data());
- bzero(io_cipher.data(), io_cipher.size());
+ memset(io_cipher.data(), 0, io_cipher.size());
io_cipher = std::move(c);
}
@@ -66,7 +66,11 @@ void dev::crypto::decrypt(Secret const& _k, bytes& io_text)
p.resize(d.MaxPlaintextLength(io_text.size()));
// todo: use StringSource with _c as input and output.
DecodingResult r = d.Decrypt(pp::PRNG(), io_text.data(), clen, p.data());
- assert(r.messageLength);
+ if (!r.isValidCoding)
+ {
+ io_text.clear();
+ return;
+ }
io_text.resize(r.messageLength);
io_text = std::move(p);
}
diff --git a/libdevcrypto/SHA3.cpp b/libdevcrypto/SHA3.cpp
index b3a6e5955..7c2cc01a3 100644
--- a/libdevcrypto/SHA3.cpp
+++ b/libdevcrypto/SHA3.cpp
@@ -20,8 +20,9 @@
*/
#include "SHA3.h"
-#include "CryptoPP.h"
+#include
+#include "CryptoPP.h"
using namespace std;
using namespace dev;
diff --git a/libdevcrypto/TrieDB.cpp b/libdevcrypto/TrieDB.cpp
index 168b2fdf7..674f21aa4 100644
--- a/libdevcrypto/TrieDB.cpp
+++ b/libdevcrypto/TrieDB.cpp
@@ -26,6 +26,7 @@ using namespace dev;
#if !ETH_LANGUAGES
-const h256 dev::c_shaNull = sha3(rlp(""));
+h256 const dev::c_shaNull = sha3(rlp(""));
+h256 const dev::EmptyTrie = c_shaNull;
#endif
diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h
index 1fca92294..b42d67aea 100644
--- a/libdevcrypto/TrieDB.h
+++ b/libdevcrypto/TrieDB.h
@@ -45,6 +45,7 @@ struct TrieDBChannel: public LogChannel { static const char* name() { return "-
struct InvalidTrie: virtual dev::Exception {};
extern const h256 c_shaNull;
+extern const h256 EmptyTrie;
/**
* @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree.
@@ -74,7 +75,7 @@ public:
void init();
void setRoot(h256 _root)
{
- m_root = _root == h256() ? c_shaNull : _root;
+ m_root = _root;
if (m_root == c_shaNull && !m_db->exists(m_root))
init();
@@ -82,14 +83,14 @@ public:
if (!node(m_root).size())
BOOST_THROW_EXCEPTION(RootNotFound());
}
- bool haveRoot(h256 _root, bool _enforceRefs = true) { return _root == h256() ? true : m_db->lookup(_root, _enforceRefs).size(); }
+ bool haveRoot(h256 _root, bool _enforceRefs = true) { return _root == c_shaNull ? true : m_db->lookup(_root, _enforceRefs).size(); }
/// True if the trie is uninitialised (i.e. that the DB doesn't contain the root node).
bool isNull() const { return !node(m_root).size(); }
/// True if the trie is initialised but empty (i.e. that the DB contains the root node which is empty).
bool isEmpty() const { return m_root == c_shaNull && node(m_root).size(); }
- h256 root() const { assert(node(m_root).size()); h256 ret = (m_root == c_shaNull ? h256() : m_root); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return ret; } // patch the root in the case of the empty trie. TODO: handle this properly.
+ h256 root() const { assert(node(m_root).size()); /*std::cout << "Returning root as " << ret << " (really " << m_root << ")" << std::endl;*/ return m_root; } // patch the root in the case of the empty trie. TODO: handle this properly.
void debugPrint() {}
diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp
index 4c9e2bef7..d87e6f5df 100644
--- a/libethcore/BlockInfo.cpp
+++ b/libethcore/BlockInfo.cpp
@@ -37,9 +37,9 @@ BlockInfo::BlockInfo(): timestamp(Invalid256)
{
}
-BlockInfo::BlockInfo(bytesConstRef _block)
+BlockInfo::BlockInfo(bytesConstRef _block, bool _checkNonce)
{
- populate(_block);
+ populate(_block, _checkNonce);
}
BlockInfo BlockInfo::fromHeader(bytesConstRef _block)
@@ -52,19 +52,17 @@ BlockInfo BlockInfo::fromHeader(bytesConstRef _block)
h256 BlockInfo::headerHashWithoutNonce() const
{
RLPStream s;
- fillStream(s, false);
+ streamRLP(s, false);
return sha3(s.out());
}
auto static const c_sha3EmptyList = sha3(RLPEmptyList);
-void BlockInfo::fillStream(RLPStream& _s, bool _nonce) const
+void BlockInfo::streamRLP(RLPStream& _s, bool _nonce) const
{
- _s.appendList(_nonce ? 13 : 12) << parentHash;
- _s.append(sha3Uncles == c_sha3EmptyList ? h256() : sha3Uncles, false, true);
- _s << coinbaseAddress;
- _s.append(stateRoot, false, true).append(transactionsRoot, false, true);
- _s << difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData;
+ _s.appendList(_nonce ? 15 : 14)
+ << parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << receiptsRoot << logBloom
+ << difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData;
if (_nonce)
_s << nonce;
}
@@ -83,22 +81,22 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce)
{
parentHash = _header[field = 0].toHash();
sha3Uncles = _header[field = 1].toHash();
- if (sha3Uncles == h256())
- sha3Uncles = c_sha3EmptyList;
coinbaseAddress = _header[field = 2].toHash();
stateRoot = _header[field = 3].toHash();
transactionsRoot = _header[field = 4].toHash();
- difficulty = _header[field = 5].toInt();
- number = _header[field = 6].toInt();
- minGasPrice = _header[field = 7].toInt();
- gasLimit = _header[field = 8].toInt();
- gasUsed = _header[field = 9].toInt();
- timestamp = _header[field = 10].toInt();
- extraData = _header[field = 11].toBytes();
- nonce = _header[field = 12].toHash();
+ receiptsRoot = _header[field = 5].toHash();
+ 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();
}
- catch (Exception & _e)
+ catch (Exception const& _e)
{
_e << errinfo_name("invalid block header format") << BadFieldError(field, toHex(_header[field].data().toBytes()));
throw;
@@ -144,7 +142,7 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
{
bytes k = rlp(i);
t.insert(&k, tr.data());
- u256 gp = tr[0][1].toInt();
+ u256 gp = tr[1].toInt();
mgp = min(mgp, gp);
++i;
}
diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h
index 283f11b88..d91ff244d 100644
--- a/libethcore/BlockInfo.h
+++ b/libethcore/BlockInfo.h
@@ -47,7 +47,7 @@ extern u256 c_genesisDifficulty;
* corresponding RLP block created with createGenesisBlock().
*
* The difficulty and gas-limit derivations may be calculated with the calculateDifficulty()
- * and calculateGasLimit() and the object serialised to RLP with fillStream. To determine the
+ * and calculateGasLimit() and the object serialised to RLP with streamRLP. To determine the
* header hash without the nonce (for mining), the method headerHashWithoutNonce() is provided.
*
* The default constructor creates an empty object, which can be tested against with the boolean
@@ -62,6 +62,8 @@ public:
Address coinbaseAddress;
h256 stateRoot;
h256 transactionsRoot;
+ h256 receiptsRoot;
+ h512 logBloom; // TODO LogBloom - get include
u256 difficulty;
u256 number;
u256 minGasPrice;
@@ -73,7 +75,7 @@ public:
BlockInfo();
explicit BlockInfo(bytes const& _block): BlockInfo(&_block) {}
- explicit BlockInfo(bytesConstRef _block);
+ explicit BlockInfo(bytesConstRef _block, bool _checkNonce = true);
static h256 headerHash(bytes const& _block) { return headerHash(&_block); }
static h256 headerHash(bytesConstRef _block);
@@ -89,6 +91,8 @@ public:
coinbaseAddress == _cmp.coinbaseAddress &&
stateRoot == _cmp.stateRoot &&
transactionsRoot == _cmp.transactionsRoot &&
+ receiptsRoot == _cmp.receiptsRoot &&
+ logBloom == _cmp.logBloom &&
difficulty == _cmp.difficulty &&
number == _cmp.number &&
minGasPrice == _cmp.minGasPrice &&
@@ -112,13 +116,14 @@ public:
/// No-nonce sha3 of the header only.
h256 headerHashWithoutNonce() const;
- void fillStream(RLPStream& _s, bool _nonce) const;
+ void streamRLP(RLPStream& _s, bool _nonce) const;
};
inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi)
{
_out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " <<
- _bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " << _bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce;
+ _bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " <<
+ _bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce;
return _out;
}
diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp
index 5f989689c..44ebe19c5 100644
--- a/libethcore/CommonEth.cpp
+++ b/libethcore/CommonEth.cpp
@@ -34,7 +34,7 @@ namespace dev
namespace eth
{
-const unsigned c_protocolVersion = 36;
+const unsigned c_protocolVersion = 37;
const unsigned c_databaseVersion = 3;
static const vector> g_units =
diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h
index 8e4fcb2d2..5a07407d2 100644
--- a/libethcore/Exceptions.h
+++ b/libethcore/Exceptions.h
@@ -57,7 +57,8 @@ struct InvalidDifficulty: virtual dev::Exception {};
class InvalidGasLimit: virtual public dev::Exception { public: InvalidGasLimit(u256 _provided = 0, u256 _valid = 0): provided(_provided), valid(_valid) {} u256 provided; u256 valid; virtual const char* what() const noexcept; };
class InvalidMinGasPrice: virtual public dev::Exception { public: InvalidMinGasPrice(u256 _provided = 0, u256 _limit = 0): provided(_provided), limit(_limit) {} u256 provided; u256 limit; virtual const char* what() const noexcept; };
struct InvalidTransactionGasUsed: virtual dev::Exception {};
-struct InvalidTransactionStateRoot: virtual dev::Exception {};
+struct InvalidTransactionsStateRoot: virtual dev::Exception {};
+struct InvalidReceiptsStateRoot: virtual dev::Exception {};
struct InvalidTimestamp: virtual dev::Exception {};
class InvalidNonce: virtual public dev::Exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; virtual const char* what() const noexcept; };
class InvalidBlockNonce: virtual public dev::Exception { public: InvalidBlockNonce(h256 _h = h256(), h256 _n = h256(), u256 _d = 0): h(_h), n(_n), d(_d) {} h256 h; h256 n; u256 d; virtual const char* what() const noexcept; };
diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp
index 7b7b8a9e5..a9bdbf247 100644
--- a/libethereum/BlockChain.cpp
+++ b/libethereum/BlockChain.cpp
@@ -101,8 +101,9 @@ bytes BlockChain::createGenesisBlock()
stateRoot = state.root();
}
- block.appendList(13) << h256() << bytes() << h160();
- block.append(stateRoot, false, true) << bytes() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
+ block.appendList(15)
+ // TODO: maybe make logbloom correct?
+ << h256() << EmptySHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
@@ -305,7 +306,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
// Get total difficulty increase and update state, checking it.
State s(bi.coinbaseAddress, _db);
auto tdIncrease = s.enactOn(&_block, bi, *this);
- auto b = s.bloom();
+ auto b = s.oldBloom();
BlockBlooms bb;
BlockTraces bt;
for (unsigned i = 0; i < s.pending().size(); ++i)
diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h
index 9cb49a21c..90bf65bdd 100644
--- a/libethereum/BlockDetails.h
+++ b/libethereum/BlockDetails.h
@@ -66,7 +66,7 @@ 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.streamOut(s); return s.out(); }
+ bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamRLP(s); return s.out(); }
Manifests traces;
};
diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp
index ecce7406d..63e093b17 100644
--- a/libethereum/Client.cpp
+++ b/libethereum/Client.cpp
@@ -159,7 +159,7 @@ void Client::clearPending()
if (!m_postMine.pending().size())
return;
for (unsigned i = 0; i < m_postMine.pending().size(); ++i)
- appendFromNewPending(m_postMine.bloom(i), changeds);
+ appendFromNewPending(m_postMine.oldBloom(i), changeds);
changeds.insert(PendingChangedFilter);
m_postMine = m_preMine;
}
@@ -325,6 +325,7 @@ void Client::transact(Secret _secret, u256 _value, Address _dest, bytes const& _
t.value = _value;
t.gasPrice = _gasPrice;
t.gas = _gas;
+ t.type = Transaction::MessageCall;
t.receiveAddress = _dest;
t.data = _data;
t.sign(_secret);
@@ -348,6 +349,7 @@ bytes Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _dat
t.value = _value;
t.gasPrice = _gasPrice;
t.gas = _gas;
+ t.type = Transaction::ContractCreation;
t.receiveAddress = _dest;
t.data = _data;
t.sign(_secret);
@@ -373,6 +375,7 @@ Address Client::transact(Secret _secret, u256 _endowment, bytes const& _init, u2
t.value = _endowment;
t.gasPrice = _gasPrice;
t.gas = _gas;
+ t.type = Transaction::ContractCreation;
t.receiveAddress = Address();
t.data = _init;
t.sign(_secret);
diff --git a/libethereum/Client.h b/libethereum/Client.h
index cabc9ac23..8ec65c199 100644
--- a/libethereum/Client.h
+++ b/libethereum/Client.h
@@ -285,7 +285,7 @@ private:
State m_preMine; ///< The present state of the client.
State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
- std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
+ std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock.
std::vector m_miners;
mutable boost::shared_mutex x_miners;
diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp
index 193010cfa..5a983b66e 100644
--- a/libethereum/Executive.cpp
+++ b/libethereum/Executive.cpp
@@ -123,7 +123,11 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu
m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms);
}
else
+ {
m_endGas = _gas;
+ if (m_ext)
+ m_ext->sub.logs.push_back(LogEntry(_receiveAddress, {u256((u160)_senderAddress) + 1}, bytes()));
+ }
return !m_ext;
}
@@ -172,6 +176,8 @@ bool Executive::go(OnOpFunc const& _onOp)
try
{
m_out = m_vm->go(*m_ext, _onOp);
+ if (m_ext)
+ m_endGas += min((m_t.gas - m_endGas) / 2, m_ext->sub.refunds);
m_endGas = m_vm->gas();
}
catch (StepsDone const&)
@@ -187,14 +193,17 @@ bool Executive::go(OnOpFunc const& _onOp)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
m_endGas = m_vm->gas();
+ revert = true;
}
catch (Exception const& _e)
{
- clog(StateChat) << "Exception in VM: " << diagnostic_information(_e);
+ // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
+ cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
}
catch (std::exception const& _e)
{
- clog(StateChat) << "std::exception in VM: " << _e.what();
+ // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
+ cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
}
cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
@@ -236,6 +245,6 @@ void Executive::finalize(OnOpFunc const&)
// Suicides...
if (m_ext)
- for (auto a: m_ext->suicides)
+ for (auto a: m_ext->sub.suicides)
m_s.m_cache[a].kill();
}
diff --git a/libethereum/Executive.h b/libethereum/Executive.h
index 82b7df7e9..cdfe23966 100644
--- a/libethereum/Executive.h
+++ b/libethereum/Executive.h
@@ -61,6 +61,7 @@ public:
bytesConstRef out() const { return m_out; }
h160 newAddress() const { return m_newAddress; }
+ LogEntries const& logs() const { return m_logs; }
VM const& vm() const { return *m_vm; }
State const& state() const { return m_s; }
@@ -77,6 +78,8 @@ private:
Transaction m_t;
Address m_sender;
u256 m_endGas;
+
+ LogEntries m_logs;
};
}
diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h
index fc76d56b0..59b6eb2ab 100644
--- a/libethereum/ExtVM.h
+++ b/libethereum/ExtVM.h
@@ -61,7 +61,7 @@ public:
m_s.noteSending(myAddress);
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
- auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
+ auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
@@ -72,7 +72,7 @@ public:
{
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
- auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
+ auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
diff --git a/libethereum/Manifest.cpp b/libethereum/Manifest.cpp
index 2d1e4aa6e..9e6d2f651 100644
--- a/libethereum/Manifest.cpp
+++ b/libethereum/Manifest.cpp
@@ -37,10 +37,10 @@ Manifest::Manifest(bytesConstRef _r)
internal.emplace_back(i.data());
}
-void Manifest::streamOut(RLPStream& _s) const
+void Manifest::streamRLP(RLPStream& _s) const
{
_s.appendList(7) << from << to << value << altered << input << output;
_s.appendList(internal.size());
for (auto const& i: internal)
- i.streamOut(_s);
+ i.streamRLP(_s);
}
diff --git a/libethereum/Manifest.h b/libethereum/Manifest.h
index 12c40b3a9..e0e512c92 100644
--- a/libethereum/Manifest.h
+++ b/libethereum/Manifest.h
@@ -41,7 +41,7 @@ struct Manifest
{
Manifest() {}
Manifest(bytesConstRef _r);
- void streamOut(RLPStream& _s) const;
+ void streamRLP(RLPStream& _s) const;
h256 bloom() const { h256 ret = from.bloom() | to.bloom(); for (auto const& i: internal) ret |= i.bloom(); for (auto const& i: altered) ret |= h256(i).bloom(); return ret; }
diff --git a/libethereum/MessageFilter.cpp b/libethereum/MessageFilter.cpp
index 951bfec98..fc6af1308 100644
--- a/libethereum/MessageFilter.cpp
+++ b/libethereum/MessageFilter.cpp
@@ -27,7 +27,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
-void MessageFilter::fillStream(RLPStream& _s) const
+void MessageFilter::streamRLP(RLPStream& _s) const
{
_s.appendList(8) << m_from << m_to << m_stateAltered << m_altered << m_earliest << m_latest << m_max << m_skip;
}
@@ -35,7 +35,7 @@ void MessageFilter::fillStream(RLPStream& _s) const
h256 MessageFilter::sha3() const
{
RLPStream s;
- fillStream(s);
+ streamRLP(s);
return dev::sha3(s.out());
}
diff --git a/libethereum/MessageFilter.h b/libethereum/MessageFilter.h
index c9fb4e51c..83cbb7237 100644
--- a/libethereum/MessageFilter.h
+++ b/libethereum/MessageFilter.h
@@ -39,7 +39,7 @@ class MessageFilter
public:
MessageFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {}
- void fillStream(RLPStream& _s) const;
+ void streamRLP(RLPStream& _s) const;
h256 sha3() const;
int earliest() const { return m_earliest; }
diff --git a/libethereum/State.cpp b/libethereum/State.cpp
index 931ee2cf6..11400b755 100644
--- a/libethereum/State.cpp
+++ b/libethereum/State.cpp
@@ -161,6 +161,7 @@ State::State(State const& _s):
m_db(_s.m_db),
m_state(&m_db, _s.m_state.root()),
m_transactions(_s.m_transactions),
+ m_receipts(_s.m_receipts),
m_transactionSet(_s.m_transactionSet),
m_cache(_s.m_cache),
m_previousBlock(_s.m_previousBlock),
@@ -192,6 +193,7 @@ State& State::operator=(State const& _s)
m_db = _s.m_db;
m_state.open(&m_db, _s.m_state.root());
m_transactions = _s.m_transactions;
+ m_receipts = _s.m_receipts;
m_transactionSet = _s.m_transactionSet;
m_cache = _s.m_cache;
m_previousBlock = _s.m_previousBlock;
@@ -353,9 +355,9 @@ void State::ensureCached(std::map& _cache, Address _a, bo
RLP state(stateBack);
AddressState s;
if (state.isNull())
- s = AddressState(0, 0, h256(), EmptySHA3);
+ s = AddressState(0, 0, EmptyTrie, EmptySHA3);
else
- s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].isEmpty() ? EmptySHA3 : state[3].toHash());
+ s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].toHash());
bool ok;
tie(it, ok) = _cache.insert(make_pair(_a, s));
}
@@ -484,6 +486,7 @@ map State::addresses() const
void State::resetCurrent()
{
m_transactions.clear();
+ m_receipts.clear();
m_transactionSet.clear();
m_cache.clear();
m_currentBlock = BlockInfo();
@@ -547,7 +550,7 @@ h256s State::sync(TransactionQueue& _tq, bool* o_transactionQueueChanged)
{
uncommitToMine();
execute(i.second);
- ret.push_back(m_transactions.back().changes.bloom());
+ ret.push_back(m_receipts.back().changes().bloom());
_tq.noteGood(i);
++goodTxs;
}
@@ -588,7 +591,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce)
// m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE
- BlockInfo bi(_block);
+ BlockInfo bi(_block, _checkNonce);
assert(m_previousBlock.hash == bi.parentHash);
assert(m_currentBlock.parentHash == bi.parentHash);
assert(rootHash() == m_previousBlock.stateRoot);
@@ -605,16 +608,32 @@ u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce)
// cnote << m_state;
MemoryDB tm;
- GenericTrieDB transactionManifest(&tm);
- transactionManifest.init();
+ GenericTrieDB transactionsTrie(&tm);
+ transactionsTrie.init();
+
+ MemoryDB rm;
+ GenericTrieDB receiptsTrie(&rm);
+ receiptsTrie.init();
// All ok with the block generally. Play back the transactions now...
unsigned i = 0;
for (auto const& tr: RLP(_block)[1])
{
+ RLPStream k;
+ k << i;
+
+ RLPStream txrlp;
+ m_transactions[i].streamRLP(txrlp);
+ transactionsTrie.insert(&k.out(), tr.data());
+
// cnote << m_state.root() << m_state;
// cnote << *this;
- execute(tr[0].data());
+ execute(tr.data());
+
+ RLPStream receiptrlp;
+ m_receipts.back().streamRLP(receiptrlp);
+ receiptsTrie.insert(&k.out(), &receiptrlp.out());
+/*
if (tr[1].toHash() != m_state.root())
{
// Invalid state root
@@ -625,15 +644,20 @@ u256 State::enact(bytesConstRef _block, BlockChain const* _bc, bool _checkNonce)
}
if (tr[2].toInt() != gasUsed())
BOOST_THROW_EXCEPTION(InvalidTransactionGasUsed());
- bytes k = rlp(i);
- transactionManifest.insert(&k, tr.data());
+*/
++i;
}
- if (m_currentBlock.transactionsRoot && transactionManifest.root() != m_currentBlock.transactionsRoot)
+ if (transactionsTrie.root() != m_currentBlock.transactionsRoot)
{
cwarn << "Bad transactions state root!";
- BOOST_THROW_EXCEPTION(InvalidTransactionStateRoot());
+ BOOST_THROW_EXCEPTION(InvalidTransactionsStateRoot());
+ }
+
+ if (receiptsTrie.root() != m_currentBlock.receiptsRoot)
+ {
+ cwarn << "Bad receipts state root!";
+ BOOST_THROW_EXCEPTION(InvalidReceiptsStateRoot());
}
// Initialise total difficulty calculation.
@@ -713,7 +737,7 @@ void State::uncommitToMine()
if (!m_transactions.size())
m_state.setRoot(m_previousBlock.stateRoot);
else
- m_state.setRoot(m_transactions[m_transactions.size() - 1].stateRoot);
+ m_state.setRoot(m_receipts[m_receipts.size() - 1].stateRoot());
m_db = m_lastTx;
paranoia("Uncommited to mine", true);
m_currentBlock.sha3Uncles = h256();
@@ -730,7 +754,7 @@ bool State::amIJustParanoid(BlockChain const& _bc)
// Compile block:
RLPStream block;
block.appendList(3);
- m_currentBlock.fillStream(block, true);
+ m_currentBlock.streamRLP(block, true);
block.appendRaw(m_currentTxs);
block.appendRaw(m_currentUncles);
@@ -757,11 +781,20 @@ bool State::amIJustParanoid(BlockChain const& _bc)
return false;
}
-h256 State::bloom() const
+h256 State::oldBloom() const
{
h256 ret = m_currentBlock.coinbaseAddress.bloom();
- for (auto const& i: m_transactions)
- ret |= i.changes.bloom();
+ for (auto const& i: m_receipts)
+ ret |= i.changes().bloom();
+ return ret;
+}
+
+LogBloom State::logBloom() const
+{
+ LogBloom ret;
+ ret.shiftBloom<3>(sha3(m_currentBlock.coinbaseAddress.ref()));
+ for (TransactionReceipt const& i: m_receipts)
+ ret |= i.bloom();
return ret;
}
@@ -797,7 +830,7 @@ void State::commitToMine(BlockChain const& _bc)
if (!knownUncles.count(u)) // ignore any uncles/mainline blocks that we know about.
{
BlockInfo ubi(_bc.block(u));
- ubi.fillStream(unclesData, true);
+ ubi.streamRLP(unclesData, true);
++unclesCount;
uncleAddresses.push_back(ubi.coinbaseAddress);
}
@@ -805,8 +838,12 @@ void State::commitToMine(BlockChain const& _bc)
}
MemoryDB tm;
- GenericTrieDB transactionReceipts(&tm);
- transactionReceipts.init();
+ GenericTrieDB transactionsTrie(&tm);
+ transactionsTrie.init();
+
+ MemoryDB rm;
+ GenericTrieDB receiptsTrie(&rm);
+ receiptsTrie.init();
RLPStream txs;
txs.appendList(m_transactions.size());
@@ -815,17 +852,25 @@ void State::commitToMine(BlockChain const& _bc)
{
RLPStream k;
k << i;
- RLPStream v;
- m_transactions[i].fillStream(v);
- transactionReceipts.insert(&k.out(), &v.out());
- txs.appendRaw(v.out());
+
+ RLPStream receiptrlp;
+ m_receipts[i].streamRLP(receiptrlp);
+ receiptsTrie.insert(&k.out(), &receiptrlp.out());
+
+ RLPStream txrlp;
+ m_transactions[i].streamRLP(txrlp);
+ transactionsTrie.insert(&k.out(), &txrlp.out());
+
+ txs.appendRaw(txrlp.out());
}
txs.swapOut(m_currentTxs);
RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles);
- m_currentBlock.transactionsRoot = transactionReceipts.root();
+ m_currentBlock.transactionsRoot = transactionsTrie.root();
+ m_currentBlock.receiptsRoot = receiptsTrie.root();
+ m_currentBlock.logBloom = logBloom();
m_currentBlock.sha3Uncles = sha3(m_currentUncles);
// Apply rewards last of all.
@@ -853,6 +898,10 @@ MineInfo State::mine(unsigned _msTimeout, bool _turbo)
if (!ret.completed)
m_currentBytes.clear();
+ else
+ {
+ cnote << "Completed" << m_currentBlock.headerHashWithoutNonce().abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHashWithoutNonce(), m_currentBlock.nonce, m_currentBlock.difficulty);
+ }
return ret;
}
@@ -865,7 +914,7 @@ void State::completeMine()
// Compile block:
RLPStream ret;
ret.appendList(3);
- m_currentBlock.fillStream(ret, true);
+ m_currentBlock.streamRLP(ret, true);
ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes);
@@ -875,6 +924,7 @@ void State::completeMine()
// Quickly reset the transactions.
// TODO: Leave this in a better state than this limbo, or at least record that it's in limbo.
m_transactions.clear();
+ m_receipts.clear();
m_transactionSet.clear();
m_lastTx = m_db;
}
@@ -1114,12 +1164,13 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
// TODO: CHECK TRIE after level DB flush to make sure exactly the same.
// Add to the user-originated transactions that we've executed.
- m_transactions.push_back(TransactionReceipt(e.t(), rootHash(), startGasUsed + e.gasUsed(), ms));
+ m_transactions.push_back(e.t());
+ m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs(), ms));
m_transactionSet.insert(e.t().sha3());
return e.gasUsed();
}
-bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
+bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{
if (!_originAddress)
_originAddress = _senderAddress;
@@ -1154,9 +1205,8 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
{
auto out = vm.go(evm, _onOp);
memcpy(_out.data(), out.data(), std::min(out.size(), _out.size()));
- if (o_suicides)
- for (auto i: evm.suicides)
- o_suicides->insert(i);
+ if (o_sub)
+ *o_sub += evm.sub;
if (o_ms)
o_ms->output = out.toBytes();
}
@@ -1168,6 +1218,7 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
+ revert = true;
}
catch (Exception const& _e)
{
@@ -1186,10 +1237,16 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
return !revert;
}
+ else
+ {
+ // non-contract call
+ if (o_sub)
+ o_sub->logs.push_back(LogEntry(_receiveAddress, {u256((u160)_senderAddress) + 1}, bytes()));
+ }
return true;
}
-h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
+h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{
if (!_origin)
_origin = _sender;
@@ -1218,9 +1275,8 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
out = vm.go(evm, _onOp);
if (o_ms)
o_ms->output = out.toBytes();
- if (o_suicides)
- for (auto i: evm.suicides)
- o_suicides->insert(i);
+ if (o_sub)
+ *o_sub += evm.sub;
}
catch (OutOfGas const& /*_e*/)
{
@@ -1230,25 +1286,32 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
+ revert = true;
}
catch (Exception const& _e)
{
- clog(StateChat) << "Exception in VM: " << diagnostic_information(_e);
+ // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
+ cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
}
catch (std::exception const& _e)
{
- clog(StateChat) << "std::exception in VM: " << _e.what();
+ // TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
+ cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
}
- // TODO: CHECK: IS THIS CORRECT?! (esp. given account created prior to revertion init.)
+ // TODO: CHECK: AUDIT: IS THIS CORRECT?! (esp. given account created prior to revertion init.)
// Write state out only in the case of a non-out-of-gas transaction.
if (revert)
+ {
evm.revert();
-
- // Set code.
- if (addressInUse(newAddress))
- m_cache[newAddress].setCode(out);
+ m_cache.erase(newAddress);
+ newAddress = Address();
+ }
+ else
+ // Set code.
+ if (addressInUse(newAddress))
+ m_cache[newAddress].setCode(out);
*_gas = vm.gas();
@@ -1263,11 +1326,12 @@ State State::fromPending(unsigned _i) const
if (!_i)
ret.m_state.setRoot(m_previousBlock.stateRoot);
else
- ret.m_state.setRoot(m_transactions[_i - 1].stateRoot);
+ ret.m_state.setRoot(m_receipts[_i - 1].stateRoot());
while (ret.m_transactions.size() > _i)
{
- ret.m_transactionSet.erase(ret.m_transactions.back().transaction.sha3());
+ ret.m_transactionSet.erase(ret.m_transactions.back().sha3());
ret.m_transactions.pop_back();
+ ret.m_receipts.pop_back();
}
return ret;
}
diff --git a/libethereum/State.h b/libethereum/State.h
index dd6043c73..b32bb6c7c 100644
--- a/libethereum/State.h
+++ b/libethereum/State.h
@@ -52,25 +52,37 @@ struct StateChat: public LogChannel { static const char* name() { return "-S-";
struct StateTrace: public LogChannel { static const char* name() { return "=S="; } static const int verbosity = 7; };
struct StateDetail: public LogChannel { static const char* name() { return "/S/"; } static const int verbosity = 14; };
-struct TransactionReceipt
+class TransactionReceipt
{
- TransactionReceipt(Transaction const& _t, h256 _root, u256 _gasUsed, Manifest const& _ms): transaction(_t), stateRoot(_root), gasUsed(_gasUsed), changes(_ms) {}
+public:
+ TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log, Manifest const& _ms): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log), m_changes(_ms) {}
+
+ Manifest const& changes() const { return m_changes; }
-// Manifest const& changes() const { return changes; }
+ h256 const& stateRoot() const { return m_stateRoot; }
+ u256 const& gasUsed() const { return m_gasUsed; }
+ LogBloom const& bloom() const { return m_bloom; }
+ LogEntries const& log() const { return m_log; }
- void fillStream(RLPStream& _s) const
+ void streamRLP(RLPStream& _s) const
{
- _s.appendList(3);
- transaction.fillStream(_s);
- _s.append(stateRoot, false, true) << gasUsed;
+ _s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom;
+ _s.appendList(m_log.size());
+ for (LogEntry const& l: m_log)
+ l.streamRLP(_s);
}
- Transaction transaction;
- h256 stateRoot;
- u256 gasUsed;
- Manifest changes;
+private:
+ h256 m_stateRoot;
+ u256 m_gasUsed;
+ LogBloom m_bloom;
+ LogEntries m_log;
+
+ Manifest m_changes; ///< TODO: PoC-7: KILL
};
+using TransactionReceipts = std::vector;
+
struct PrecompiledAddress
{
unsigned gas;
@@ -226,16 +238,28 @@ public:
h256 rootHash() const { return m_state.root(); }
/// Get the list of pending transactions.
- Transactions pending() const { Transactions ret; for (auto const& t: m_transactions) ret.push_back(t.transaction); return ret; }
+ Transactions const& pending() const { return m_transactions; }
+
+ /// Get the list of pending transactions. TODO: PoC-7: KILL
+ Manifest changesFromPending(unsigned _i) const { return m_receipts[_i].changes(); }
+
+ /// Get the bloom filter of all changes happened in the block. TODO: PoC-7: KILL
+ h256 oldBloom() const;
+
+ /// Get the bloom filter of a particular transaction that happened in the block. TODO: PoC-7: KILL
+ h256 oldBloom(unsigned _i) const { return m_receipts[_i].changes().bloom(); }
+
+ /// Get the transaction receipt for the transaction of the given index.
+ TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; }
/// Get the list of pending transactions.
- Manifest changesFromPending(unsigned _i) const { return m_transactions[_i].changes; }
+ LogEntries const& log(unsigned _i) const { return m_receipts[_i].log(); }
- /// Get the bloom filter of all changes happened in the block.
- h256 bloom() const;
+ /// Get the bloom filter of all logs that happened in the block.
+ LogBloom logBloom() const;
/// Get the bloom filter of a particular transaction that happened in the block.
- h256 bloom(unsigned _i) const { return m_transactions[_i].changes.bloom(); }
+ LogBloom const& logBloom(unsigned _i) const { return m_receipts[_i].bloom(); }
/// Get the State immediately after the given number of pending transactions have been applied.
/// If (_i == 0) returns the initial state of the block.
@@ -288,12 +312,12 @@ private:
// We assume all instrinsic fees are paid up before this point.
/// Execute a contract-creation transaction.
- h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
+ h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Execute a call.
/// @a _gas points to the amount of gas to use for the call, and will lower it accordingly.
/// @returns false if the call ran out of gas before completion. true otherwise.
- bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
+ bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();
@@ -304,14 +328,15 @@ private:
void refreshManifest(RLPStream* _txs = nullptr);
/// @returns gas used by transactions thus far executed.
- u256 gasUsed() const { return m_transactions.size() ? m_transactions.back().gasUsed : 0; }
+ u256 gasUsed() const { return m_receipts.size() ? m_receipts.back().gasUsed() : 0; }
bool isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const;
void paranoia(std::string const& _when, bool _enforceRefs = false) const;
OverlayDB m_db; ///< Our overlay for the state tree.
TrieDB m_state; ///< Our state tree, as an OverlayDB DB.
- std::vector m_transactions; ///< The current list of transactions that we've included in the state.
+ Transactions m_transactions; ///< The current list of transactions that we've included in the state.
+ TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts.
std::set m_transactionSet; ///< The set of transaction hashes that we've included in the state.
OverlayDB m_lastTx;
@@ -367,16 +392,10 @@ void commit(std::map const& _cache, DB& _db, TrieDB();
gasPrice = rlp[field = 1].toInt();
gas = rlp[field = 2].toInt();
+ type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
receiveAddress = rlp[field = 3].toHash();
value = rlp[field = 4].toInt();
data = rlp[field = 5].toBytes();
@@ -84,11 +85,11 @@ void Transaction::sign(Secret _priv)
vrs = *(SignatureStruct const*)&sig;
}
-void Transaction::fillStream(RLPStream& _s, bool _sig) const
+void Transaction::streamRLP(RLPStream& _s, bool _sig) const
{
_s.appendList((_sig ? 3 : 0) + 6);
_s << nonce << gasPrice << gas;
- if (receiveAddress)
+ if (type == MessageCall)
_s << receiveAddress;
else
_s << "";
diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h
index eb40c5fcb..2492e32bc 100644
--- a/libethereum/Transaction.h
+++ b/libethereum/Transaction.h
@@ -32,13 +32,20 @@ namespace eth
struct Transaction
{
+ enum Type
+ {
+ ContractCreation,
+ MessageCall
+ };
+
Transaction() {}
Transaction(bytesConstRef _rlp, bool _checkSender = false);
Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {}
- bool operator==(Transaction const& _c) const { return receiveAddress == _c.receiveAddress && value == _c.value && data == _c.data; }
+ bool operator==(Transaction const& _c) const { return type == _c.type && (type == ContractCreation || receiveAddress == _c.receiveAddress) && value == _c.value && data == _c.data; }
bool operator!=(Transaction const& _c) const { return !operator==(_c); }
+ Type type; ///< True if this is a contract-creation transaction. F
u256 nonce; ///< The transaction-count of the sender.
u256 value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
Address receiveAddress; ///< The receiving address of the transaction.
@@ -57,11 +64,11 @@ struct Transaction
static h256 kFromMessage(h256 _msg, h256 _priv);
- void fillStream(RLPStream& _s, bool _sig = true) const;
- bytes rlp(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return s.out(); }
+ void streamRLP(RLPStream& _s, bool _sig = true) const;
+ bytes rlp(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return s.out(); }
std::string rlpString(bool _sig = true) const { return asString(rlp(_sig)); }
- h256 sha3(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return dev::sha3(s.out()); }
- bytes sha3Bytes(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return dev::sha3Bytes(s.out()); }
+ h256 sha3(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return dev::sha3(s.out()); }
+ bytes sha3Bytes(bool _sig = true) const { RLPStream s; streamRLP(s, _sig); return dev::sha3Bytes(s.out()); }
private:
mutable Address m_sender;
diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h
index 67fec9321..1b1ae7455 100644
--- a/libevm/ExtVMFace.h
+++ b/libevm/ExtVMFace.h
@@ -24,6 +24,9 @@
#include
#include
#include
+#include
+#include
+#include
#include
#include
#include
@@ -33,13 +36,53 @@ namespace dev
namespace eth
{
-struct Post
+using LogBloom = h512;
+
+struct LogEntry
{
+ LogEntry() {}
+ LogEntry(RLP const& _r) { from = (Address)_r[0]; topics = (h256s)_r[1]; data = (bytes)_r[2]; }
+ LogEntry(Address const& _f, h256s&& _ts, bytes&& _d): from(_f), topics(std::move(_ts)), data(std::move(_d)) {}
+
+ void streamRLP(RLPStream& _s) const { _s.appendList(3) << from << topics << data; }
+
+ LogBloom bloom() const
+ {
+ LogBloom ret;
+ ret.shiftBloom<3, 32>(sha3(from.ref()));
+ for (auto t: topics)
+ ret.shiftBloom<3, 32>(sha3(t.ref()));
+ return ret;
+ }
+
Address from;
- Address to;
- u256 value;
+ h256s topics;
bytes data;
- u256 gas;
+};
+
+using LogEntries = std::vector;
+
+inline LogBloom bloom(LogEntries const& _logs)
+{
+ LogBloom ret;
+ for (auto const& l: _logs)
+ ret |= l.bloom();
+ return ret;
+}
+
+struct SubState
+{
+ std::set suicides; ///< Any accounts that have suicided.
+ LogEntries logs; ///< Any logs.
+ u256 refunds; ///< Refund counter of SSTORE nonzero->zero.
+
+ SubState& operator+=(SubState const& _s)
+ {
+ suicides += _s.suicides;
+ refunds += _s.refunds;
+ suicides += _s.suicides;
+ return *this;
+ }
};
using OnOpFunc = std::function;
@@ -80,7 +123,7 @@ public:
virtual u256 txCount(Address) { return 0; }
/// Suicide the associated contract and give proceeds to the given address.
- virtual void suicide(Address) { suicides.insert(myAddress); }
+ virtual void suicide(Address) { sub.suicides.insert(myAddress); }
/// Create a new (contract) account.
virtual h160 create(u256, u256*, bytesConstRef, OnOpFunc const&) { return h160(); }
@@ -88,6 +131,9 @@ public:
/// Make a new message call.
virtual bool call(Address, u256, bytesConstRef, u256*, bytesRef, OnOpFunc const&, Address, Address) { return false; }
+ /// Revert any changes made (by any of the other calls).
+ virtual void log(h256s&& _topics, bytesConstRef _data) { sub.logs.push_back(LogEntry(myAddress, std::move(_topics), _data.toBytes())); }
+
/// Revert any changes made (by any of the other calls).
virtual void revert() {}
@@ -103,7 +149,7 @@ public:
bytesConstRef code; ///< Current code that is executing.
BlockInfo previousBlock; ///< The previous block's information.
BlockInfo currentBlock; ///< The current block's information.
- std::set suicides; ///< Any accounts that have suicided.
+ SubState sub; ///< Sub-band VM state (suicides, refund counter, logs).
unsigned depth; ///< Depth of the present call.
};
diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp
index d29b9fef9..47236b506 100644
--- a/libevm/FeeStructure.cpp
+++ b/libevm/FeeStructure.cpp
@@ -29,7 +29,9 @@ u256 const dev::eth::c_stepGas = 1;
u256 const dev::eth::c_balanceGas = 20;
u256 const dev::eth::c_sha3Gas = 20;
u256 const dev::eth::c_sloadGas = 20;
-u256 const dev::eth::c_sstoreGas = 100;
+u256 const dev::eth::c_sstoreSetGas = 300;
+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_memoryGas = 1;
diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h
index 76be9a398..84a2551d9 100644
--- a/libevm/FeeStructure.h
+++ b/libevm/FeeStructure.h
@@ -32,7 +32,9 @@ extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD
extern u256 const c_balanceGas; ///< Once per BALANCE operation.
extern u256 const c_sha3Gas; ///< Once per SHA3 operation.
extern u256 const c_sloadGas; ///< Once per SLOAD operation.
-extern u256 const c_sstoreGas; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once).
+extern u256 const c_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero.
+extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness doesn't change.
+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_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.
diff --git a/libevm/VM.cpp b/libevm/VM.cpp
index e47237d5a..bded9a10d 100644
--- a/libevm/VM.cpp
+++ b/libevm/VM.cpp
@@ -29,4 +29,5 @@ void VM::reset(u256 _gas)
{
m_gas = _gas;
m_curPC = 0;
+ m_jumpDests.clear();
}
diff --git a/libevm/VM.h b/libevm/VM.h
index ce8001bbf..e81ddb475 100644
--- a/libevm/VM.h
+++ b/libevm/VM.h
@@ -41,7 +41,7 @@ struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {};
-class StackTooSmall: virtual public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
+struct StackTooSmall: virtual public VMException { StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash.
// Currently we just pull out the right (low-order in BE) 160-bits.
@@ -84,6 +84,7 @@ private:
u256 m_curPC = 0;
bytes m_temp;
u256s m_stack;
+ std::set m_jumpDests;
};
}
@@ -93,6 +94,16 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
{
auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? _offset + _size : 0; };
+ if (m_jumpDests.empty())
+ {
+ m_jumpDests.insert(0);
+ for (unsigned i = 1; i < _ext.code.size(); ++i)
+ if (_ext.code[i] == (byte)Instruction::JUMPDEST)
+ m_jumpDests.insert(i + 1);
+ else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
+ i += _ext.code[i] - (int)Instruction::PUSH1 + 1;
+ }
+
u256 nextPC = m_curPC + 1;
auto osteps = _steps;
for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1)
@@ -117,11 +128,14 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::SSTORE:
require(2);
if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2])
- runGas = c_sstoreGas * 2;
+ runGas = c_sstoreSetGas;
else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
+ {
runGas = 0;
+ _ext.sub.refunds += c_sstoreRefundGas;
+ }
else
- runGas = c_sstoreGas;
+ runGas = c_sstoreResetGas;
break;
case Instruction::SLOAD:
@@ -169,6 +183,18 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
runGas = c_balanceGas;
break;
+ case Instruction::LOG0:
+ case Instruction::LOG1:
+ case Instruction::LOG2:
+ case Instruction::LOG3:
+ case Instruction::LOG4:
+ {
+ unsigned n = (unsigned)inst - (unsigned)Instruction::LOG0;
+ require(n + 2);
+ newTempSize = memNeed(m_stack[m_stack.size() - 1 - n], m_stack[m_stack.size() - 2 - n]);
+ break;
+ }
+
case Instruction::CALL:
case Instruction::CALLCODE:
require(7);
@@ -236,7 +262,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::PUSH31:
case Instruction::PUSH32:
break;
- case Instruction::NEG:
+ case Instruction::BNOT:
case Instruction::NOT:
case Instruction::CALLDATALOAD:
case Instruction::EXTCODESIZE:
@@ -262,6 +288,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::XOR:
case Instruction::BYTE:
case Instruction::JUMPI:
+ case Instruction::SIGNEXTEND:
require(2);
break;
case Instruction::ADDMOD:
@@ -368,8 +395,8 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256);
break;
}
- case Instruction::NEG:
- m_stack.back() = ~(m_stack.back() - 1);
+ case Instruction::BNOT:
+ m_stack.back() = ~m_stack.back();
break;
case Instruction::LT:
m_stack[m_stack.size() - 2] = m_stack.back() < m_stack[m_stack.size() - 2] ? 1 : 0;
@@ -420,6 +447,22 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.pop_back();
m_stack.pop_back();
break;
+ case Instruction::SIGNEXTEND:
+ {
+ unsigned k = m_stack[m_stack.size() - 2];
+ if (k > 31)
+ m_stack[m_stack.size() - 2] = m_stack.back();
+ else
+ {
+ u256 b = m_stack.back();
+ if ((b >> (k * 8)) & 0x80)
+ for (int i = 31; i > k; --i)
+ b |= (u256(0xff) << i);
+ m_stack[m_stack.size() - 2] = b;
+ }
+ m_stack.pop_back();
+ break;
+ }
case Instruction::SHA3:
{
unsigned inOff = (unsigned)m_stack.back();
@@ -646,7 +689,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::JUMP:
nextPC = m_stack.back();
- if (nextPC && (Instruction)_ext.getCode(nextPC - 1) != Instruction::JUMPDEST)
+ if (!m_jumpDests.count((unsigned)nextPC))
BOOST_THROW_EXCEPTION(BadJumpDestination());
m_stack.pop_back();
break;
@@ -654,7 +697,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
if (m_stack[m_stack.size() - 2])
{
nextPC = m_stack.back();
- if (nextPC && (Instruction)_ext.getCode(nextPC - 1) != Instruction::JUMPDEST)
+ if (!m_jumpDests.count((unsigned)nextPC))
BOOST_THROW_EXCEPTION(BadJumpDestination());
}
m_stack.pop_back();
@@ -671,6 +714,36 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::JUMPDEST:
break;
+/* case Instruction::LOG0:
+ _ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
+ break;
+ case Instruction::LOG1:
+ _ext.log({m_stack[m_stack.size() - 1]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 2], (unsigned)m_stack[m_stack.size() - 3]));
+ break;
+ case Instruction::LOG2:
+ _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 3], (unsigned)m_stack[m_stack.size() - 4]));
+ break;
+ case Instruction::LOG3:
+ _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 4], (unsigned)m_stack[m_stack.size() - 5]));
+ break;
+ case Instruction::LOG4:
+ _ext.log({m_stack[m_stack.size() - 1], m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 5], (unsigned)m_stack[m_stack.size() - 6]));
+ break;*/
+ case Instruction::LOG0:
+ _ext.log({}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
+ break;
+ case Instruction::LOG1:
+ _ext.log({m_stack[m_stack.size() - 3]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
+ break;
+ case Instruction::LOG2:
+ _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
+ break;
+ case Instruction::LOG3:
+ _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
+ break;
+ case Instruction::LOG4:
+ _ext.log({m_stack[m_stack.size() - 3], m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5], m_stack[m_stack.size() - 6]}, bytesConstRef(m_temp.data() + (unsigned)m_stack[m_stack.size() - 1], (unsigned)m_stack[m_stack.size() - 2]));
+ break;
case Instruction::CREATE:
{
u256 endowment = m_stack.back();
diff --git a/libevmface/CMakeLists.txt b/libevmface/CMakeLists.txt
index 874b9e397..f82d2b96b 100644
--- a/libevmface/CMakeLists.txt
+++ b/libevmface/CMakeLists.txt
@@ -17,25 +17,6 @@ include_directories(..)
target_link_libraries(${EXECUTABLE} devcore)
-if("${TARGET_PLATFORM}" STREQUAL "w64")
- target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s)
- target_link_libraries(${EXECUTABLE} iphlpapi)
- target_link_libraries(${EXECUTABLE} ws2_32)
- target_link_libraries(${EXECUTABLE} mswsock)
- target_link_libraries(${EXECUTABLE} shlwapi)
-elseif (APPLE)
- # Latest mavericks boost libraries only come with -mt
- find_package(Threads REQUIRED)
- target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
-elseif (UNIX)
- target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY})
- target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
-else ()
- target_link_libraries(${EXECUTABLE} boost_thread)
- find_package(Threads REQUIRED)
- target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
-endif ()
-
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
diff --git a/libevmface/Instruction.cpp b/libevmface/Instruction.cpp
index 7b253f388..2c2af8eb0 100644
--- a/libevmface/Instruction.cpp
+++ b/libevmface/Instruction.cpp
@@ -39,7 +39,7 @@ const std::map dev::eth::c_instructions =
{ "MOD", Instruction::MOD },
{ "SMOD", Instruction::SMOD },
{ "EXP", Instruction::EXP },
- { "NEG", Instruction::NEG },
+ { "BNOT", Instruction::BNOT },
{ "LT", Instruction::LT },
{ "GT", Instruction::GT },
{ "SLT", Instruction::SLT },
@@ -52,6 +52,7 @@ const std::map dev::eth::c_instructions =
{ "BYTE", Instruction::BYTE },
{ "ADDMOD", Instruction::ADDMOD },
{ "MULMOD", Instruction::MULMOD },
+ { "SIGNEXTEND", Instruction::SIGNEXTEND },
{ "SHA3", Instruction::SHA3 },
{ "ADDRESS", Instruction::ADDRESS },
{ "BALANCE", Instruction::BALANCE },
@@ -148,6 +149,11 @@ const std::map dev::eth::c_instructions =
{ "SWAP14", Instruction::SWAP14 },
{ "SWAP15", Instruction::SWAP15 },
{ "SWAP16", Instruction::SWAP16 },
+ { "LOG0", Instruction::LOG0 },
+ { "LOG1", Instruction::LOG1 },
+ { "LOG2", Instruction::LOG2 },
+ { "LOG3", Instruction::LOG3 },
+ { "LOG4", Instruction::LOG4 },
{ "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL },
{ "CALLCODE", Instruction::CALLCODE },
@@ -166,7 +172,7 @@ static const std::map c_instructionInfo =
{ Instruction::MOD, { "MOD", 0, 2, 1 } },
{ Instruction::SMOD, { "SMOD", 0, 2, 1 } },
{ Instruction::EXP, { "EXP", 0, 2, 1 } },
- { Instruction::NEG, { "NEG", 0, 1, 1 } },
+ { Instruction::BNOT, { "BNOT", 0, 1, 1 } },
{ Instruction::LT, { "LT", 0, 2, 1 } },
{ Instruction::GT, { "GT", 0, 2, 1 } },
{ Instruction::SLT, { "SLT", 0, 2, 1 } },
@@ -179,6 +185,7 @@ static const std::map c_instructionInfo =
{ Instruction::BYTE, { "BYTE", 0, 2, 1 } },
{ Instruction::ADDMOD, { "ADDMOD", 0, 3, 1 } },
{ Instruction::MULMOD, { "MULMOD", 0, 3, 1 } },
+ { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1 } },
{ Instruction::SHA3, { "SHA3", 0, 2, 1 } },
{ Instruction::ADDRESS, { "ADDRESS", 0, 0, 1 } },
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1 } },
@@ -275,6 +282,11 @@ static const std::map c_instructionInfo =
{ Instruction::SWAP14, { "SWAP14", 0, 15, 15 } },
{ Instruction::SWAP15, { "SWAP15", 0, 16, 16 } },
{ Instruction::SWAP16, { "SWAP16", 0, 17, 17 } },
+ { Instruction::LOG0, { "LOG0", 0, 1, 0 } },
+ { Instruction::LOG1, { "LOG1", 0, 2, 0 } },
+ { Instruction::LOG2, { "LOG2", 0, 3, 0 } },
+ { Instruction::LOG3, { "LOG3", 0, 4, 0 } },
+ { Instruction::LOG4, { "LOG4", 0, 5, 0 } },
{ Instruction::CREATE, { "CREATE", 0, 3, 1 } },
{ Instruction::CALL, { "CALL", 0, 7, 1 } },
{ Instruction::CALLCODE, { "CALLCODE", 0, 7, 1 } },
diff --git a/libevmface/Instruction.h b/libevmface/Instruction.h
index b6aa477b1..ea355fab1 100644
--- a/libevmface/Instruction.h
+++ b/libevmface/Instruction.h
@@ -44,7 +44,7 @@ enum class Instruction: uint8_t
MOD, ///< modulo remainder operation
SMOD, ///< signed modulo remainder operation
EXP, ///< exponential operation
- NEG, ///< negation operation
+ BNOT, ///< bitwise not
LT, ///< less-than comparision
GT, ///< greater-than comparision
SLT, ///< signed less-than comparision
@@ -58,6 +58,7 @@ enum class Instruction: uint8_t
BYTE, ///< retrieve single byte from word
ADDMOD, ///< unsigned modular addition
MULMOD, ///< unsigned modular multiplication
+ SIGNEXTEND, ///< extend length of signed integer
SHA3 = 0x20, ///< compute SHA3-256 hash
ADDRESS = 0x30, ///< get address of currently executing account
@@ -161,6 +162,12 @@ enum class Instruction: uint8_t
SWAP15, ///< swaps the highest and 16th highest value on the stack
SWAP16, ///< swaps the highest and 17th highest value on the stack
+ LOG0 = 0xa0, ///< Makes a log entry; no topics.
+ LOG1, ///< Makes a log entry; 1 topic.
+ LOG2, ///< Makes a log entry; 2 topics.
+ LOG3, ///< Makes a log entry; 3 topics.
+ LOG4, ///< Makes a log entry; 4 topics.
+
CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
RETURN, ///< halt execution returning output data
diff --git a/liblll/Assembly.cpp b/liblll/Assembly.cpp
index c26a9a98e..5b10138d1 100644
--- a/liblll/Assembly.cpp
+++ b/liblll/Assembly.cpp
@@ -147,7 +147,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
return _out;
}
-ostream& Assembly::streamOut(ostream& _out, string const& _prefix) const
+ostream& Assembly::streamRLP(ostream& _out, string const& _prefix) const
{
_out << _prefix << ".code:" << endl;
for (AssemblyItem const& i: m_items)
@@ -189,7 +189,7 @@ ostream& Assembly::streamOut(ostream& _out, string const& _prefix) const
for (auto const& i: m_subs)
{
_out << _prefix << " " << hex << (unsigned)(u256)i.first << ": " << endl;
- i.second.streamOut(_out, _prefix + " ");
+ i.second.streamRLP(_out, _prefix + " ");
}
}
return _out;
diff --git a/liblll/Assembly.h b/liblll/Assembly.h
index b7feaf4f4..8ab3062dc 100644
--- a/liblll/Assembly.h
+++ b/liblll/Assembly.h
@@ -104,11 +104,11 @@ public:
void injectStart(AssemblyItem const& _i);
- std::string out() const { std::stringstream ret; streamOut(ret); return ret.str(); }
+ std::string out() const { std::stringstream ret; streamRLP(ret); return ret.str(); }
int deposit() const { return m_deposit; }
bytes assemble() const;
Assembly& optimise(bool _enable);
- std::ostream& streamOut(std::ostream& _out, std::string const& _prefix = "") const;
+ std::ostream& streamRLP(std::ostream& _out, std::string const& _prefix = "") const;
private:
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
@@ -127,7 +127,7 @@ private:
inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a)
{
- _a.streamOut(_out);
+ _a.streamRLP(_out);
return _out;
}
diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp
index ff6bdf996..020c7e420 100644
--- a/libp2p/Host.cpp
+++ b/libp2p/Host.cpp
@@ -143,6 +143,9 @@ void Host::stop()
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();
m_ioService.reset();
// m_acceptor & m_socket are DANGEROUS now.
@@ -249,17 +252,27 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp)
m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p);
else
{
- m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p);
+ 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.
- m_public = bi::tcp::endpoint(_publicAddress.size() ? bi::address::from_string(_publicAddress)
- : m_peerAddresses.size() ? m_peerAddresses[0]
- : bi::address(), m_listenPort);
- m_addresses.push_back(m_public.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);
}
}
@@ -366,7 +379,7 @@ void Host::populateAddresses()
shared_ptr Host::noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId)
{
RecursiveGuard l(x_peers);
- if (_a.port() < 30300 && _a.port() > 30303)
+ if (_a.port() < 30300 || _a.port() > 30303)
cwarn << "Wierd port being recorded!";
if (_a.port() >= /*49152*/32768)
@@ -648,23 +661,26 @@ void Host::prunePeers()
{
RecursiveGuard l(x_peers);
// We'll keep at most twice as many as is ideal, halfing what counts as "too young to kill" until we get there.
- for (unsigned old = 15000; m_peers.size() > m_idealPeerCount * 2 && old > 100; old /= 2)
- while (m_peers.size() > m_idealPeerCount)
+ set dc;
+ for (unsigned old = 15000; m_peers.size() - dc.size() > m_idealPeerCount * 2 && old > 100; old /= 2)
+ if (m_peers.size() - dc.size() > m_idealPeerCount)
{
// look for worst peer to kick off
// first work out how many are old enough to kick off.
shared_ptr worst;
unsigned agedPeers = 0;
for (auto i: m_peers)
- if (auto p = i.second.lock())
- if (/*(m_mode != NodeMode::Host || p->m_caps != 0x01) &&*/ chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers.
- {
- ++agedPeers;
- if ((!worst || p->rating() < worst->rating() || (p->rating() == worst->rating() && p->m_connect > worst->m_connect))) // kill older ones
- worst = p;
- }
+ if (!dc.count(i.first))
+ if (auto p = i.second.lock())
+ if (/*(m_mode != NodeMode::Host || p->m_caps != 0x01) &&*/ chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers.
+ {
+ ++agedPeers;
+ if ((!worst || p->rating() < worst->rating() || (p->rating() == worst->rating() && p->m_connect > worst->m_connect))) // kill older ones
+ worst = p;
+ }
if (!worst || agedPeers <= m_idealPeerCount)
break;
+ dc.insert(worst->id());
worst->disconnect(TooManyPeers);
}
diff --git a/libp2p/Host.h b/libp2p/Host.h
index 34179a3b4..6e60b915e 100644
--- a/libp2p/Host.h
+++ b/libp2p/Host.h
@@ -203,7 +203,7 @@ private:
/// This won't touch alter the blockchain.
virtual void doWork();
- std::shared_ptr noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = h256());
+ std::shared_ptr noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = NodeId());
Nodes potentialPeers(RangeMask const& _known);
std::string m_clientVersion; ///< Our version string.
diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp
index beb6f161d..c4385df5f 100644
--- a/libp2p/Session.cpp
+++ b/libp2p/Session.cpp
@@ -475,7 +475,6 @@ void Session::drop(DisconnectReason _reason)
{
if (m_dropped)
return;
- cerr << (void*)this << " dropped" << endl;
if (m_socket.is_open())
try
{
diff --git a/libpyserpent/pyserpent.cpp b/libpyserpent/pyserpent.cpp
index d97fd8fed..ff8813fcf 100644
--- a/libpyserpent/pyserpent.cpp
+++ b/libpyserpent/pyserpent.cpp
@@ -120,11 +120,15 @@ std::vector cppifyNodeList(PyObject* o) {
}
PYMETHOD(ps_compile, FROMSTR, compile, pyifyString)
+PYMETHOD(ps_compile_chunk, FROMSTR, compileChunk, pyifyString)
PYMETHOD(ps_compile_to_lll, FROMSTR, compileToLLL, pyifyNode)
+PYMETHOD(ps_compile_chunk_to_lll, FROMSTR, compileChunkToLLL, pyifyNode)
PYMETHOD(ps_compile_lll, FROMNODE, compileLLL, pyifyString)
PYMETHOD(ps_parse, FROMSTR, parseSerpent, pyifyNode)
PYMETHOD(ps_rewrite, FROMNODE, rewrite, pyifyNode)
+PYMETHOD(ps_rewrite_chunk, FROMNODE, rewriteChunk, pyifyNode)
PYMETHOD(ps_pretty_compile, FROMSTR, prettyCompile, pyifyNodeList)
+PYMETHOD(ps_pretty_compile_chunk, FROMSTR, prettyCompileChunk, pyifyNodeList)
PYMETHOD(ps_pretty_compile_lll, FROMNODE, prettyCompileLLL, pyifyNodeList)
PYMETHOD(ps_serialize, FROMLIST, serialize, pyifyString)
PYMETHOD(ps_deserialize, FROMSTR, deserialize, pyifyNodeList)
@@ -134,16 +138,24 @@ PYMETHOD(ps_parse_lll, FROMSTR, parseLLL, pyifyNode)
static PyMethodDef PyextMethods[] = {
{"compile", ps_compile, METH_VARARGS,
"Compile code."},
+ {"compile_chunk", ps_compile_chunk, METH_VARARGS,
+ "Compile code chunk (no wrappers)."},
{"compile_to_lll", ps_compile_to_lll, METH_VARARGS,
"Compile code to LLL."},
+ {"compile_chunk_to_lll", ps_compile_chunk_to_lll, METH_VARARGS,
+ "Compile code chunk to LLL (no wrappers)."},
{"compile_lll", ps_compile_lll, METH_VARARGS,
"Compile LLL to EVM."},
{"parse", ps_parse, METH_VARARGS,
"Parse serpent"},
{"rewrite", ps_rewrite, METH_VARARGS,
"Rewrite parsed serpent to LLL"},
+ {"rewrite_chunk", ps_rewrite_chunk, METH_VARARGS,
+ "Rewrite parsed serpent to LLL (no wrappers)"},
{"pretty_compile", ps_pretty_compile, METH_VARARGS,
"Compile to EVM opcodes"},
+ {"pretty_compile_chunk", ps_pretty_compile_chunk, METH_VARARGS,
+ "Compile chunk to EVM opcodes (no wrappers)"},
{"pretty_compile_lll", ps_pretty_compile_lll, METH_VARARGS,
"Compile LLL to EVM opcodes"},
{"serialize", ps_serialize, METH_VARARGS,
diff --git a/libqethereum/QEthereum.cpp b/libqethereum/QEthereum.cpp
index 267fed35e..89d5f1ea5 100644
--- a/libqethereum/QEthereum.cpp
+++ b/libqethereum/QEthereum.cpp
@@ -1,3 +1,25 @@
+/*
+ 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 .
+*/
+/** @file QEthereum.cpp
+ * @author Gav Wood
+ * @date 2014
+ */
+
+#include
#include
#include
#include
@@ -5,6 +27,8 @@
#include
#include
#include
+#include
+#include
#include "QEthereum.h"
using namespace std;
using namespace dev;
@@ -21,7 +45,7 @@ dev::bytes toBytes(QString const& _s)
else
{
// Binary
- cwarn << "THIS FUNCTIONALITY IS DEPRECATED. DO NOT ASSUME ASCII/BINARY-STRINGS WILL BE ACCEPTED. USE eth.fromAscii().";
+ cwarn << "'" << _s.toStdString() << "': Unrecognised format for number/hash. USE eth.fromAscii() if you mean to convert from ASCII.";
return asBytes(_s);
}
}
@@ -534,8 +558,9 @@ void QEthereum::poll()
// TODO: repot and hook all these up.
-QWhisper::QWhisper(QObject* _p, std::shared_ptr const& _c): QObject(_p), m_face(_c)
+QWhisper::QWhisper(QObject* _p, std::shared_ptr const& _c, QList _ids): QObject(_p), m_face(_c)
{
+ setIdentities(_ids);
}
QWhisper::~QWhisper()
@@ -558,37 +583,250 @@ void QWhisper::faceDieing()
}
-void QWhisper::send(QString /*dev::Address*/ _dest, QString /*ev::KeyPair*/ _from, QString /*dev::h256 const&*/ _topic, QString /*dev::bytes const&*/ _payload)
+static shh::Message toMessage(QString _json)
{
- (void)_dest;
- (void)_from;
- (void)_topic;
- (void)_payload;
+ shh::Message ret;
+
+ QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
+ if (f.contains("from"))
+ ret.setFrom(toPublic(f["from"].toString()));
+ if (f.contains("to"))
+ ret.setTo(toPublic(f["to"].toString()));
+ if (f.contains("payload"))
+ ret.setPayload(toBytes(f["payload"].toString()));
+
+ return ret;
}
-unsigned QWhisper::newWatch(QString _json)
+static shh::Envelope toSealed(QString _json, shh::Message const& _m, Secret _from)
{
- (void)_json;
- return 0;
+ unsigned ttl = 50;
+ unsigned workToProve = 50;
+
+ shh::BuildTopic bt;
+
+ QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
+ if (f.contains("ttl"))
+ ttl = f["ttl"].toInt();
+ if (f.contains("workToProve"))
+ workToProve = f["workToProve"].toInt();
+ if (f.contains("topic"))
+ {
+ if (f["topic"].isString())
+ bt.shift(asBytes(padded(f["topic"].toString(), 32)));
+ else if (f["topic"].isArray())
+ for (auto i: f["topic"].toArray())
+ bt.shift(asBytes(padded(i.toString(), 32)));
+ }
+ return _m.seal(_from, bt, ttl, workToProve);
}
-QString QWhisper::watchMessages(unsigned _w)
+void QWhisper::doPost(QString _json)
{
- (void)_w;
- return "";
+ shh::Message m = toMessage(_json);
+ Secret from;
+
+ if (m.from() && m_ids.count(m.from()))
+ {
+ cwarn << "Silently signing message from identity" << m.from().abridged() << ": User validation hook goes here.";
+ // TODO: insert validification hook here.
+ from = m_ids[m.from()];
+ }
+
+ face()->inject(toSealed(_json, m, from));
+}
+
+void QWhisper::setIdentities(QList const& _l)
+{
+ m_ids.clear();
+ for (auto i: _l)
+ m_ids[i.pub()] = i.secret();
+ emit idsChanged();
+}
+
+static pair toWatch(QString _json)
+{
+ shh::BuildTopicMask bt(shh::BuildTopicMask::Empty);
+ Public to;
+
+ QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
+ if (f.contains("to"))
+ to = toPublic(f["to"].toString());
+
+ if (f.contains("topic"))
+ {
+ if (f["topic"].isString())
+ bt.shift(asBytes(padded(f["topic"].toString(), 32)));
+ else if (f["topic"].isArray())
+ for (auto i: f["topic"].toArray())
+ if (i.isString())
+ bt.shift(asBytes(padded(i.toString(), 32)));
+ else
+ bt.shift();
+ }
+ return make_pair(bt.toTopicMask(), to);
+}
+
+// _json contains
+// topic: the topic as an array of components, some may be null.
+// to: specifies the id to which the message is encrypted. null if broadcast.
+unsigned QWhisper::newWatch(QString _json)
+{
+ auto w = toWatch(_json);
+ auto ret = face()->installWatch(w.first);
+ m_watches.insert(make_pair(ret, w.second));
+ return ret;
}
void QWhisper::killWatch(unsigned _w)
{
- (void)_w;
+ face()->uninstallWatch(_w);
+ m_watches.erase(_w);
}
void QWhisper::clearWatches()
{
+ for (auto i: m_watches)
+ face()->uninstallWatch(i.first);
+ m_watches.clear();
+}
+
+static QString toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m)
+{
+ QJsonObject v;
+ v["hash"] = toQJS(_h);
+
+ v["expiry"] = (int)_e.expiry();
+ v["sent"] = (int)_e.sent();
+ v["ttl"] = (int)_e.ttl();
+ v["workProved"] = (int)_e.workProved();
+ v["topic"] = toQJS(_e.topic());
+
+ v["payload"] = toQJS(_m.payload());
+ v["from"] = toQJS(_m.from());
+ v["to"] = toQJS(_m.to());
+
+ return QString::fromUtf8(QJsonDocument(v).toJson());
+}
+
+QString QWhisper::watchMessages(unsigned _w)
+{
+ QString ret = "[";
+ auto wit = m_watches.find(_w);
+ if (wit == m_watches.end())
+ {
+ cwarn << "watchMessages called with invalid watch id" << _w;
+ return "";
+ }
+ Public p = wit->second;
+ if (!p || m_ids.count(p))
+ for (h256 const& h: face()->watchMessages(_w))
+ {
+ auto e = face()->envelope(h);
+ shh::Message m;
+ if (p)
+ {
+ cwarn << "Silently decrypting message from identity" << p.abridged() << ": User validation hook goes here.";
+ m = e.open(m_ids[p]);
+ }
+ else
+ m = e.open();
+ ret.append((ret == "[" ? "" : ",") + toJson(h, e, m));
+ }
+
+ return ret + "]";
+}
+
+QString QWhisper::newIdentity()
+{
+ return toQJS(makeIdentity());
+}
+
+Public QWhisper::makeIdentity()
+{
+ KeyPair kp = KeyPair::create();
+ emit newIdToAdd(toQJS(kp.sec()));
+ return kp.pub();
+}
+
+QString QWhisper::newGroup(QString _me, QString _others)
+{
+ (void)_me;
+ (void)_others;
+ return "";
+}
+
+QString QWhisper::addToGroup(QString _group, QString _who)
+{
+ (void)_group;
+ (void)_who;
+ return "";
}
void QWhisper::poll()
{
+ for (auto const& w: m_watches)
+ if (!w.second || m_ids.count(w.second))
+ for (h256 const& h: face()->checkWatch(w.first))
+ {
+ auto e = face()->envelope(h);
+ shh::Message m;
+ if (w.second)
+ {
+ cwarn << "Silently decrypting message from identity" << w.second.abridged() << ": User validation hook goes here.";
+ m = e.open(m_ids[w.second]);
+ if (!m)
+ continue;
+ }
+ else
+ m = e.open();
+ emit watchChanged(w.first, toJson(h, e, m));
+ }
+}
+
+#include
+
+QLDB::QLDB(QObject* _p): QObject(_p)
+{
+ auto path = getDataDir() + "/.web3";
+ boost::filesystem::create_directories(path);
+ ldb::Options o;
+ o.create_if_missing = true;
+ ldb::DB::Open(o, path, &m_db);
+}
+
+QLDB::~QLDB()
+{
+}
+
+void QLDB::put(QString _p, QString _k, QString _v)
+{
+ bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes();
+ bytes v = toBytes(_v);
+ m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size()));
+}
+
+QString QLDB::get(QString _p, QString _k)
+{
+ bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes();
+ string ret;
+ m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret);
+ return toQJS(dev::asBytes(ret));
+}
+
+void QLDB::putString(QString _p, QString _k, QString _v)
+{
+ bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes();
+ string v = _v.toStdString();
+ m_db->Put(m_writeOptions, ldb::Slice((char const*)k.data(), k.size()), ldb::Slice((char const*)v.data(), v.size()));
+}
+
+QString QLDB::getString(QString _p, QString _k)
+{
+ bytes k = sha3(_p.toStdString()).asBytes() + sha3(_k.toStdString()).asBytes();
+ string ret;
+ m_db->Get(m_readOptions, ldb::Slice((char const*)k.data(), k.size()), &ret);
+ return QString::fromStdString(ret);
}
// extra bits needed to link on VS
diff --git a/libqethereum/QEthereum.h b/libqethereum/QEthereum.h
index 2d6ea8b4c..b27ca84ca 100644
--- a/libqethereum/QEthereum.h
+++ b/libqethereum/QEthereum.h
@@ -1,11 +1,39 @@
+/*
+ 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 .
+*/
+/** @file QEthereum.h
+ * @author Gav Wood
+ * @date 2014
+ */
+
#pragma once
+#pragma warning(push)
+#pragma warning(disable: 4100 4267)
+#include
+#pragma warning(pop)
+
#include
#include
#include
#include
#include
+namespace ldb = leveldb;
+
namespace dev {
namespace eth {
class Interface;
@@ -59,8 +87,9 @@ template dev::FixedHash toFixed(QString const& _s)
template inline boost::multiprecision::number> toInt(QString const& _s);
-inline dev::Address toAddress(QString const& _s) { return toFixed<20>(_s); }
-inline dev::Secret toSecret(QString const& _s) { return toFixed<32>(_s); }
+inline dev::Address toAddress(QString const& _s) { return toFixed(_s); }
+inline dev::Public toPublic(QString const& _s) { return toFixed(_s); }
+inline dev::Secret toSecret(QString const& _s) { return toFixed(_s); }
inline dev::u256 toU256(QString const& _s) { return toInt<32>(_s); }
template QString toQJS(dev::FixedHash const& _h) { return QString::fromStdString("0x" + toHex(_h.ref())); }
@@ -213,26 +242,36 @@ class QWhisper: public QObject
Q_OBJECT
public:
- QWhisper(QObject* _p, std::shared_ptr const& _c);
+ QWhisper(QObject* _p, std::shared_ptr const& _c, QList _ids);
virtual ~QWhisper();
std::shared_ptr face() const;
void setFace(std::shared_ptr const& _c) { m_face = _c; }
+ void setIdentities(QList const& _l);
+
/// Call when the face() is going to be deleted to make this object useless but safe.
void faceDieing();
Q_INVOKABLE QWhisper* self() { return this; }
/// Basic message send.
- Q_INVOKABLE void send(QString /*dev::Address*/ _dest, QString /*ev::KeyPair*/ _from, QString /*dev::h256 const&*/ _topic, QString /*dev::bytes const&*/ _payload);
+ Q_INVOKABLE void doPost(QString _json);
- // Watches interface
+ Q_INVOKABLE QString newIdentity();
+ Q_INVOKABLE bool haveIdentity(QString _id) { return m_ids.count(toPublic(_id)); }
+
+ Q_INVOKABLE QString newGroup(QString _id, QString _who);
+ Q_INVOKABLE QString addToGroup(QString _group, QString _who);
+ // Watches interface
Q_INVOKABLE unsigned newWatch(QString _json);
- Q_INVOKABLE QString watchMessages(unsigned _w);
Q_INVOKABLE void killWatch(unsigned _w);
- void clearWatches();
+ Q_INVOKABLE void clearWatches();
+ Q_INVOKABLE QString watchMessages(unsigned _w);
+
+ dev::Public makeIdentity();
+ std::map const& ids() const { return m_ids; }
public slots:
/// Check to see if anything has changed, fire off signals if so.
@@ -240,23 +279,52 @@ public slots:
void poll();
signals:
- void watchChanged(unsigned _w);
+ void watchChanged(unsigned _w, QString _envelopeJson);
+ void idsChanged();
+ void newIdToAdd(QString _id);
private:
std::weak_ptr m_face;
- std::vector m_watches;
+ std::map m_watches;
+
+ std::map m_ids;
+};
+
+class QLDB: public QObject
+{
+ Q_OBJECT
+
+public:
+ QLDB(QObject* _p);
+ ~QLDB();
+
+ Q_INVOKABLE void put(QString _name, QString _key, QString _value);
+ Q_INVOKABLE QString get(QString _name, QString _key);
+ Q_INVOKABLE void putString(QString _name, QString _key, QString _value);
+ Q_INVOKABLE QString getString(QString _name, QString _key);
+
+private:
+ ldb::ReadOptions m_readOptions;
+ ldb::WriteOptions m_writeOptions;
+
+ ldb::DB* m_db;
};
// TODO: add p2p object
-#define QETH_INSTALL_JS_NAMESPACE(_frame, _env, _web3, _eth, _shh) [_frame, _env, _web3, _eth, _shh]() \
+#define QETH_INSTALL_JS_NAMESPACE(_frame, _env, _web3, _eth, _shh, _ldb) [_frame, _env, _web3, _eth, _shh, _ldb]() \
{ \
_frame->disconnect(); \
_frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \
_frame->addToJavaScriptWindowObject("web3", _web3, QWebFrame::ScriptOwnership); \
+ if (_ldb) \
+ { \
+ _frame->addToJavaScriptWindowObject("_web3_dot_db", _ldb, QWebFrame::QtOwnership); \
+ _frame->evaluateJavaScript("web3.db = _web3_dot_db"); \
+ } \
if (_eth) \
{ \
_frame->addToJavaScriptWindowObject("_web3_dot_eth", _eth, QWebFrame::ScriptOwnership); \
- _frame->evaluateJavaScript("_web3_dot_eth.makeWatch = function(a) { var ww = _web3_dot_eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_eth.killWatch(w); }; ret.changed = function(f) { _web3_dot_eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(_web3_dot_eth.watchMessages(this.w)) }; return ret; }"); \
+ _frame->evaluateJavaScript("_web3_dot_eth.makeWatch = function(a) { var ww = _web3_dot_eth.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_eth.killWatch(this.w); }; ret.changed = function(f) { _web3_dot_eth.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(_web3_dot_eth.watchMessages(this.w)) }; return ret; }"); \
_frame->evaluateJavaScript("_web3_dot_eth.watch = function(a) { return _web3_dot_eth.makeWatch(JSON.stringify(a)) }"); \
_frame->evaluateJavaScript("_web3_dot_eth.transact = function(a, f) { var r = _web3_dot_eth.doTransact(JSON.stringify(a)); if (f) f(r); }"); \
_frame->evaluateJavaScript("_web3_dot_eth.call = function(a, f) { var ret = _web3_dot_eth.doCallJson(JSON.stringify(a)); if (f) f(ret); return ret; }"); \
@@ -269,8 +337,9 @@ private:
if (_shh) \
{ \
_frame->addToJavaScriptWindowObject("_web3_dot_shh", _shh, QWebFrame::ScriptOwnership); \
- _frame->evaluateJavaScript("_web3_dot_shh.makeWatch = function(a) { var ww = _web3_dot_shh.newWatch(a); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_shh.killWatch(w); }; ret.changed = function(f) { _web3_dot_shh.watchChanged.connect(function(nw) { if (nw == ww) f() }); }; ret.messages = function() { return JSON.parse(_web3_dot_shh.watchMessages(this.w)) }; return ret; }"); \
- _frame->evaluateJavaScript("_web3_dot_shh.watch = function(a) { return _web3_dot_shh.makeWatch(JSON.stringify(a)) }"); \
+ _frame->evaluateJavaScript("_web3_dot_shh.makeWatch = function(json) { var ww = _web3_dot_shh.newWatch(json); var ret = { w: ww }; ret.uninstall = function() { _web3_dot_shh.killWatch(this.w); }; ret.arrived = function(f) { _web3_dot_shh.watchChanged.connect(function(nw, envelope) { if (nw == ww) f(JSON.parse(envelope)) }); var existing = JSON.parse(_web3_dot_shh.watchMessages(this.w)); for (var e in existing) f(existing[e]) }; return ret; }"); \
+ _frame->evaluateJavaScript("_web3_dot_shh.watch = function(filter) { return _web3_dot_shh.makeWatch(JSON.stringify(filter)) }"); \
+ _frame->evaluateJavaScript("_web3_dot_shh.post = function(message) { return _web3_dot_shh.doPost(JSON.stringify(message)) }"); \
_frame->evaluateJavaScript("web3.shh = _web3_dot_shh"); \
} \
}
diff --git a/libserpent/compiler.cpp b/libserpent/compiler.cpp
index 4360bfba6..251c7d9da 100644
--- a/libserpent/compiler.cpp
+++ b/libserpent/compiler.cpp
@@ -123,9 +123,10 @@ programData opcodeify(Node node,
token("$begincode"+symb+".endcode"+symb, m), token("DUP1", m),
token("$begincode"+symb, m), sub.code, token("CODECOPY", m),
token("$endcode"+symb, m), token("JUMP", m),
- token("~begincode"+symb, m), code, token("~endcode"+symb, m)
+ token("~begincode"+symb, m), code, token("~endcode"+symb, m),
+ token("JUMPDEST", m)
};
- return pd(sub.aux, multiToken(nodelist, 10, m), 1);
+ return pd(sub.aux, multiToken(nodelist, 11, m), 1);
}
// Stack variables
if (node.val == "with") {
@@ -171,9 +172,9 @@ programData opcodeify(Node node,
cond.code,
token("$endif"+symb, m), token("JUMPI", m),
action.code,
- token("~endif"+symb, m)
+ token("~endif"+symb, m), token("JUMPDEST", m)
};
- return pd(aux, multiToken(nodelist, 5, m), 0);
+ return pd(aux, multiToken(nodelist, 6, m), 0);
}
// 3-part conditional
else if (node.val == "if" && node.args.size() == 3) {
@@ -190,13 +191,15 @@ programData opcodeify(Node node,
if (elsed.outs > outs) elsed.code = popwrap(elsed.code);
Node nodelist[] = {
ifd.code,
- token("NOT", m), token("$else"+symb, m), token("JUMPI", m),
+ token("NOT", m),
+ token("$else"+symb, m), token("JUMPI", m),
thend.code,
- token("$endif"+symb, m), token("JUMP", m), token("~else"+symb, m),
+ token("$endif"+symb, m), token("JUMP", m),
+ token("~else"+symb, m), token("JUMPDEST", m),
elsed.code,
- token("~endif"+symb, m)
+ token("~endif"+symb, m), token("JUMPDEST", m)
};
- return pd(aux, multiToken(nodelist, 10, m), outs);
+ return pd(aux, multiToken(nodelist, 12, m), outs);
}
// While (rewritten to this in rewrites)
else if (node.val == "until") {
@@ -207,13 +210,14 @@ programData opcodeify(Node node,
err("Condition of while/until loop has arity 0", m);
if (action.outs) action.code = popwrap(action.code);
Node nodelist[] = {
- token("~beg"+symb, m),
+ token("~beg"+symb, m), token("JUMPDEST", m),
cond.code,
token("$end"+symb, m), token("JUMPI", m),
action.code,
- token("$beg"+symb, m), token("JUMP", m), token("~end"+symb, m)
+ token("$beg"+symb, m), token("JUMP", m),
+ token("~end"+symb, m), token("JUMPDEST", m)
};
- return pd(aux, multiToken(nodelist, 8, m));
+ return pd(aux, multiToken(nodelist, 10, m));
}
// Memory allocations
else if (node.val == "alloc") {
@@ -298,7 +302,7 @@ Node finalize(programData c) {
Metadata m = c.code.metadata;
// If we are using both alloc and variables, we need to pre-zfill
// some memory
- if (c.aux.allocUsed && c.aux.vars.size() > 0) {
+ if ((c.aux.allocUsed || c.aux.calldataUsed) && c.aux.vars.size() > 0) {
Node nodelist[] = {
token("0", m),
token(unsignedToDecimal(c.aux.vars.size() * 32 - 1)),
@@ -309,8 +313,8 @@ Node finalize(programData c) {
// If msg.data is being used as an array, then we need to copy it
if (c.aux.calldataUsed) {
Node nodelist[] = {
- token("MSIZE", m), token("CALLDATASIZE", m), token("MSIZE", m),
- token("0", m), token("CALLDATACOPY", m),
+ token("MSIZE", m), token("CALLDATASIZE", m), token("0", m),
+ token("MSIZE", m), token("CALLDATACOPY", m),
token(c.aux.vars["'msg.data"], m), token("MSTORE", m)
};
bottom.push_back(multiToken(nodelist, 7, m));
diff --git a/libserpent/funcs.cpp b/libserpent/funcs.cpp
index e80fcff06..ea9be14a6 100644
--- a/libserpent/funcs.cpp
+++ b/libserpent/funcs.cpp
@@ -14,6 +14,10 @@ Node compileToLLL(std::string input) {
return rewrite(parseSerpent(input));
}
+Node compileChunkToLLL(std::string input) {
+ return rewriteChunk(parseSerpent(input));
+}
+
std::string compile(std::string input) {
return compileLLL(compileToLLL(input));
}
@@ -21,3 +25,11 @@ std::string compile(std::string input) {
std::vector prettyCompile(std::string input) {
return prettyCompileLLL(compileToLLL(input));
}
+
+std::string compileChunk(std::string input) {
+ return compileLLL(compileChunkToLLL(input));
+}
+
+std::vector prettyCompileChunk(std::string input) {
+ return prettyCompileLLL(compileChunkToLLL(input));
+}
diff --git a/libserpent/funcs.h b/libserpent/funcs.h
index 0f3355ec8..d9bf44549 100644
--- a/libserpent/funcs.h
+++ b/libserpent/funcs.h
@@ -24,6 +24,12 @@
Node compileToLLL(std::string input);
+Node compileChunkToLLL(std::string input);
+
std::string compile(std::string input);
std::vector prettyCompile(std::string input);
+
+std::string compileChunk(std::string input);
+
+std::vector prettyCompileChunk(std::string input);
diff --git a/libserpent/opcodes.h b/libserpent/opcodes.h
index f55834efa..552364731 100644
--- a/libserpent/opcodes.h
+++ b/libserpent/opcodes.h
@@ -72,11 +72,11 @@ Mapping mapping[] = {
Mapping("PC", 0x5a, 0, 1),
Mapping("MSIZE", 0x5b, 0, 1),
Mapping("GAS", 0x5c, 0, 1),
+ Mapping("JUMPDEST", 0x5d, 0, 0),
Mapping("CREATE", 0xf0, 3, 1),
Mapping("CALL", 0xf1, 7, 1),
Mapping("RETURN", 0xf2, 2, 0),
- Mapping("POST", 0xf3, 5, 0),
- Mapping("CALL_STATELESS", 0xf4, 7, 1),
+ Mapping("CALL_CODE", 0xf3, 7, 1),
Mapping("SUICIDE", 0xff, 1, 0),
Mapping("---END---", 0x00, 0, 0),
};
diff --git a/libserpent/parser.cpp b/libserpent/parser.cpp
index 5adf1672d..0460c974c 100644
--- a/libserpent/parser.cpp
+++ b/libserpent/parser.cpp
@@ -32,7 +32,7 @@ int toktype(Node tok) {
if (v == "(" || v == "[" || v == "{") return LPAREN;
else if (v == ")" || v == "]" || v == "}") return RPAREN;
else if (v == ",") return COMMA;
- else if (v == "!" || v == "not") return UNARY_OP;
+ else if (v == "!" || v == "not" || v == "neg") return UNARY_OP;
else if (precedence(tok) >= 0) return BINARY_OP;
if (tok.val[0] != '"' && tok.val[0] != '\'') {
for (unsigned i = 0; i < tok.val.length(); i++) {
@@ -91,17 +91,19 @@ std::vector shuntingYard(std::vector tokens) {
// If binary op, keep popping from stack while higher bedmas precedence
else if (toktyp == BINARY_OP) {
if (tok.val == "-" && prevtyp != ALPHANUM && prevtyp != RPAREN) {
- oq.push_back(token("0", tok.metadata));
+ stack.push_back(token("neg", tok.metadata));
}
- int prec = precedence(tok);
- while (stack.size()
- && (toktype(stack.back()) == BINARY_OP
- || toktype(stack.back()) == UNARY_OP)
- && precedence(stack.back()) <= prec) {
- oq.push_back(stack.back());
- stack.pop_back();
+ else {
+ int prec = precedence(tok);
+ while (stack.size()
+ && (toktype(stack.back()) == BINARY_OP
+ || toktype(stack.back()) == UNARY_OP)
+ && precedence(stack.back()) <= prec) {
+ oq.push_back(stack.back());
+ stack.pop_back();
+ }
+ stack.push_back(tok);
}
- stack.push_back(tok);
}
// Comma means finish evaluating the argument
else if (toktyp == COMMA) {
@@ -153,10 +155,10 @@ Node treefy(std::vector stream) {
else if (typ == RPAREN) {
std::vector args;
while (1) {
- if (!oq.size()) err("Bracket without matching", tok.metadata);
if (toktype(oq.back()) == LPAREN) break;
args.push_back(oq.back());
oq.pop_back();
+ if (!oq.size()) err("Bracket without matching", tok.metadata);
}
oq.pop_back();
args.push_back(oq.back());
diff --git a/libserpent/rewriter.cpp b/libserpent/rewriter.cpp
index 72feb1277..bf6a73828 100644
--- a/libserpent/rewriter.cpp
+++ b/libserpent/rewriter.cpp
@@ -17,9 +17,7 @@ std::string valid[][3] = {
{ "alloc", "1", "1" },
{ "array", "1", "1" },
{ "call", "2", "4" },
- { "call_stateless", "2", "4" },
- { "post", "4", "5" },
- { "postcall", "3", "4" },
+ { "call_code", "2", "4" },
{ "create", "1", "4" },
{ "msg", "4", "6" },
{ "msg_stateless", "4", "6" },
@@ -28,6 +26,8 @@ std::string valid[][3] = {
{ "sha3", "1", "2" },
{ "return", "1", "2" },
{ "inset", "1", "1" },
+ { "min", "2", "2" },
+ { "max", "2", "2" },
{ "array_lit", "0", tt256 },
{ "seq", "0", tt256 },
{ "---END---", "", "" } //Keep this line at the end of the list
@@ -70,6 +70,14 @@ std::string macros[][2] = {
"(!= $a $b)",
"(not (eq $a $b))"
},
+ {
+ "(min a b)",
+ "(with $1 a (with $2 b (if (lt $1 $2) $1 $2)))"
+ },
+ {
+ "(max a b)",
+ "(with $1 a (with $2 b (if (lt $1 $2) $2 $1)))"
+ },
{
"(if $cond $do (else $else))",
"(if $cond $do $else)"
@@ -134,22 +142,6 @@ std::string macros[][2] = {
"(send $to $value)",
"(call (sub (gas) 25) $to $value 0 0 0 0)"
},
- {
- "(post $gas $to $value $datain $datainsz)",
- "(~post $gas $to $value $datain (mul $datainsz 32))"
- },
- {
- "(post $gas $to $value $datain)",
- "(seq (set $1 $datain) (~post $gas $to $value (ref $1) 32))"
- },
- {
- "(postcall $gas $to $datain)",
- "(post $gas $to 0 $datain)",
- },
- {
- "(postcall $gas $to $datain $datainsz)",
- "(post $gas $to 0 $datain $datainsz)",
- },
{
"(send $gas $to $value)",
"(call $gas $to $value 0 0 0 0)"
@@ -229,32 +221,32 @@ std::string macros[][2] = {
},
{
"(msg $gas $to $val $inp $inpsz $outsz)",
- "(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (call $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2)))"
+ "(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (seq (call $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2))))"
},
// Call stateless and msg stateless
{
- "(call_stateless $f $dataval)",
- "(msg_stateless (sub (gas) 45) $f 0 $dataval)"
+ "(call_code $f $dataval)",
+ "(msg_code (sub (gas) 45) $f 0 $dataval)"
},
{
- "(call_stateless $f $inp $inpsz)",
- "(msg_stateless (sub (gas) 25) $f 0 $inp $inpsz)"
+ "(call_code $f $inp $inpsz)",
+ "(msg_code (sub (gas) 25) $f 0 $inp $inpsz)"
},
{
- "(call_stateless $f $inp $inpsz $outsz)",
- "(with $1 $outsz (with $2 (alloc (mul 32 (get $1))) (seq (call_stateless (sub (gas) (add 25 (get $1))) $f 0 $inp (mul 32 $inpsz) (get $2) (mul 32 (get $1))) (get $2))))"
+ "(call_code $f $inp $inpsz $outsz)",
+ "(with $1 $outsz (with $2 (alloc (mul 32 (get $1))) (seq (call_code (sub (gas) (add 25 (get $1))) $f 0 $inp (mul 32 $inpsz) (get $2) (mul 32 (get $1))) (get $2))))"
},
{
- "(msg_stateless $gas $to $val $inp $inpsz)",
- "(seq (call_stateless $gas $to $val $inp (mul 32 $inpsz) (ref $1) 32) (get $1))"
+ "(msg_code $gas $to $val $inp $inpsz)",
+ "(seq (call_code $gas $to $val $inp (mul 32 $inpsz) (ref $1) 32) (get $1))"
},
{
- "(msg_stateless $gas $to $val $dataval)",
- "(seq (set $1 $dataval) (call_stateless $gas $to $val (ref $1) 32 (ref $2) 32) (get $2))"
+ "(msg_code $gas $to $val $dataval)",
+ "(seq (set $1 $dataval) (call_code $gas $to $val (ref $1) 32 (ref $2) 32) (get $2))"
},
{
- "(msg_stateless $gas $to $val $inp $inpsz $outsz)",
- "(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (call_stateless $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2)))"
+ "(msg_code $gas $to $val $inp $inpsz $outsz)",
+ "(with $1 (mul 32 $outsz) (with $2 (alloc (get $1)) (call_code $gas $to $val $inp (mul 32 $inpsz) (get $2) (get $1)) (get $2)))"
},
// Wrappers
{
@@ -561,4 +553,8 @@ Node rewrite(Node inp) {
return optimize(apply_rules(validate(preprocess(inp))));
}
+Node rewriteChunk(Node inp) {
+ return optimize(apply_rules(validate(inp)));
+}
+
using namespace std;
diff --git a/libserpent/rewriter.h b/libserpent/rewriter.h
index b2e8f00dd..716815cee 100644
--- a/libserpent/rewriter.h
+++ b/libserpent/rewriter.h
@@ -10,4 +10,7 @@
// Applies rewrite rules
Node rewrite(Node inp);
+// Applies rewrite rules adding without wrapper
+Node rewriteChunk(Node inp);
+
#endif
diff --git a/libserpent/util.cpp b/libserpent/util.cpp
index 693427c3f..cc1394a21 100644
--- a/libserpent/util.cpp
+++ b/libserpent/util.cpp
@@ -137,7 +137,7 @@ std::string strToNumeric(std::string inp) {
}
else if (inp.substr(0,2) == "0x") {
for (unsigned i = 2; i < inp.length(); i++) {
- int dig = std::string("0123456789abcdef").find(inp[i]);
+ int dig = std::string("0123456789abcdef0123456789ABCDEF").find(inp[i]) % 16;
if (dig < 0) return "";
o = decimalAdd(decimalMul(o,"16"), unsignedToDecimal(dig));
}
@@ -219,8 +219,8 @@ void err(std::string errtext, Metadata met) {
std::string err = "Error (file \"" + met.file + "\", line " +
unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) +
"): " + errtext;
- std::cerr << err << "\n";
- throw(err);
+ std::cerr << err << "\n";
+ throw(err);
}
//Bin to hex
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index 59aa78364..757d0cc06 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -16,33 +16,8 @@ file(GLOB HEADERS "*.h")
include_directories(..)
-target_link_libraries(${EXECUTABLE} evmface)
target_link_libraries(${EXECUTABLE} devcore)
-
-
-if("${TARGET_PLATFORM}" STREQUAL "w64")
- target_link_libraries(${EXECUTABLE} boost_system-mt-s)
- target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s)
- target_link_libraries(${EXECUTABLE} iphlpapi)
- target_link_libraries(${EXECUTABLE} ws2_32)
- target_link_libraries(${EXECUTABLE} mswsock)
- target_link_libraries(${EXECUTABLE} shlwapi)
-elseif (APPLE)
- # Latest mavericks boost libraries only come with -mt
- target_link_libraries(${EXECUTABLE} boost_system-mt)
- target_link_libraries(${EXECUTABLE} boost_thread-mt)
- find_package(Threads REQUIRED)
- target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
-elseif (UNIX)
- target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY})
- target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY})
- target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
-else ()
- target_link_libraries(${EXECUTABLE} boost_system)
- target_link_libraries(${EXECUTABLE} boost_thread)
- find_package(Threads REQUIRED)
- target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT})
-endif ()
+target_link_libraries(${EXECUTABLE} evmface)
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp
index cf901e64b..05b12df09 100644
--- a/libsolidity/Types.cpp
+++ b/libsolidity/Types.cpp
@@ -53,6 +53,7 @@ std::shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken)
return std::make_shared();
else
assert(false); // @todo add other tyes
+ return std::shared_ptr();
}
std::shared_ptr Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp
index e4903821c..15b07e433 100644
--- a/libwhisper/Common.cpp
+++ b/libwhisper/Common.cpp
@@ -36,17 +36,23 @@ BuildTopic& BuildTopic::shiftBytes(bytes const& _b)
h256 TopicFilter::sha3() const
{
RLPStream s;
- fillStream(s);
+ streamRLP(s);
return dev::sha3(s.out());
}
TopicMask BuildTopicMask::toTopicMask() const
{
TopicMask ret;
- for (auto i = 0; i < 32; ++i)
- {
- ret.first[i] = m_parts[i * m_parts.size() / 32][i];
- ret.second[i] = m_parts[i * m_parts.size() / 32] ? 255 : 0;
- }
+ if (m_parts.size())
+ for (auto i = 0; i < 32; ++i)
+ {
+ ret.first[i] = m_parts[i * m_parts.size() / 32][i];
+ ret.second[i] = m_parts[i * m_parts.size() / 32] ? 255 : 0;
+ }
return ret;
}
+/*
+web3.shh.watch({}).arrived(function(m) { env.note("New message:\n"+JSON.stringify(m)); })
+k = web3.shh.newIdentity()
+web3.shh.post({from: k, topic: web3.fromAscii("test"), payload: web3.fromAscii("Hello world!")})
+*/
diff --git a/libwhisper/Common.h b/libwhisper/Common.h
index 1f4c4a17f..21296e193 100644
--- a/libwhisper/Common.h
+++ b/libwhisper/Common.h
@@ -64,6 +64,7 @@ using Topic = h256;
class BuildTopic
{
public:
+ BuildTopic() {}
template BuildTopic(T const& _t) { shift(_t); }
template BuildTopic& shift(T const& _r) { return shiftBytes(RLPStream().append(_r).out()); }
@@ -75,8 +76,6 @@ public:
Topic toTopic() const { Topic ret; for (auto i = 0; i < 32; ++i) ret[i] = m_parts[i * m_parts.size() / 32][i]; return ret; }
protected:
- BuildTopic() {}
-
BuildTopic& shiftBytes(bytes const& _b);
h256s m_parts;
@@ -93,7 +92,7 @@ public:
TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {}
TopicFilter(RLP const& _r): m_topicMasks((TopicMasks)_r) {}
- void fillStream(RLPStream& _s) const { _s << m_topicMasks; }
+ void streamRLP(RLPStream& _s) const { _s << m_topicMasks; }
h256 sha3() const;
bool matches(Envelope const& _m) const;
@@ -105,7 +104,10 @@ private:
class BuildTopicMask: BuildTopic
{
public:
+ enum EmptyType { Empty };
+
BuildTopicMask() { shift(); }
+ BuildTopicMask(EmptyType) {}
template BuildTopicMask(T const& _t) { shift(_t); }
template BuildTopicMask& shift(T const& _r) { BuildTopic::shift(_r); return *this; }
diff --git a/libwhisper/Interface.h b/libwhisper/Interface.h
index e131a3ada..56b3be599 100644
--- a/libwhisper/Interface.h
+++ b/libwhisper/Interface.h
@@ -43,6 +43,8 @@ namespace shh
Topic mask;
};*/
+class Watch;
+
struct InstalledFilter
{
InstalledFilter(TopicFilter const& _f): filter(_f) {}
@@ -73,13 +75,14 @@ public:
virtual void uninstallWatch(unsigned _watchId) = 0;
virtual h256s peekWatch(unsigned _watchId) const = 0;
virtual h256s checkWatch(unsigned _watchId) = 0;
+ virtual h256s watchMessages(unsigned _watchId) = 0;
virtual Envelope envelope(h256 _m) const = 0;
void post(bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); }
- void post(Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_to, _topic, _ttl, _workToProve)); }
+ void post(Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topic, _ttl, _workToProve)); }
void post(Secret _from, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); }
- void post(Secret _from, Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _to, _topic, _ttl, _workToProve)); }
+ void post(Secret _from, Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topic, _ttl, _workToProve)); }
};
struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; };
@@ -87,9 +90,11 @@ struct WatshhChannel: public dev::LogChannel { static const char* name() { retur
}
}
-/*
-namespace std { void swap(shh::Watch& _a, shh::Watch& _b); }
+namespace std { void swap(dev::shh::Watch& _a, dev::shh::Watch& _b); }
+
+namespace dev
+{
namespace shh
{
@@ -99,28 +104,29 @@ class Watch: public boost::noncopyable
public:
Watch() {}
- Watch(Whisper& _c, h256 _f): m_c(&_c), m_id(_c.installWatch(_f)) {}
- Watch(Whisper& _c, TopicFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {}
+ Watch(Interface& _c, TopicMask const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {}
+ Watch(Interface& _c, TopicFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {}
~Watch() { if (m_c) m_c->uninstallWatch(m_id); }
- bool check() { return m_c ? m_c->checkWatch(m_id) : false; }
- bool peek() { return m_c ? m_c->peekWatch(m_id) : false; }
+ h256s check() { return m_c ? m_c->checkWatch(m_id) : h256s(); }
+ h256s peek() { return m_c ? m_c->peekWatch(m_id) : h256s(); }
private:
- Whisper* m_c;
+ Interface* m_c;
unsigned m_id;
};
+}
}
-namespace shh
+namespace std
{
-inline void swap(shh::Watch& _a, shh::Watch& _b)
+inline void swap(dev::shh::Watch& _a, dev::shh::Watch& _b)
{
swap(_a.m_c, _b.m_c);
swap(_a.m_id, _b.m_id);
}
}
-*/
+
diff --git a/libwhisper/Message.cpp b/libwhisper/Message.cpp
index 80b493076..93dcaa033 100644
--- a/libwhisper/Message.cpp
+++ b/libwhisper/Message.cpp
@@ -34,33 +34,36 @@ Message::Message(Envelope const& _e, Secret const& _s)
if (_s)
if (!decrypt(_s, &(_e.data()), b))
return;
- populate(_s ? b : _e.data());
- m_to = KeyPair(_s).pub();
+ if (populate(_s ? b : _e.data()))
+ m_to = KeyPair(_s).pub();
}
catch (...) // Invalid secret? TODO: replace ... with InvalidSecret
{
}
}
-void Message::populate(bytes const& _data)
+bool Message::populate(bytes const& _data)
{
if (!_data.size())
- return;
+ return false;
byte flags = _data[0];
- if (!!(flags & ContainsSignature) && _data.size() > sizeof(Signature) + 1) // has a signature
+ if (!!(flags & ContainsSignature) && _data.size() >= sizeof(Signature) + 1) // has a signature
{
bytesConstRef payload = bytesConstRef(&_data).cropped(1, _data.size() - sizeof(Signature) - 1);
h256 h = sha3(payload);
Signature const& sig = *(Signature const*)&(_data[1 + payload.size()]);
m_from = recover(sig, h);
+ if (!m_from)
+ return false;
m_payload = payload.toBytes();
}
else
m_payload = bytesConstRef(&_data).cropped(1).toBytes();
+ return true;
}
-Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigned _workToProve)
+Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigned _workToProve) const
{
Envelope ret(time(0) + _ttl, _ttl, _topic);
@@ -91,11 +94,6 @@ Message Envelope::open(Secret const& _s) const
return Message(*this, _s);
}
-Message Envelope::open() const
-{
- return Message(*this);
-}
-
unsigned Envelope::workProved() const
{
h256 d[2];
diff --git a/libwhisper/Message.h b/libwhisper/Message.h
index fa5cc3662..6b28073b7 100644
--- a/libwhisper/Message.h
+++ b/libwhisper/Message.h
@@ -55,9 +55,9 @@ public:
operator bool() const { return !!m_expiry; }
- void streamOut(RLPStream& _s, bool _withNonce) const { _s.appendList(_withNonce ? 5 : 4) << m_expiry << m_ttl << m_topic << m_data; if (_withNonce) _s << m_nonce; }
- h256 sha3() const { RLPStream s; streamOut(s, true); return dev::sha3(s.out()); }
- h256 sha3NoNonce() const { RLPStream s; streamOut(s, false); return dev::sha3(s.out()); }
+ void streamRLP(RLPStream& _s, bool _withNonce) const { _s.appendList(_withNonce ? 5 : 4) << m_expiry << m_ttl << m_topic << m_data; if (_withNonce) _s << m_nonce; }
+ h256 sha3() const { RLPStream s; streamRLP(s, true); return dev::sha3(s.out()); }
+ h256 sha3NoNonce() const { RLPStream s; streamRLP(s, false); return dev::sha3(s.out()); }
unsigned sent() const { return m_expiry - m_ttl; }
unsigned expiry() const { return m_expiry; }
@@ -65,8 +65,7 @@ public:
Topic const& topic() const { return m_topic; }
bytes const& data() const { return m_data; }
- Message open(Secret const& _s) const;
- Message open() const;
+ Message open(Secret const& _s = Secret()) const;
unsigned workProved() const;
void proveWork(unsigned _ms);
@@ -102,19 +101,22 @@ public:
Public to() const { return m_to; }
bytes const& payload() const { return m_payload; }
+ void setFrom(Public _from) { m_from = _from; }
void setTo(Public _to) { m_to = _to; }
+ void setPayload(bytes const& _payload) { m_payload = _payload; }
+ void setPayload(bytes&& _payload) { swap(m_payload, _payload); }
operator bool() const { return !!m_payload.size() || m_from || m_to; }
/// Turn this message into a ditributable Envelope.
- Envelope seal(Secret _from, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50);
+ Envelope seal(Secret _from, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) const;
// Overloads for skipping _from or specifying _to.
- Envelope seal(Topic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { return seal(Secret(), _topic, _workToProve, _ttl); }
- Envelope seal(Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); }
- Envelope seal(Secret _from, Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); }
+ Envelope seal(Topic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _workToProve, _ttl); }
+ Envelope sealTo(Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); }
+ Envelope sealTo(Secret _from, Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); }
private:
- void populate(bytes const& _data);
+ bool populate(bytes const& _data);
Public m_from;
Public m_to;
diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp
index 2d9a5e6b6..71030aae2 100644
--- a/libwhisper/WhisperHost.cpp
+++ b/libwhisper/WhisperHost.cpp
@@ -47,12 +47,19 @@ void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const
if (m_messages.count(_m))
{
UpgradeGuard ll(l);
- m_messages.at(_m).streamOut(_s, true);
+ auto const& m = m_messages.at(_m);
+ cnote << "streamRLP: " << m.expiry() << m.ttl() << m.topic() << toHex(m.data());
+ m.streamRLP(_s, true);
}
}
void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
{
+ cnote << "inject: " << _m.expiry() << _m.ttl() << _m.topic() << toHex(_m.data());
+
+ if (_m.expiry() <= time(0))
+ return;
+
auto h = _m.sha3();
{
UpgradableGuard l(x_messages);
@@ -60,9 +67,10 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
return;
UpgradeGuard ll(l);
m_messages[h] = _m;
+ m_expiryQueue[_m.expiry()] = h;
}
- if (_p)
+// if (_p)
{
Guard l(m_filterLock);
for (auto const& f: m_filters)
@@ -107,6 +115,28 @@ unsigned WhisperHost::installWatch(shh::TopicFilter const& _f)
return installWatchOnId(h);
}
+h256s WhisperHost::watchMessages(unsigned _watchId)
+{
+ cleanup();
+ h256s ret;
+ auto wit = m_watches.find(_watchId);
+ if (wit == m_watches.end())
+ return ret;
+ TopicFilter f;
+ {
+ Guard l(m_filterLock);
+ auto fit = m_filters.find(wit->second.id);
+ if (fit == m_filters.end())
+ return ret;
+ f = fit->second.filter;
+ }
+ ReadGuard l(x_messages);
+ for (auto const& m: m_messages)
+ if (f.matches(m.second))
+ ret.push_back(m.first);
+ return ret;
+}
+
void WhisperHost::uninstallWatch(unsigned _i)
{
cwatshh << "XXX" << _i;
@@ -124,3 +154,13 @@ void WhisperHost::uninstallWatch(unsigned _i)
if (!--fit->second.refCount)
m_filters.erase(fit);
}
+
+void WhisperHost::cleanup()
+{
+ // remove old messages.
+ // should be called every now and again.
+ auto now = time(0);
+ WriteGuard l(x_messages);
+ for (auto it = m_expiryQueue.begin(); it != m_expiryQueue.end() && it->first <= now; it = m_expiryQueue.erase(it))
+ m_messages.erase(it->second);
+}
diff --git a/libwhisper/WhisperHost.h b/libwhisper/WhisperHost.h
index 2a8789956..4d761919c 100644
--- a/libwhisper/WhisperHost.h
+++ b/libwhisper/WhisperHost.h
@@ -48,16 +48,21 @@ public:
unsigned protocolVersion() const { return 0; }
- virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr);
+ virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr) override;
using Interface::installWatch;
- virtual unsigned installWatch(TopicFilter const& _filter);
- virtual unsigned installWatchOnId(h256 _filterId);
- virtual void uninstallWatch(unsigned _watchId);
- virtual h256s peekWatch(unsigned _watchId) const { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } }
- virtual h256s checkWatch(unsigned _watchId) { dev::Guard l(m_filterLock); h256s ret; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes.clear(); } catch (...) {} return ret; }
+ virtual unsigned installWatch(TopicFilter const& _filter) override;
+ virtual unsigned installWatchOnId(h256 _filterId) override;
+ virtual void uninstallWatch(unsigned _watchId) override;
+ virtual h256s peekWatch(unsigned _watchId) const override { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } }
+ virtual h256s checkWatch(unsigned _watchId) override { cleanup(); dev::Guard l(m_filterLock); h256s ret; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes.clear(); } catch (...) {} return ret; }
+ virtual h256s watchMessages(unsigned _watchId) override;
- virtual Envelope envelope(h256 _m) const { try { dev::ReadGuard l(x_messages); return m_messages.at(_m); } catch (...) { return Envelope(); } }
+ virtual Envelope envelope(h256 _m) const override { try { dev::ReadGuard l(x_messages); return m_messages.at(_m); } catch (...) { return Envelope(); } }
+
+ std::map all() const { ReadGuard l(x_messages); return m_messages; }
+
+ void cleanup();
private:
void streamMessage(h256 _m, RLPStream& _s) const;
@@ -66,6 +71,7 @@ private:
mutable dev::SharedMutex x_messages;
std::map m_messages;
+ std::map m_expiryQueue;
mutable dev::Mutex m_filterLock;
std::map m_filters;
diff --git a/sc/cmdline.cpp b/sc/cmdline.cpp
index d6602c56d..b44d2538c 100644
--- a/sc/cmdline.cpp
+++ b/sc/cmdline.cpp
@@ -3,100 +3,117 @@
#include
#include
#include