const& _h) { return (*this |= _h.template 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/SHA3.cpp b/libdevcrypto/SHA3.cpp
index eaabae0ff..7c2cc01a3 100644
--- a/libdevcrypto/SHA3.cpp
+++ b/libdevcrypto/SHA3.cpp
@@ -30,7 +30,6 @@ namespace dev
{
h256 EmptySHA3 = sha3(bytesConstRef());
-h256 ZeroRLPSHA3 = sha3(rlp(bytesConstRef()));
std::string sha3(std::string const& _input, bool _hex)
{
diff --git a/libdevcrypto/SHA3.h b/libdevcrypto/SHA3.h
index 5948cb3bd..fc2cfcfc3 100644
--- a/libdevcrypto/SHA3.h
+++ b/libdevcrypto/SHA3.h
@@ -57,7 +57,6 @@ inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef((bytes*)&_inpu
inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); }
extern h256 EmptySHA3;
-extern h256 ZeroRLPSHA3;
// Other crypto convenience routines
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 0ca0b9d98..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.
diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp
index 6e4af247f..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,16 +52,16 @@ 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 << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot
+ _s.appendList(_nonce ? 15 : 14)
+ << parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << receiptsRoot << logBloom
<< difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData;
if (_nonce)
_s << nonce;
@@ -84,17 +84,19 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce)
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;
@@ -140,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/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 b08179f9e..a9bdbf247 100644
--- a/libethereum/BlockChain.cpp
+++ b/libethereum/BlockChain.cpp
@@ -101,8 +101,9 @@ bytes BlockChain::createGenesisBlock()
stateRoot = state.root();
}
- block.appendList(13)
- << h256() << c_shaNull << h160() << stateRoot << c_shaNull << 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 dc36957ee..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;
}
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 9ec835833..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,7 +355,7 @@ void State::ensureCached(std::map& _cache, Address _a, bo
RLP state(stateBack);
AddressState s;
if (state.isNull())
- s = AddressState(0, 0, ZeroRLPSHA3, EmptySHA3);
+ s = AddressState(0, 0, EmptyTrie, EmptySHA3);
else
s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash(), state[3].toHash());
bool ok;
@@ -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 6e65bfd53..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;
diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp
index b707efde3..bdc8bf34d 100644
--- a/libethereum/Transaction.cpp
+++ b/libethereum/Transaction.cpp
@@ -85,7 +85,7 @@ 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;
diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h
index ca73ba06a..2492e32bc 100644
--- a/libethereum/Transaction.h
+++ b/libethereum/Transaction.h
@@ -64,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 625963e34..774667902 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);
@@ -423,18 +449,13 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::SIGNEXTEND:
{
- unsigned k = (unsigned)m_stack[m_stack.size() - 2];
- if (k > 31)
- m_stack[m_stack.size() - 2] = m_stack.back();
- else
- {
- u256 b = m_stack.back();
+ unsigned k = m_stack.back();
+ m_stack.pop_back();
+ auto& b = m_stack.back();
+ if (k <= 31)
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:
@@ -663,7 +684,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;
@@ -671,7 +692,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();
@@ -688,6 +709,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/Instruction.cpp b/libevmface/Instruction.cpp
index c9b6ea2ce..2c2af8eb0 100644
--- a/libevmface/Instruction.cpp
+++ b/libevmface/Instruction.cpp
@@ -149,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 },
@@ -277,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 faad50fb2..ea355fab1 100644
--- a/libevmface/Instruction.h
+++ b/libevmface/Instruction.h
@@ -162,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 2af4d2808..020c7e420 100644
--- a/libp2p/Host.cpp
+++ b/libp2p/Host.cpp
@@ -252,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);
}
}
diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp
index 11908d181..15b07e433 100644
--- a/libwhisper/Common.cpp
+++ b/libwhisper/Common.cpp
@@ -36,7 +36,7 @@ BuildTopic& BuildTopic::shiftBytes(bytes const& _b)
h256 TopicFilter::sha3() const
{
RLPStream s;
- fillStream(s);
+ streamRLP(s);
return dev::sha3(s.out());
}
diff --git a/libwhisper/Common.h b/libwhisper/Common.h
index a85caafe1..21296e193 100644
--- a/libwhisper/Common.h
+++ b/libwhisper/Common.h
@@ -92,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;
diff --git a/libwhisper/Message.h b/libwhisper/Message.h
index 84214fd18..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; }
diff --git a/libwhisper/WhisperHost.cpp b/libwhisper/WhisperHost.cpp
index 591574bf0..71030aae2 100644
--- a/libwhisper/WhisperHost.cpp
+++ b/libwhisper/WhisperHost.cpp
@@ -48,8 +48,8 @@ void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const
{
UpgradeGuard ll(l);
auto const& m = m_messages.at(_m);
- cnote << "streamOut: " << m.expiry() << m.ttl() << m.topic() << toHex(m.data());
- m.streamOut(_s, true);
+ cnote << "streamRLP: " << m.expiry() << m.ttl() << m.topic() << toHex(m.data());
+ m.streamRLP(_s, true);
}
}
diff --git a/test/genesis.cpp b/test/genesis.cpp
index 5f17d2762..16ffb1859 100644
--- a/test/genesis.cpp
+++ b/test/genesis.cpp
@@ -35,9 +35,20 @@ namespace js = json_spirit;
BOOST_AUTO_TEST_CASE(genesis_tests)
{
+ const char* ptestPath = getenv("ETHEREUM_TEST_PATH");
+ string testPath;
+
+ if (ptestPath == NULL)
+ {
+ cnote << " could not find environment variable ETHEREUM_TEST_PATH \n";
+ testPath = "../../../tests";
+ }
+ else
+ testPath = ptestPath;
+
cnote << "Testing Genesis block...";
js::mValue v;
- string s = asString(contents("../../../tests/genesishashestest.json"));
+ string s = asString(contents(testPath + "/genesishashestest.json"));
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'genesishashestest.json' is empty. Have you cloned the 'tests' repo branch develop?");
js::read_string(s, v);
diff --git a/test/hexPrefix.cpp b/test/hexPrefix.cpp
index fb2fbc826..99207ab97 100644
--- a/test/hexPrefix.cpp
+++ b/test/hexPrefix.cpp
@@ -33,9 +33,20 @@ namespace js = json_spirit;
BOOST_AUTO_TEST_CASE(hexPrefix_test)
{
+ const char* ptestPath = getenv("ETHEREUM_TEST_PATH");
+ string testPath;
+
+ if (ptestPath == NULL)
+ {
+ cnote << " could not find environment variable ETHEREUM_TEST_PATH \n";
+ testPath = "../../../tests";
+ }
+ else
+ testPath = ptestPath;
+
cnote << "Testing Hex-Prefix-Encode...";
js::mValue v;
- string s = asString(contents("../../../tests/hexencodetest.json"));
+ string s = asString(contents(testPath + "/hexencodetest.json"));
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content from 'hexencodetest.json' is empty. Have you cloned the 'tests' repo branch develop?");
js::read_string(s, v);
for (auto& i: v.get_obj())
diff --git a/test/main.cpp b/test/main.cpp
index ef009e299..3f8860d7a 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -44,7 +44,7 @@ using namespace dev::eth;
BOOST_AUTO_TEST_CASE(basic_tests)
{
/* RLPStream s;
- BlockInfo::genesis().fillStream(s, false);
+ BlockInfo::genesis().streamRLP(s, false);
std::cout << RLP(s.out()) << std::endl;
std::cout << toHex(s.out()) << std::endl;
std::cout << sha3(s.out()) << std::endl;*/
diff --git a/test/rlp.cpp b/test/rlp.cpp
index 69360ad66..608a8499e 100644
--- a/test/rlp.cpp
+++ b/test/rlp.cpp
@@ -61,7 +61,18 @@ namespace dev
static void getRLPTestCases(js::mValue& v)
{
- string s = asString(contents("../../../tests/rlptest.json"));
+ const char* ptestPath = getenv("ETHEREUM_TEST_PATH");
+ string testPath;
+
+ if (ptestPath == NULL)
+ {
+ cnote << " could not find environment variable ETHEREUM_TEST_PATH \n";
+ testPath = "../../../tests";
+ }
+ else
+ testPath = ptestPath;
+
+ string s = asString(contents(testPath + "/rlptest.json"));
BOOST_REQUIRE_MESSAGE( s.length() > 0,
"Contents of 'rlptest.json' is empty. Have you cloned the 'tests' repo branch develop?");
js::read_string(s, v);
diff --git a/test/trie.cpp b/test/trie.cpp
index 899eb1f60..f8ebd10c8 100644
--- a/test/trie.cpp
+++ b/test/trie.cpp
@@ -49,9 +49,20 @@ static unsigned fac(unsigned _i)
BOOST_AUTO_TEST_CASE(trie_tests)
{
+ const char* ptestPath = getenv("ETHEREUM_TEST_PATH");
+ string testPath;
+
+ if (ptestPath == NULL)
+ {
+ cnote << " could not find environment variable ETHEREUM_TEST_PATH \n";
+ testPath = "../../../tests";
+ }
+ else
+ testPath = ptestPath;
+
cnote << "Testing Trie...";
js::mValue v;
- string s = asString(contents("../../../tests/trietest.json"));
+ string s = asString(contents(testPath + "/trietest.json"));
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'trietest.json' is empty. Have you cloned the 'tests' repo branch develop?");
js::read_string(s, v);
for (auto& i: v.get_obj())
diff --git a/test/vm.cpp b/test/vm.cpp
index b49c5cd27..40a0a862b 100644
--- a/test/vm.cpp
+++ b/test/vm.cpp
@@ -20,9 +20,8 @@
* vm test functions.
*/
-#include "vm.h"
-#include
#include
+#include "vm.h"
//#define FILL_TESTS
@@ -45,7 +44,7 @@ h160 FakeExtVM::create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFun
m_s.noteSending(myAddress);
m_ms.internal.resize(m_ms.internal.size() + 1);
- auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1);
+ auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _init, origin, &sub, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1);
if (!m_ms.internal.back().from)
m_ms.internal.pop_back();
@@ -91,7 +90,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data,
if (!m_s.addresses().count(myAddress))
{
m_ms.internal.resize(m_ms.internal.size() + 1);
- auto na = m_s.createNewAddress(myAddress, myAddress, balance(myAddress), gasPrice, &contractgas, init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1);
+ auto na = m_s.createNewAddress(myAddress, myAddress, balance(myAddress), gasPrice, &contractgas, init, origin, &sub, &m_ms ? &(m_ms.internal.back()) : nullptr, {}, 1);
if (!m_ms.internal.back().from)
m_ms.internal.pop_back();
if (na != myAddress)
@@ -116,7 +115,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data,
{
m_s.noteSending(myAddress);
m_ms.internal.resize(m_ms.internal.size() + 1);
- auto na = m_s.createNewAddress(_codeAddressOverride ? _codeAddressOverride : _receiveAddress, myAddress, balance(_codeAddressOverride ? _codeAddressOverride : _receiveAddress), gasPrice, &contractgas, init, origin, &suicides, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1);
+ auto na = m_s.createNewAddress(_codeAddressOverride ? _codeAddressOverride : _receiveAddress, myAddress, balance(_codeAddressOverride ? _codeAddressOverride : _receiveAddress), gasPrice, &contractgas, init, origin, &sub, &m_ms ? &(m_ms.internal.back()) : nullptr, OnOpFunc(), 1);
if (!m_ms.internal.back().from)
m_ms.internal.pop_back();
@@ -131,7 +130,7 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data,
m_ms.internal.resize(m_ms.internal.size() + 1);
- auto ret = m_s.call(_receiveAddress,_codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _value, gasPrice, _data, _gas, _out, origin, &suicides, &(m_ms.internal.back()), OnOpFunc(), 1);
+ auto ret = m_s.call(_receiveAddress,_codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _value, gasPrice, _data, _gas, _out, origin, &sub, &(m_ms.internal.back()), simpleTrace(), 1);
if (!m_ms.internal.back().from)
m_ms.internal.pop_back();
@@ -146,12 +145,15 @@ bool FakeExtVM::call(Address _receiveAddress, u256 _value, bytesConstRef _data,
if (!ret)
return false;
+ // TODO: @CJentzsch refund SSTORE stuff.
+ // TODO: @CJentzsch test logs.
+
// do suicides
- for (auto const& f: suicides)
+ for (auto const& f: sub.suicides)
addresses.erase(f);
// get storage
- if ((get<0>(addresses[myAddress]) >= _value) && (suicides.find(_receiveAddress) == suicides.end()))
+ if ((get<0>(addresses[myAddress]) >= _value) && (sub.suicides.find(_receiveAddress) == sub.suicides.end()))
{
for (auto const& j: m_s.storage(_receiveAddress))
{
@@ -419,8 +421,11 @@ void FakeExtVM::importCallCreates(mArray& _callcreates)
}
}
-h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
+// THIS IS BROKEN AND NEEDS TO BE REMOVED.
+h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{
+ (void)o_sub;
+
if (!_origin)
_origin = _sender;
@@ -446,9 +451,7 @@ h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _end
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);
+ // TODO: deal with evm.sub
}
catch (OutOfGas const& /*_e*/)
{
@@ -483,8 +486,6 @@ h160 FakeState::createNewAddress(Address _newAddress, Address _sender, u256 _end
return _newAddress;
}
-
-
namespace dev { namespace test {
void doTests(json_spirit::mValue& v, bool _fillin)
@@ -516,7 +517,7 @@ void doTests(json_spirit::mValue& v, bool _fillin)
VM vm(fev.gas);
try
{
- output = vm.go(fev).toVector();
+ output = vm.go(fev, fev.simpleTrace()).toVector();
}
catch (Exception const& _e)
{
@@ -752,3 +753,32 @@ BOOST_AUTO_TEST_CASE(vmSystemOperationsTest)
{
dev::test::executeTests("vmSystemOperationsTest");
}
+
+BOOST_AUTO_TEST_CASE(userDefinedFile)
+{
+
+ if (boost::unit_test::framework::master_test_suite().argc == 2)
+ {
+ string filename = boost::unit_test::framework::master_test_suite().argv[1];
+ int currentVerbosity = g_logVerbosity;
+ g_logVerbosity = 12;
+ try
+ {
+ cnote << "Testing VM..." << "user defined test";
+ json_spirit::mValue v;
+ string s = asString(contents(filename));
+ BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. ");
+ json_spirit::read_string(s, v);
+ dev::test::doTests(v, false);
+ }
+ catch (Exception const& _e)
+ {
+ BOOST_ERROR("Failed VM Test with Exception: " << diagnostic_information(_e));
+ }
+ catch (std::exception const& _e)
+ {
+ BOOST_ERROR("Failed VM Test with Exception: " << _e.what());
+ }
+ g_logVerbosity = currentVerbosity;
+ }
+}
diff --git a/test/vm.h b/test/vm.h
index d9dca1d7a..f3aae694a 100644
--- a/test/vm.h
+++ b/test/vm.h
@@ -28,6 +28,7 @@ along with cpp-ethereum. If not, see .
#include
#include "JsonSpiritHeaders.h"
#include
+#include
#include
#include
#include
@@ -44,7 +45,7 @@ class FakeState: public eth::State
{
public:
/// Execute a contract-creation transaction.
- h160 createNewAddress(Address _newAddress, Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = {}, std::set* o_suicides = nullptr, eth::Manifest* o_ms = nullptr, eth::OnOpFunc const& _onOp = {}, unsigned _level = 0);
+ h160 createNewAddress(Address _newAddress, Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = {}, eth::SubState* o_sub = nullptr, eth::Manifest* o_ms = nullptr, eth::OnOpFunc const& _onOp = {}, unsigned _level = 0);
};
class FakeExtVM: public eth::ExtVMFace
@@ -80,6 +81,11 @@ public:
json_spirit::mArray exportCallCreates();
void importCallCreates(json_spirit::mArray& _callcreates);
+ template
+ eth::OnOpFunc simpleTrace();
+
+ FakeState state() const { return m_s; }
+
std::map, bytes>> addresses;
eth::Transactions callcreates;
bytes thisTxData;
@@ -91,4 +97,32 @@ private:
eth::Manifest m_ms;
};
+template
+eth::OnOpFunc FakeExtVM::simpleTrace()
+{
+ return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, void* voidVM, void const* voidExt)
+ {
+ ExtVMType const& ext = *(ExtVMType const*)voidExt;
+ eth::VM& vm = *(eth::VM*)voidVM;
+
+ std::ostringstream o;
+ o << std::endl << " STACK" << std::endl;
+ for (auto i: vm.stack())
+ o << (h256)i << std::endl;
+ o << " MEMORY" << std::endl << memDump(vm.memory());
+ o << " STORAGE" << std::endl;
+ for (auto const& i: ext.state().storage(ext.myAddress))
+ o << std::showbase << std::hex << i.first << ": " << i.second << std::endl;
+ dev::LogOutputStream(true) << o.str();
+ dev::LogOutputStream(false) << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << vm.gas() << " | -" << std::dec << gasCost << " | " << newMemSize << "x32" << " ]";
+
+ if (eth::VMTraceChannel::verbosity <= g_logVerbosity)
+ {
+ std::ofstream f;
+ f.open("./vmtrace.log", std::ofstream::app);
+ f << o.str();
+ f << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << vm.gas() << " | -" << std::dec << gasCost << " | " << newMemSize << "x32";
+ }
+ };
+}
} } // Namespace Close