Browse Source

Report bad blocks using published standards.

cl-refactor
Gav Wood 10 years ago
parent
commit
095b6f8189
  1. 1
      eth/main.cpp
  2. 7
      ethminer/CMakeLists.txt
  3. 39
      libdevcore/Exceptions.h
  4. 24
      libethcore/BlockInfo.cpp
  5. 73
      libethcore/Exceptions.h
  6. 48
      libethereum/BlockChain.cpp
  7. 5
      libethereum/BlockChain.h
  8. 55
      libethereum/BlockQueue.cpp
  9. 4
      libethereum/BlockQueue.h
  10. 7
      libethereum/CMakeLists.txt
  11. 104
      libethereum/Client.cpp
  12. 8
      libethereum/Client.h
  13. 116
      libethereum/Executive.cpp
  14. 18
      libethereum/Executive.h
  15. 31
      libethereum/Sentinel.h
  16. 17
      libethereum/State.cpp
  17. 8
      libethereum/State.h
  18. 1
      libevm/VMFace.h
  19. 4
      libevmcore/Exceptions.h

1
eth/main.cpp

@ -125,6 +125,7 @@ void help()
<< " --session-sign-key <address> Sign all transactions with the key of the given address for this session only." << endl << " --session-sign-key <address> Sign all transactions with the key of the given address for this session only." << endl
<< " --master <password> Give the master password for the key store." << endl << " --master <password> Give the master password for the key store." << endl
<< " --password <password> Give a password for a private key." << endl << " --password <password> Give a password for a private key." << endl
<< " --sentinel <server> Set the sentinel for reporting bad blocks or chain issues." << endl
<< endl << endl
<< "Client transacting:" << endl << "Client transacting:" << endl
/*<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl /*<< " -B,--block-fees <n> Set the block fee profit in the reference unit e.g. ¢ (default: 15)." << endl

7
ethminer/CMakeLists.txt

@ -5,7 +5,10 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
if (JSONRPC)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
endif()
set(EXECUTABLE ethminer) set(EXECUTABLE ethminer)
@ -19,10 +22,6 @@ target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
if (JSONRPC) if (JSONRPC)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${CURL_LIBRARIES})
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls(${EXECUTABLE} CURL_DLLS)
endif()
endif() endif()
target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} ethcore)

39
libdevcore/Exceptions.h

@ -30,7 +30,8 @@
namespace dev namespace dev
{ {
// base class for all exceptions
/// Base class for all exceptions.
struct Exception: virtual std::exception, virtual boost::exception struct Exception: virtual std::exception, virtual boost::exception
{ {
Exception(std::string _message = std::string()): m_message(std::move(_message)) {} Exception(std::string _message = std::string()): m_message(std::move(_message)) {}
@ -40,20 +41,26 @@ private:
std::string m_message; std::string m_message;
}; };
struct BadHexCharacter: virtual Exception {}; #define DEV_SIMPLE_EXCEPTION(X) struct X: virtual Exception { public: X(): Exception(#X) {} }
struct RLPException: virtual Exception {};
struct BadCast: virtual RLPException {}; /// Base class for all RLP exceptions.
struct BadRLP: virtual RLPException {}; struct RLPException: virtual Exception { RLPException(std::string _message = std::string()): Exception(_message) {} };
struct OversizeRLP: virtual RLPException {}; #define DEV_SIMPLE_EXCEPTION_RLP(X) struct X: virtual RLPException { public: X(): RLPException(#X) {} }
struct UndersizeRLP: virtual RLPException {};
struct NoNetworking: virtual Exception {}; DEV_SIMPLE_EXCEPTION_RLP(BadCast);
struct NoUPnPDevice: virtual Exception {}; DEV_SIMPLE_EXCEPTION_RLP(BadRLP);
struct RootNotFound: virtual Exception {}; DEV_SIMPLE_EXCEPTION_RLP(OversizeRLP);
struct BadRoot: virtual Exception {}; DEV_SIMPLE_EXCEPTION_RLP(UndersizeRLP);
struct FileError: virtual Exception {};
struct Overflow: virtual Exception {}; DEV_SIMPLE_EXCEPTION(BadHexCharacter);
DEV_SIMPLE_EXCEPTION(NoNetworking);
DEV_SIMPLE_EXCEPTION(NoUPnPDevice);
DEV_SIMPLE_EXCEPTION(RootNotFound);
DEV_SIMPLE_EXCEPTION(BadRoot);
DEV_SIMPLE_EXCEPTION(FileError);
DEV_SIMPLE_EXCEPTION(Overflow);
DEV_SIMPLE_EXCEPTION(FailedInvariant);
struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} }; struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} };
struct FailedInvariant: virtual Exception {};
struct ExternalFunctionFailure: virtual Exception { public: ExternalFunctionFailure(std::string _f): Exception("Function " + _f + "() failed.") {} }; struct ExternalFunctionFailure: virtual Exception { public: ExternalFunctionFailure(std::string _f): Exception("Function " + _f + "() failed.") {} };
// error information to be added to exceptions // error information to be added to exceptions
@ -66,5 +73,7 @@ using errinfo_min = boost::error_info<struct tag_min, bigint>;
using errinfo_max = boost::error_info<struct tag_max, bigint>; using errinfo_max = boost::error_info<struct tag_max, bigint>;
using RequirementError = boost::tuple<errinfo_required, errinfo_got>; using RequirementError = boost::tuple<errinfo_required, errinfo_got>;
using errinfo_hash256 = boost::error_info<struct tag_hash, h256>; using errinfo_hash256 = boost::error_info<struct tag_hash, h256>;
using HashMismatchError = boost::tuple<errinfo_hash256, errinfo_hash256>; using errinfo_required_h256 = boost::error_info<struct tag_required_h256, h256>;
using errinfo_got_h256 = boost::error_info<struct tag_get_h256, h256>;
using Hash256RequirementError = boost::tuple<errinfo_required_h256, errinfo_got_h256>;
} }

24
libethcore/BlockInfo.cpp

@ -139,7 +139,6 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const
mixHash = _header[field = 13].toHash<h256>(RLP::VeryStrict); mixHash = _header[field = 13].toHash<h256>(RLP::VeryStrict);
nonce = _header[field = 14].toHash<Nonce>(RLP::VeryStrict); nonce = _header[field = 14].toHash<Nonce>(RLP::VeryStrict);
} }
catch (Exception const& _e) catch (Exception const& _e)
{ {
_e << errinfo_name("invalid block header format") << BadFieldError(field, toHex(_header[field].data().toBytes())); _e << errinfo_name("invalid block header format") << BadFieldError(field, toHex(_header[field].data().toBytes()));
@ -151,9 +150,26 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const
// check it hashes according to proof of work or that it's the genesis block. // check it hashes according to proof of work or that it's the genesis block.
if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this)) if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this))
BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty)); {
InvalidBlockNonce ex;
ex << errinfo_hash256(headerHash(WithoutNonce));
ex << errinfo_nonce(nonce);
ex << errinfo_difficulty(difficulty);
ex << errinfo_seedHash(seedHash());
ex << errinfo_target(boundary());
ex << errinfo_mixHash(mixHash);
Ethash::Result er = EthashAux::eval(seedHash(), headerHash(WithoutNonce), nonce);
ex << errinfo_ethashResult(make_tuple(er.value, er.mixHash));
BOOST_THROW_EXCEPTION(ex);
}
else if (_s == QuickNonce && parentHash && !ProofOfWork::preVerify(*this)) else if (_s == QuickNonce && parentHash && !ProofOfWork::preVerify(*this))
BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty)); {
InvalidBlockNonce ex;
ex << errinfo_hash256(headerHash(WithoutNonce));
ex << errinfo_nonce(nonce);
ex << errinfo_difficulty(difficulty);
BOOST_THROW_EXCEPTION(ex);
}
if (_s != CheckNothing) if (_s != CheckNothing)
{ {
@ -224,7 +240,7 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
for (auto const& t: txs) for (auto const& t: txs)
cdebug << toHex(t); cdebug << toHex(t);
BOOST_THROW_EXCEPTION(InvalidTransactionsHash() << HashMismatchError(expectedRoot, transactionsRoot)); BOOST_THROW_EXCEPTION(InvalidTransactionsRoot() << Hash256RequirementError(expectedRoot, transactionsRoot));
} }
clog(BlockInfoDiagnosticsChannel) << "Expected uncle hash:" << toString(sha3(root[2].data())); clog(BlockInfoDiagnosticsChannel) << "Expected uncle hash:" << toString(sha3(root[2].data()));
if (sha3Uncles != sha3(root[2].data())) if (sha3Uncles != sha3(root[2].data()))

73
libethcore/Exceptions.h

@ -35,42 +35,45 @@ using errinfo_field = boost::error_info<struct tag_field, int>;
using errinfo_data = boost::error_info<struct tag_data, std::string>; using errinfo_data = boost::error_info<struct tag_data, std::string>;
using errinfo_nonce = boost::error_info<struct tag_nonce, h64>; using errinfo_nonce = boost::error_info<struct tag_nonce, h64>;
using errinfo_difficulty = boost::error_info<struct tag_difficulty, u256>; using errinfo_difficulty = boost::error_info<struct tag_difficulty, u256>;
using errinfo_target = boost::error_info<struct tag_target, h256>;
using errinfo_seedHash = boost::error_info<struct tag_seedHash, h256>;
using errinfo_mixHash = boost::error_info<struct tag_mixHash, h256>;
using errinfo_ethashResult = boost::error_info<struct tag_ethashResult, std::tuple<h256, h256>>;
using BadFieldError = boost::tuple<errinfo_field, errinfo_data>; using BadFieldError = boost::tuple<errinfo_field, errinfo_data>;
struct DatabaseAlreadyOpen: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(OutOfGasBase);
struct OutOfGasBase: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(OutOfGasIntrinsic);
struct OutOfGasIntrinsic: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(NotEnoughAvailableSpace);
struct NotEnoughAvailableSpace: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(NotEnoughCash);
struct NotEnoughCash: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(GasPriceTooLow);
struct GasPriceTooLow: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(BlockGasLimitReached);
struct BlockGasLimitReached: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(FeeTooSmall);
struct NoSuchContract: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(TooMuchGasUsed);
struct ContractAddressCollision: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(ExtraDataTooBig);
struct FeeTooSmall: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidSignature);
struct TooMuchGasUsed: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidBlockFormat);
struct ExtraDataTooBig: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidUnclesHash);
struct InvalidSignature: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(TooManyUncles);
struct InvalidBlockFormat: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(UncleTooOld);
struct InvalidUnclesHash: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(UncleIsBrother);
struct TooManyUncles: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(UncleInChain);
struct UncleTooOld: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidStateRoot);
struct UncleIsBrother: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidGasUsed);
struct UncleInChain: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidTransactionsRoot);
struct InvalidStateRoot: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidDifficulty);
struct InvalidGasUsed: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidGasLimit);
struct InvalidTransactionsRoot: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidReceiptsStateRoot);
struct InvalidDifficulty: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidTimestamp);
struct InvalidGasLimit: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidLogBloom);
struct InvalidReceiptsStateRoot: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidNonce);
struct InvalidTimestamp: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidBlockHeaderItemCount);
struct InvalidLogBloom: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidBlockNonce);
struct InvalidNonce: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidParentHash);
struct InvalidBlockHeaderItemCount: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(InvalidNumber);
struct InvalidBlockNonce: virtual dev::Exception {};
struct InvalidParentHash: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(DatabaseAlreadyOpen);
struct InvalidNumber: virtual dev::Exception {}; DEV_SIMPLE_EXCEPTION(DAGCreationFailure);
struct InvalidContractAddress: virtual public dev::Exception {}; DEV_SIMPLE_EXCEPTION(DAGComputeFailure);
struct DAGCreationFailure: virtual public dev::Exception {};
struct DAGComputeFailure: virtual public dev::Exception {};
} }
} }

48
libethereum/BlockChain.cpp

@ -331,9 +331,18 @@ tuple<h256s, h256s, bool> BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st
// Can't continue - chain bad. // Can't continue - chain bad.
badBlocks.push_back(block.first.hash()); badBlocks.push_back(block.first.hash());
} }
catch (Exception const& _e) catch (dev::eth::FutureTime)
{ {
cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << LogTag::Error << diagnostic_information(_e); cwarn << "ODD: Import queue contains a block with future time." << LogTag::Error << boost::current_exception_diagnostic_information();
// NOTE: don't reimport since the queue should guarantee everything in the past.
// Can't continue - chain bad.
badBlocks.push_back(block.first.hash());
}
catch (Exception& ex)
{
cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << LogTag::Error << diagnostic_information(ex);
if (m_onBad)
m_onBad(ex);
// NOTE: don't reimport since the queue should guarantee everything in the right order. // NOTE: don't reimport since the queue should guarantee everything in the right order.
// Can't continue - chain bad. // Can't continue - chain bad.
badBlocks.push_back(block.first.hash()); badBlocks.push_back(block.first.hash());
@ -360,8 +369,10 @@ pair<ImportResult, ImportRoute> BlockChain::attemptImport(bytes const& _block, O
{ {
return make_pair(ImportResult::FutureTime, make_pair(h256s(), h256s())); return make_pair(ImportResult::FutureTime, make_pair(h256s(), h256s()));
} }
catch (...) catch (Exception& ex)
{ {
if (m_onBad)
m_onBad(ex);
return make_pair(ImportResult::Malformed, make_pair(h256s(), h256s())); return make_pair(ImportResult::Malformed, make_pair(h256s(), h256s()));
} }
} }
@ -379,10 +390,11 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import
bi.verifyInternals(&_block); bi.verifyInternals(&_block);
} }
#if ETH_CATCH #if ETH_CATCH
catch (Exception const& _e) catch (Exception& ex)
{ {
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(_e); clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
_e << errinfo_comment("Malformed block "); ex << errinfo_now(time(0));
ex << errinfo_block(_block);
throw; throw;
} }
#endif #endif
@ -470,14 +482,8 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
blb.blooms.push_back(s.receipt(i).bloom()); blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i)); br.receipts.push_back(s.receipt(i));
} }
try {
s.cleanup(true); s.cleanup(true);
}
catch (BadRoot)
{
cwarn << "BadRoot error. Retrying import later.";
BOOST_THROW_EXCEPTION(FutureTime());
}
td = pd.totalDifficulty + tdIncrease; td = pd.totalDifficulty + tdIncrease;
@ -520,20 +526,20 @@ ImportRoute BlockChain::import(BlockInfo const& _bi, bytes const& _block, Overla
#endif #endif
} }
#if ETH_CATCH #if ETH_CATCH
catch (InvalidNonce const& _e) catch (BadRoot& ex)
{ {
clog(BlockChainNote) << " Malformed block: " << diagnostic_information(_e); cwarn << "BadRoot error. Retrying import later.";
_e << errinfo_comment("Malformed block "); BOOST_THROW_EXCEPTION(FutureTime());
throw;
} }
catch (Exception const& _e) catch (Exception& ex)
{ {
clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(_e); clog(BlockChainWarn) << " Malformed block: " << diagnostic_information(ex);
_e << errinfo_comment("Malformed block ");
clog(BlockChainWarn) << "Block: " << _bi.hash(); clog(BlockChainWarn) << "Block: " << _bi.hash();
clog(BlockChainWarn) << _bi; clog(BlockChainWarn) << _bi;
clog(BlockChainWarn) << "Block parent: " << _bi.parentHash; clog(BlockChainWarn) << "Block parent: " << _bi.parentHash;
clog(BlockChainWarn) << BlockInfo(block(_bi.parentHash)); clog(BlockChainWarn) << BlockInfo(block(_bi.parentHash));
ex << errinfo_now(time(0));
ex << errinfo_block(_block);
throw; throw;
} }
#endif #endif

5
libethereum/BlockChain.h

@ -256,6 +256,9 @@ public:
/// Deallocate unused data. /// Deallocate unused data.
void garbageCollect(bool _force = false); void garbageCollect(bool _force = false);
/// Change the function that is called with a bad block.
template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
private: private:
static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); } static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); }
@ -335,6 +338,8 @@ private:
ldb::ReadOptions m_readOptions; ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions; ldb::WriteOptions m_writeOptions;
std::function<void(Exception&)> m_onBad; ///< Called if we have a block that doesn't verify.
friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
}; };

55
libethereum/BlockQueue.cpp

@ -26,6 +26,7 @@
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include "BlockChain.h" #include "BlockChain.h"
#include "State.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -76,56 +77,44 @@ void BlockQueue::verifierBody()
std::pair<BlockInfo, bytes> res; std::pair<BlockInfo, bytes> res;
swap(work.second, res.second); swap(work.second, res.second);
try { try
try { {
try
{
res.first.populate(res.second, CheckEverything, work.first); res.first.populate(res.second, CheckEverything, work.first);
res.first.verifyInternals(&res.second); res.first.verifyInternals(&res.second);
} }
catch (InvalidBlockNonce&) catch (Exception& ex)
{
badBlock(res.second, "Invalid block nonce");
cwarn << " Nonce:" << res.first.nonce.hex();
cwarn << " PoWHash:" << res.first.headerHash(WithoutNonce).hex();
cwarn << " SeedHash:" << res.first.seedHash().hex();
cwarn << " Target:" << res.first.boundary().hex();
cwarn << " MixHash:" << res.first.mixHash.hex();
Ethash::Result er = EthashAux::eval(res.first.seedHash(), res.first.headerHash(WithoutNonce), res.first.nonce);
cwarn << " Ethash v:" << er.value.hex();
cwarn << " Ethash mH:" << er.mixHash.hex();
throw;
}
catch (Exception& _e)
{ {
badBlock(res.second, _e.what()); clog(BlockChainNote) << " Malformed block: " << diagnostic_information(ex);
badBlock(res.second, ex.what());
ex << errinfo_now(time(0));
ex << errinfo_block(res.second);
if (m_onBad)
m_onBad(ex);
throw; throw;
} }
RLP r(&res.second); RLP r(&res.second);
unsigned ii = 0;
for (auto const& uncle: r[2]) for (auto const& uncle: r[2])
{ {
try try
{ {
BlockInfo().populateFromHeader(RLP(uncle.data()), CheckEverything); BlockInfo().populateFromHeader(RLP(uncle.data()), CheckEverything);
} }
catch (InvalidNonce&) catch (Exception& ex)
{
badBlockHeader(uncle.data(), "Invalid uncle nonce");
BlockInfo bi = BlockInfo::fromHeader(uncle.data(), CheckNothing);
cwarn << " Nonce:" << bi.nonce.hex();
cwarn << " PoWHash:" << bi.headerHash(WithoutNonce).hex();
cwarn << " SeedHash:" << bi.seedHash().hex();
cwarn << " Target:" << bi.boundary().hex();
cwarn << " MixHash:" << bi.mixHash.hex();
Ethash::Result er = EthashAux::eval(bi.seedHash(), bi.headerHash(WithoutNonce), bi.nonce);
cwarn << " Ethash v:" << er.value.hex();
cwarn << " Ethash mH:" << er.mixHash.hex();
throw;
}
catch (Exception& _e)
{ {
badBlockHeader(uncle.data(), _e.what()); clog(BlockChainNote) << " Malformed block header: " << diagnostic_information(ex);
badBlockHeader(uncle.data(), ex.what());
ex << errinfo_uncleIndex(ii);
ex << errinfo_now(time(0));
ex << errinfo_block(res.second);
if (m_onBad)
m_onBad(ex);
throw; throw;
} }
++ii;
} }
} }
catch (...) catch (...)

4
libethereum/BlockQueue.h

@ -110,6 +110,8 @@ public:
template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); } template <class T> Handler onReady(T const& _t) { return m_onReady.add(_t); }
template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
private: private:
void noteReady_WITH_LOCK(h256 const& _b); void noteReady_WITH_LOCK(h256 const& _b);
@ -134,6 +136,8 @@ private:
std::vector<std::thread> m_verifiers; ///< Threads who only verify. std::vector<std::thread> m_verifiers; ///< Threads who only verify.
bool m_deleting = false; ///< Exit condition for verifiers. bool m_deleting = false; ///< Exit condition for verifiers.
std::function<void(Exception&)> m_onBad; ///< Called if we have a block that doesn't verify.
}; };
std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s); std::ostream& operator<<(std::ostream& _out, BlockQueueStatus const& _s);

7
libethereum/CMakeLists.txt

@ -14,6 +14,10 @@ aux_source_directory(. SRC_LIST)
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${LEVELDB_INCLUDE_DIRS}) include_directories(${LEVELDB_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
if (JSONRPC)
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
endif()
set(EXECUTABLE ethereum) set(EXECUTABLE ethereum)
@ -30,6 +34,9 @@ target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES})
target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${Boost_REGEX_LIBRARIES})
target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} secp256k1)
if (JSONRPC)
target_link_libraries(${EXECUTABLE} ${JSON_RPC_CPP_CLIENT_LIBRARIES})
endif()
if (CMAKE_COMPILER_IS_MINGW) if (CMAKE_COMPILER_IS_MINGW)
target_link_libraries(${EXECUTABLE} ssp shlwapi) target_link_libraries(${EXECUTABLE} ssp shlwapi)

104
libethereum/Client.cpp

@ -25,9 +25,16 @@
#include <thread> #include <thread>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/math/distributions/normal.hpp> #include <boost/math/distributions/normal.hpp>
#if ETH_JSONRPC || !ETH_TRUE
#include <jsonrpccpp/client.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
#endif
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/StructuredLogger.h> #include <libdevcore/StructuredLogger.h>
#include <libp2p/Host.h> #include <libp2p/Host.h>
#if ETH_JSONRPC || !ETH_TRUE
#include "Sentinel.h"
#endif
#include "Defaults.h" #include "Defaults.h"
#include "Executive.h" #include "Executive.h"
#include "EthereumHost.h" #include "EthereumHost.h"
@ -80,6 +87,101 @@ void VersionChecker::setOk()
} }
} }
void Client::onBadBlock(Exception& _ex)
{
// BAD BLOCK!!!
bytes const& block = *boost::get_error_info<errinfo_block>(_ex);
if (!&block)
{
cwarn << "ODD: onBadBlock called but exception has no block in it.";
return;
}
badBlock(block, _ex.what());
cwarn << boost::diagnostic_information(_ex, true);
#if ETH_JSONRPC || !ETH_TRUE
if (!m_sentinel.empty())
{
Json::Value report;
report["client"] = "cpp";
report["version"] = Version;
report["protocolVersion"] = c_protocolVersion;
report["databaseVersion"] = c_databaseVersion;
report["errortype"] = _ex.what();
report["block"] = toHex(block);
report["hint"] = Json::Value(Json::objectValue);
// add the various hints.
if (unsigned const* uncleIndex = boost::get_error_info<errinfo_uncleIndex>(_ex))
{
// uncle that failed.
report["hints"]["uncleIndex"] = *uncleIndex;
}
else if (unsigned const* txIndex = boost::get_error_info<errinfo_transactionIndex>(_ex))
{
// transaction that failed.
report["hints"]["transactionIndex"] = *txIndex;
}
else
{
// general block failure.
}
if (string const* vmtraceJson = boost::get_error_info<errinfo_vmtrace>(_ex))
Json::Reader().parse(*vmtraceJson, report["hints"]["vmtrace"]);
if (vector<bytes> const* receipts = boost::get_error_info<errinfo_receipts>(_ex))
{
report["hints"]["receipts"] = Json::arrayValue;
for (auto const& r: *receipts)
report["hints"]["receipts"].append(toHex(r));
}
if (h256Hash const* excluded = boost::get_error_info<errinfo_unclesExcluded>(_ex))
{
report["hints"]["unclesExcluded"] = Json::arrayValue;
for (auto const& r: h256Set() + *excluded)
report["hints"]["unclesExcluded"].append(Json::Value(r.hex()));
}
#define DEV_HINT_ERRINFO(X) \
if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \
report["hints"][#X] = toString(*n)
#define DEV_HINT_ERRINFO_HASH(X) \
if (auto const* n = boost::get_error_info<errinfo_ ## X>(_ex)) \
report["hints"][#X] = n->hex()
DEV_HINT_ERRINFO_HASH(hash256);
DEV_HINT_ERRINFO(uncleNumber);
DEV_HINT_ERRINFO(currentNumber);
DEV_HINT_ERRINFO(now);
DEV_HINT_ERRINFO(invalidSymbol);
DEV_HINT_ERRINFO(wrongAddress);
DEV_HINT_ERRINFO(comment);
DEV_HINT_ERRINFO(min);
DEV_HINT_ERRINFO(max);
DEV_HINT_ERRINFO(required);
DEV_HINT_ERRINFO(got);
DEV_HINT_ERRINFO_HASH(required_LogBloom);
DEV_HINT_ERRINFO_HASH(got_LogBloom);
DEV_HINT_ERRINFO_HASH(required_h256);
DEV_HINT_ERRINFO_HASH(got_h256);
jsonrpc::HttpClient client(m_sentinel);
Sentinel rpc(client);
try
{
rpc.eth_badBlock(report);
}
catch (...)
{
cwarn << "Error reporting to sentinel. Sure the address" << m_sentinel << "is correct?";
}
}
#endif
}
void BasicGasPricer::update(BlockChain const& _bc) void BasicGasPricer::update(BlockChain const& _bc)
{ {
unsigned c = 0; unsigned c = 0;
@ -185,6 +287,8 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr<GasPricer> _gp, std::string c
m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30); m_lastGetWork = std::chrono::system_clock::now() - chrono::seconds(30);
m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue);
m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue);
m_bq.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
m_bc.setOnBad([=](Exception& ex){ this->onBadBlock(ex); });
m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); });
m_gp->update(m_bc); m_gp->update(m_bc);

8
libethereum/Client.h

@ -213,6 +213,8 @@ public:
void retryUnkonwn() { m_bq.retryAllUnknown(); } void retryUnkonwn() { m_bq.retryAllUnknown(); }
/// Get a report of activity. /// Get a report of activity.
ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; } ActivityReport activityReport() { ActivityReport ret; std::swap(m_report, ret); return ret; }
/// Set a JSONRPC server to which we can report bad blocks.
void setSentinel(std::string const& _server) { m_sentinel = _server; }
protected: protected:
/// InterfaceStub methods /// InterfaceStub methods
@ -278,6 +280,10 @@ private:
/// @returns true only if it's worth bothering to prep the mining block. /// @returns true only if it's worth bothering to prep the mining block.
bool shouldServeWork() const { return m_bq.items().first == 0 && (isMining() || remoteActive()); } bool shouldServeWork() const { return m_bq.items().first == 0 && (isMining() || remoteActive()); }
/// Called when we have attempted to import a bad block.
/// @warning May be called from any thread.
void onBadBlock(Exception& _ex);
VersionChecker m_vc; ///< Dummy object to check & update the protocol version. VersionChecker m_vc; ///< Dummy object to check & update the protocol version.
CanonBlockChain m_bc; ///< Maintains block database. CanonBlockChain m_bc; ///< Maintains block database.
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
@ -317,6 +323,8 @@ private:
Mutex x_signalled; Mutex x_signalled;
std::atomic<bool> m_syncTransactionQueue = {false}; std::atomic<bool> m_syncTransactionQueue = {false};
std::atomic<bool> m_syncBlockQueue = {false}; std::atomic<bool> m_syncBlockQueue = {false};
std::string m_sentinel;
}; };
} }

116
libethereum/Executive.cpp

@ -19,6 +19,9 @@
#include "Executive.h" #include "Executive.h"
#include <boost/timer.hpp> #include <boost/timer.hpp>
#if ETH_JSONRPC || !ETH_TRUE
#include <json/json.h>
#endif
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <libevm/VM.h> #include <libevm/VM.h>
@ -34,6 +37,101 @@ using namespace dev::eth;
const char* VMTraceChannel::name() { return "EVM"; } const char* VMTraceChannel::name() { return "EVM"; }
const char* ExecutiveWarnChannel::name() { return WarnChannel::name(); } const char* ExecutiveWarnChannel::name() { return WarnChannel::name(); }
StandardTrace::StandardTrace():
m_trace(new Json::Value(Json::arrayValue))
{}
bool changesMemory(Instruction _inst)
{
return
_inst == Instruction::MSTORE ||
_inst == Instruction::MSTORE8 ||
_inst == Instruction::CALL ||
_inst == Instruction::CALLCODE ||
_inst == Instruction::SHA3 ||
_inst == Instruction::CALLDATACOPY ||
_inst == Instruction::CODECOPY ||
_inst == Instruction::EXTCODECOPY;
}
bool changesStorage(Instruction _inst)
{
return _inst == Instruction::SSTORE;
}
void StandardTrace::operator()(uint64_t _steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
Json::Value r(Json::objectValue);
Json::Value stack(Json::arrayValue);
for (auto const& i: vm.stack())
stack.append(toHex(toCompactBigEndian(i), 1));
r["stack"] = stack;
bool newContext = false;
Instruction lastInst = Instruction::STOP;
if (m_lastInst.size() == ext.depth)
{
// starting a new context
assert(m_lastInst.size() == ext.depth);
m_lastInst.push_back(inst);
newContext = true;
}
else if (m_lastInst.size() == ext.depth + 2)
{
// returned from old context
m_lastInst.pop_back();
lastInst = m_lastInst.back();
}
else if (m_lastInst.size() == ext.depth + 1)
{
// continuing in previous context
lastInst = m_lastInst.back();
}
else
{
cwarn << "GAA!!! Tracing VM and more than one new/deleted stack frame between steps!";
cwarn << "Attmepting naive recovery...";
m_lastInst.resize(ext.depth + 1);
}
if (changesMemory(lastInst) || newContext)
{
Json::Value mem(Json::arrayValue);
for (auto const& i: vm.memory())
mem.append(toHex(toCompactBigEndian(i), 1));
r["memory"] = mem;
}
if (changesStorage(lastInst) || newContext)
{
Json::Value storage(Json::objectValue);
for (auto const& i: ext.state().storage(ext.myAddress))
storage[toHex(toCompactBigEndian(i.first), 1)] = toHex(toCompactBigEndian(i.second), 1);
r["storage"] = storage;
}
r["depth"] = ext.depth;
r["address"] = ext.myAddress.hex();
r["steps"] = (unsigned)_steps;
r["inst"] = (unsigned)inst;
r["pc"] = toString(vm.curPC());
r["gas"] = toString(gas);
r["gascost"] = toString(gasCost);
r["memexpand"] = toString(newMemSize);
m_trace->append(r);
}
string StandardTrace::json() const
{
return Json::FastWriter().write(*m_trace);
}
Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level): Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
m_s(_s), m_s(_s),
m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)), m_lastHashes(_bc.lastHashes((unsigned)_s.info().number - 1)),
@ -202,24 +300,6 @@ OnOpFunc Executive::simpleTrace()
}; };
} }
OnOpFunc Executive::standardTrace(ostream& o_output)
{
return [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
o_output << endl << " STACK" << endl;
for (auto i: vm.stack())
o_output << (h256)i << endl;
o_output << " MEMORY" << endl << ((vm.memory().size() > 1000) ? " mem size greater than 1000 bytes " : memDump(vm.memory()));
o_output << " STORAGE" << endl;
for (auto const& i: ext.state().storage(ext.myAddress))
o_output << showbase << hex << i.first << ": " << i.second << endl;
o_output << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
};
}
bool Executive::go(OnOpFunc const& _onOp) bool Executive::go(OnOpFunc const& _onOp)
{ {
if (m_ext) if (m_ext)

18
libethereum/Executive.h

@ -25,6 +25,11 @@
#include <libevm/VMFace.h> #include <libevm/VMFace.h>
#include "Transaction.h" #include "Transaction.h"
namespace Json
{
class Value;
}
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -38,6 +43,19 @@ struct Manifest;
struct VMTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 11; }; struct VMTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 11; };
struct ExecutiveWarnChannel: public LogChannel { static const char* name(); static const int verbosity = 6; }; struct ExecutiveWarnChannel: public LogChannel { static const char* name(); static const int verbosity = 6; };
class StandardTrace
{
public:
StandardTrace();
void operator()(uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM);
std::string json() const;
private:
std::vector<Instruction> m_lastInst;
std::shared_ptr<Json::Value> m_trace;
};
/** /**
* @brief Message-call/contract-creation executor; useful for executing transactions. * @brief Message-call/contract-creation executor; useful for executing transactions.
* *

31
libethereum/Sentinel.h

@ -0,0 +1,31 @@
/**
* This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!
*/
#ifndef JSONRPC_CPP_STUB_DEV_ETH_SENTINEL_H_
#define JSONRPC_CPP_STUB_DEV_ETH_SENTINEL_H_
#include <jsonrpccpp/client.h>
namespace dev {
namespace eth {
class Sentinel : public jsonrpc::Client
{
public:
Sentinel(jsonrpc::IClientConnector &conn, jsonrpc::clientVersion_t type = jsonrpc::JSONRPC_CLIENT_V2) : jsonrpc::Client(conn, type) {}
int eth_badBlock(const Json::Value& param1) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
Json::Value result = this->CallMethod("eth_badBlock",p);
if (result.isInt())
return result.asInt();
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
};
}
}
#endif //JSONRPC_CPP_STUB_DEV_ETH_SENTINEL_H_

17
libethereum/State.cpp

@ -577,19 +577,20 @@ string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequire
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number); LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number);
vector<bytes> receipts; vector<bytes> receipts;
ostringstream ss; string ret;
unsigned i = 0; unsigned i = 0;
for (auto const& tr: rlp[1]) for (auto const& tr: rlp[1])
{ {
ss << " VM Execution of transaction" << i << ":" << endl; StandardTrace st;
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, Executive::standardTrace(ss)); execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, [&](uint64_t _steps, Instruction _inst, bigint _newMemSize, bigint _gasCost, bigint _gas, VM* _vm, ExtVMFace const* _extVM) { st(_steps, _inst, _newMemSize, _gasCost, _gas, _vm, _extVM); });
ret += (ret.empty() ? "[" : ",") + st.json();
RLPStream receiptRLP; RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP); m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out()); receipts.push_back(receiptRLP.out());
++i; ++i;
ss << endl;
} }
return ss.str(); return ret.empty() ? "[]" : (ret + "]");
} }
u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir) u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir)
@ -664,7 +665,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
cwarn << " VMTrace:\n" << vmTrace(_block, _bc, _ir); cwarn << " VMTrace:\n" << vmTrace(_block, _bc, _ir);
InvalidReceiptsStateRoot ex; InvalidReceiptsStateRoot ex;
ex << HashMismatchError(receiptsRoot, m_currentBlock.receiptsRoot); ex << Hash256RequirementError(receiptsRoot, m_currentBlock.receiptsRoot);
ex << errinfo_receipts(receipts); ex << errinfo_receipts(receipts);
ex << errinfo_vmtrace(vmTrace(_block, _bc, _ir)); ex << errinfo_vmtrace(vmTrace(_block, _bc, _ir));
BOOST_THROW_EXCEPTION(ex); BOOST_THROW_EXCEPTION(ex);
@ -681,7 +682,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
} }
cwarn << " Final bloom:" << m_currentBlock.logBloom.hex(); cwarn << " Final bloom:" << m_currentBlock.logBloom.hex();
InvalidLogBloom ex; InvalidLogBloom ex;
ex << LogBloomMismatchError(logBloom(), m_currentBlock.logBloom); ex << LogBloomRequirementError(logBloom(), m_currentBlock.logBloom);
ex << errinfo_receipts(receipts); ex << errinfo_receipts(receipts);
BOOST_THROW_EXCEPTION(ex); BOOST_THROW_EXCEPTION(ex);
} }
@ -769,7 +770,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, ImportRequirement
{ {
badBlock(_block, "Bad state root"); badBlock(_block, "Bad state root");
m_db.rollback(); m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidStateRoot() << HashMismatchError(rootHash(), m_currentBlock.stateRoot)); BOOST_THROW_EXCEPTION(InvalidStateRoot() << Hash256RequirementError(rootHash(), m_currentBlock.stateRoot));
} }
if (m_currentBlock.gasUsed != gasUsed()) if (m_currentBlock.gasUsed != gasUsed())

8
libethereum/State.h

@ -51,14 +51,16 @@ using errinfo_uncleIndex = boost::error_info<struct tag_uncleIndex, unsigned>;
using errinfo_currentNumber = boost::error_info<struct tag_currentNumber, u256>; using errinfo_currentNumber = boost::error_info<struct tag_currentNumber, u256>;
using errinfo_uncleNumber = boost::error_info<struct tag_uncleNumber, u256>; using errinfo_uncleNumber = boost::error_info<struct tag_uncleNumber, u256>;
using errinfo_unclesExcluded = boost::error_info<struct tag_unclesExcluded, h256Hash>; using errinfo_unclesExcluded = boost::error_info<struct tag_unclesExcluded, h256Hash>;
using errinfo_block = boost::error_info<struct tag_block, bytes>;
using errinfo_now = boost::error_info<struct tag_now, unsigned>;
using errinfo_transactionIndex = boost::error_info<struct tag_transactionIndex, unsigned>; using errinfo_transactionIndex = boost::error_info<struct tag_transactionIndex, unsigned>;
using errinfo_vmtrace = boost::error_info<struct tag_vmtrace, std::string>; using errinfo_vmtrace = boost::error_info<struct tag_vmtrace, std::string>;
using errinfo_receipts = boost::error_info<struct tag_receipts, std::vector<bytes>>; using errinfo_receipts = boost::error_info<struct tag_receipts, std::vector<bytes>>;
using errinfo_logBloom = boost::error_info<struct tag_logBloom, LogBloom>; using errinfo_required_LogBloom = boost::error_info<struct tag_required_LogBloom, LogBloom>;
using LogBloomMismatchError = boost::tuple<errinfo_logBloom, errinfo_logBloom>; using errinfo_got_LogBloom = boost::error_info<struct tag_get_LogBloom, LogBloom>;
using LogBloomRequirementError = boost::tuple<errinfo_required_LogBloom, errinfo_got_LogBloom>;
class BlockChain; class BlockChain;
class State; class State;

1
libevm/VMFace.h

@ -25,6 +25,7 @@ namespace dev
namespace eth namespace eth
{ {
#define ETH_SIMPLE_EXCEPTION_VM(X) struct X: virtual VMException { public X(): VMException(#X) {} };
struct VMException: virtual Exception {}; struct VMException: virtual Exception {};
struct BreakPointHit: virtual VMException {}; struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {}; struct BadInstruction: virtual VMException {};

4
libevmcore/Exceptions.h

@ -28,8 +28,8 @@ namespace dev
namespace eth namespace eth
{ {
struct InvalidDeposit: virtual Exception {}; DEV_SIMPLE_EXCEPTION(InvalidDeposit);
struct InvalidOpcode: virtual Exception {}; DEV_SIMPLE_EXCEPTION(InvalidOpcode);
} }
} }

Loading…
Cancel
Save