Browse Source

Merge branch 'develop' into netFix

cl-refactor
subtly 9 years ago
parent
commit
c80d9d4a92
  1. 10
      CMakeLists.txt
  2. 18
      alethzero/MainWin.cpp
  3. 8
      alethzero/Transact.cpp
  4. 780
      eth/main.cpp
  5. 3
      ethrpctest/CommandLineInterface.cpp
  6. 20
      ethvm/main.cpp
  7. 2
      evmjit/libevmjit-cpp/Env.cpp
  8. 14
      evmjit/libevmjit-cpp/JitVM.cpp
  9. 2
      evmjit/libevmjit/Compiler.cpp
  10. 2
      evmjit/libevmjit/Ext.cpp
  11. 2
      evmjit/libevmjit/Ext.h
  12. 12
      exp/main.cpp
  13. 2
      libdevcore/Exceptions.h
  14. 5
      libdevcore/Guards.h
  15. 5
      libdevcore/Log.h
  16. 6
      libethcore/BlockInfo.h
  17. 56
      libethereum/Account.cpp
  18. 41
      libethereum/Account.h
  19. 5
      libethereum/BasicGasPricer.cpp
  20. 2
      libethereum/BasicGasPricer.h
  21. 812
      libethereum/Block.cpp
  22. 310
      libethereum/Block.h
  23. 29
      libethereum/BlockChain.cpp
  24. 12
      libethereum/BlockChain.h
  25. 29
      libethereum/CanonBlockChain.cpp
  26. 4
      libethereum/CanonBlockChain.h
  27. 85
      libethereum/Client.cpp
  28. 27
      libethereum/Client.h
  29. 23
      libethereum/ClientBase.cpp
  30. 15
      libethereum/ClientBase.h
  31. 37
      libethereum/Executive.cpp
  32. 39
      libethereum/Executive.h
  33. 10
      libethereum/ExtVM.cpp
  34. 4
      libethereum/ExtVM.h
  35. 6
      libethereum/GasPricer.h
  36. 2
      libethereum/Interface.h
  37. 4
      libethereum/LogFilter.cpp
  38. 3
      libethereum/LogFilter.h
  39. 799
      libethereum/State.cpp
  40. 153
      libethereum/State.h
  41. 6
      libevm/ExtVMFace.cpp
  42. 66
      libevm/ExtVMFace.h
  43. 12
      libevm/VM.cpp
  44. 5
      libevmasm/KnownState.h
  45. 20
      libtestutils/BlockChainLoader.cpp
  46. 7
      libtestutils/BlockChainLoader.h
  47. 4
      libtestutils/FixedClient.cpp
  48. 12
      libtestutils/FixedClient.h
  49. 57
      libtestutils/StateLoader.cpp
  50. 49
      libtestutils/StateLoader.h
  51. 2
      libweb3jsonrpc/JsonHelper.cpp
  52. 15
      libweb3jsonrpc/WebThreeStubServer.cpp
  53. 71
      mix/MixClient.cpp
  54. 17
      mix/MixClient.h
  55. 2
      solc/docker_emscripten/Dockerfile
  56. 200
      test/TestHelper.cpp
  57. 40
      test/TestHelper.h
  58. 4
      test/TestUtils.cpp
  59. 418
      test/contracts/AuctionRegistrar.cpp
  60. 5
      test/contracts/FixedFeeRegistrar.cpp
  61. 4
      test/contracts/Wallet.cpp
  62. 23
      test/fuzzTesting/CMakeLists.txt
  63. 2
      test/libethcore/keymanager.cpp
  64. 59
      test/libethereum/BlockchainTestsFiller/bcInvalidHeaderTestFiller.json
  65. 4
      test/libethereum/BlockchainTestsFiller/bcValidBlockTestFiller.json
  66. 2
      test/libethereum/ClientBase.cpp
  67. 1
      test/libethereum/StateTestsFiller/stExampleFiller.json
  68. 169
      test/libethereum/Transaction.cpp
  69. 305
      test/libethereum/blockchain.cpp
  70. 21
      test/libethereum/gaspricer.cpp
  71. 18
      test/libethereum/state.cpp
  72. 19
      test/libethereum/stateOriginal.cpp
  73. 2
      test/libethereum/transactionTests.cpp
  74. 58
      test/libevm/vm.cpp
  75. 4
      test/libevm/vm.h
  76. 3
      test/libp2p/net.cpp
  77. 9
      test/libp2p/peer.cpp
  78. 12
      test/libsolidity/SolidityEndToEndTest.cpp
  79. 68
      test/libsolidity/solidityExecutionFramework.h
  80. 6
      test/libweb3jsonrpc/jsonrpc.cpp
  81. 264
      test/libwhisper/shhrpc.cpp
  82. 1
      test/libwhisper/whisperTopic.cpp
  83. 4
      third/MainWin.cpp

10
CMakeLists.txt

@ -58,7 +58,7 @@ set(D_PARANOID OFF)
set(D_PROFILING OFF)
set(D_ROCKSDB OFF)
set(D_NOBOOST OFF)
set(D_FRONTIER ON)
set(D_OLYMPIC OFF)
set(D_MINER ON)
set(D_ETHKEY ON)
@ -189,7 +189,9 @@ function(configureProject)
add_definitions(-DNOBOOST)
endif()
if (FRONTIER)
if (OLYMPIC)
add_definitions(-DETH_OLYMPIC)
else()
add_definitions(-DETH_FRONTIER)
endif()
@ -301,7 +303,7 @@ eth_format_option(TOOLS)
eth_format_option(ETHKEY)
eth_format_option(ETHASHCL)
eth_format_option(JSCONSOLE)
eth_format_option(FRONTIER)
eth_format_option(OLYMPIC)
eth_format_option(SERPENT)
if (JSCONSOLE)
@ -342,7 +344,7 @@ message("-- FATDB Full database exploring ${FATDB}")
message("-- JSONRPC JSON-RPC support ${JSONRPC}")
message("-- USENPM Javascript source building ${USENPM}")
message("-- ROCKSDB Prefer rocksdb to leveldb ${ROCKSDB}")
message("-- FRONTIER Default to the Frontier network ${FRONTIER}")
message("-- OLYMPIC Default to the Olympic network ${OLYMPIC}")
message("------------------------------------------------------------- components")
message("-- MINER Build miner ${MINER}")
message("-- ETHKEY Build wallet tools ${ETHKEY}")

18
alethzero/MainWin.cpp

@ -278,9 +278,6 @@ Main::Main(QWidget* _parent):
connect(ui->blockChainDockWidget, &QDockWidget::visibilityChanged, [=]() { refreshBlockChain(); });
#if !ETH_FATDB
removeDockWidget(ui->dockWidget_accounts);
#endif
installWatches();
startTimer(100);
@ -293,7 +290,9 @@ Main::Main(QWidget* _parent):
}
}
#if ETH_FATDB
loadPlugin<dev::az::AllAccounts>();
#endif
loadPlugin<dev::az::LogPanel>();
}
@ -1149,7 +1148,7 @@ void Main::setBeneficiary(Address const& _b)
ui->ourAccounts->item(i)->setCheckState(h == _b ? Qt::Checked : Qt::Unchecked);
}
m_beneficiary = _b;
ethereum()->setAddress(_b);
ethereum()->setBeneficiary(_b);
}
void Main::on_ourAccounts_itemClicked(QListWidgetItem* _i)
@ -1664,7 +1663,7 @@ void Main::on_blocks_currentItemChanged()
s << "<div>D/TD: <b>" << info.difficulty() << "</b>/<b>" << details.totalDifficulty << "</b> = 2^" << log2((double)info.difficulty()) << "/2^" << log2((double)details.totalDifficulty) << "</div>";
s << "&nbsp;&emsp;&nbsp;Children: <b>" << details.children.size() << "</b></div>";
s << "<div>Gas used/limit: <b>" << info.gasUsed() << "</b>/<b>" << info.gasLimit() << "</b>" << "</div>";
s << "<div>Beneficiary: <b>" << htmlEscaped(pretty(info.coinbaseAddress())) << " " << info.coinbaseAddress() << "</b>" << "</div>";
s << "<div>Beneficiary: <b>" << htmlEscaped(pretty(info.beneficiary())) << " " << info.beneficiary() << "</b>" << "</div>";
s << "<div>Seed hash: <b>" << info.seedHash() << "</b>" << "</div>";
s << "<div>Mix hash: <b>" << info.mixHash() << "</b>" << "</div>";
s << "<div>Nonce: <b>" << info.nonce() << "</b>" << "</div>";
@ -1697,7 +1696,7 @@ void Main::on_blocks_currentItemChanged()
s << line << "Hash: <b>" << uncle.hash() << "</b>" << "</div>";
s << line << "Parent: <b>" << uncle.parentHash() << "</b>" << "</div>";
s << line << "Number: <b>" << uncle.number() << "</b>" << "</div>";
s << line << "Coinbase: <b>" << htmlEscaped(pretty(uncle.coinbaseAddress())) << " " << uncle.coinbaseAddress() << "</b>" << "</div>";
s << line << "Coinbase: <b>" << htmlEscaped(pretty(uncle.beneficiary())) << " " << uncle.beneficiary() << "</b>" << "</div>";
s << line << "Seed hash: <b>" << uncle.seedHash() << "</b>" << "</div>";
s << line << "Mix hash: <b>" << uncle.mixHash() << "</b>" << "</div>";
s << line << "Nonce: <b>" << uncle.nonce() << "</b>" << "</div>";
@ -1787,7 +1786,8 @@ void Main::on_debugCurrent_triggered()
unsigned txi = item->data(Qt::UserRole + 1).toInt();
bytes t = ethereum()->blockChain().transaction(h, txi);
State s(ethereum()->state(txi, h));
Executive e(s, ethereum()->blockChain());
BlockInfo bi(ethereum()->blockChain().info(h));
Executive e(s, ethereum()->blockChain(), EnvInfo(bi));
Debugger dw(this, this);
dw.populate(e, Transaction(t, CheckTransaction::Everything));
dw.exec();
@ -1816,7 +1816,7 @@ void Main::on_dumpBlockState_triggered()
{
f << "{" << endl;
// js::mObject s;
State state = ethereum()->state(h);
State state = ethereum()->block(h).state();
int fi = 0;
for (pair<Address, u256> const& i: state.addresses())
{
@ -2020,7 +2020,7 @@ void Main::on_mine_triggered()
if (ui->mine->isChecked())
{
// EthashAux::computeFull(ethereum()->blockChain().number());
ethereum()->setAddress(m_beneficiary);
ethereum()->setBeneficiary(m_beneficiary);
ethereum()->startMining();
}
else

8
alethzero/Transact.cpp

@ -522,13 +522,13 @@ void Transact::on_debug_clicked()
try
{
State st(ethereum()->postState());
Block postState(ethereum()->postState());
Transaction t = isCreation() ?
Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(from)) :
Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, st.transactionsFrom(from));
Transaction(value(), gasPrice(), ui->gas->value(), m_data, postState.transactionsFrom(from)) :
Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText().toStdString()).first, m_data, postState.transactionsFrom(from));
t.forceSender(from);
Debugger dw(m_context, this);
Executive e(st, ethereum()->blockChain(), 0);
Executive e(postState, ethereum()->blockChain(), 0);
dw.populate(e, t);
dw.exec();
}

780
eth/main.cpp

@ -310,783 +310,7 @@ void stopMiningAfterXBlocks(eth::Client* _c, unsigned _start, unsigned& io_minin
this_thread::sleep_for(chrono::milliseconds(100));
}
void interactiveMode(eth::Client* c, std::shared_ptr<eth::TrivialGasPricer> gasPricer, WebThreeDirect& web3, KeyManager& keyManager, string& logbuf, string& additional, function<string(string const&)> getPassword, function<string(Address const&)> getAccountPassword, NetworkPreferences netPrefs, Address beneficiary, Address signingKey, TransactionPriority priority)
{
additional = "Press Enter";
string l;
while (!g_exit)
{
g_silence = false;
cout << logbuf << "Press Enter" << flush;
std::getline(cin, l);
logbuf.clear();
g_silence = true;
#if ETH_READLINE
if (l.size())
add_history(l.c_str());
if (auto c = readline("> "))
{
l = c;
free(c);
}
else
break;
#else
string l;
cout << "> " << flush;
std::getline(cin, l);
#endif
istringstream iss(l);
string cmd;
iss >> cmd;
boost::to_lower(cmd);
if (cmd == "netstart")
{
iss >> netPrefs.listenPort;
web3.setNetworkPreferences(netPrefs);
web3.startNetwork();
}
else if (cmd == "connect")
{
string addrPort;
iss >> addrPort;
web3.addNode(p2p::NodeId(), addrPort);
}
else if (cmd == "netstop")
web3.stopNetwork();
else if (c && cmd == "minestart")
c->startMining();
else if (c && cmd == "minestop")
c->stopMining();
else if (c && cmd == "mineforce")
{
string enable;
iss >> enable;
c->setForceMining(isTrue(enable));
}
else if (cmd == "verbosity")
{
if (iss.peek() != -1)
iss >> g_logVerbosity;
cout << "Verbosity: " << g_logVerbosity << endl;
}
else if (cmd == "address")
{
cout << "Current mining beneficiary:" << endl << beneficiary << endl;
cout << "Current signing account:" << endl << signingKey << endl;
}
else if (c && cmd == "blockhashfromnumber")
{
if (iss.peek() != -1)
{
unsigned number;
iss >> number;
cout << " hash of block: " << c->hashFromNumber(number).hex() << endl;
}
}
else if (c && cmd == "numberfromblockhash")
{
if (iss.peek() != -1)
{
string stringHash;
iss >> stringHash;
h256 hash = h256(fromHex(stringHash));
cout << " number of block: " << c->numberFromHash(hash) << endl;
}
}
else if (c && cmd == "block")
cout << "Current block: " << c->blockChain().details().number << endl;
else if (c && cmd == "blockqueue")
cout << "Current blockqueue status: " << endl << c->blockQueueStatus() << endl;
else if (c && cmd == "sync")
cout << "Current sync status: " << endl << c->syncStatus() << endl;
else if (c && cmd == "hashrate")
cout << "Current hash rate: " << toString(c->hashrate()) << " hashes per second." << endl;
else if (c && cmd == "findblock")
{
if (iss.peek() != -1)
{
string stringHash;
iss >> stringHash;
h256 hash = h256(fromHex(stringHash));
// search in blockchain
cout << "search in blockchain... " << endl;
try
{
cout << c->blockInfo(hash) << endl;
}
catch(Exception& _e)
{
cout << "block not in blockchain" << endl;
cout << boost::diagnostic_information(_e) << endl;
}
cout << "search in blockqueue... " << endl;
switch(c->blockQueue().blockStatus(hash))
{
case QueueStatus::Ready:
cout << "Ready" << endl;
break;
case QueueStatus::Importing:
cout << "Importing" << endl;
break;
case QueueStatus::UnknownParent:
cout << "UnknownParent" << endl;
break;
case QueueStatus::Bad:
cout << "Bad" << endl;
break;
case QueueStatus::Unknown:
cout << "Unknown" << endl;
break;
default:
cout << "invalid queueStatus" << endl;
}
}
else
cwarn << "Require parameter: findblock HASH";
}
else if (c && cmd == "firstunknown")
cout << "first unknown blockhash: " << c->blockQueue().firstUnknown().hex() << endl;
else if (c && cmd == "retryunknown")
c->retryUnknown();
else if (cmd == "peers")
{
for (auto it: web3.peers())
cout << it.host << ":" << it.port << ", " << it.clientVersion << ", "
<< std::chrono::duration_cast<std::chrono::milliseconds>(it.lastPing).count() << "ms"
<< endl;
}
else if (cmd == "newaccount")
{
string name;
std::getline(iss, name);
auto s = Secret::random();
string password;
while (password.empty())
{
password = getPassword("Please enter a password to protect this key (press enter for protection only be the MASTER password/keystore): ");
string confirm = getPassword("Please confirm the password by entering it again: ");
if (password != confirm)
{
cout << "Passwords were different. Try again." << endl;
password.clear();
}
}
if (!password.empty())
{
cout << "Enter a hint for this password: " << flush;
string hint;
std::getline(cin, hint);
keyManager.import(s, name, password, hint);
}
else
keyManager.import(s, name);
cout << "New account created: " << toAddress(s);
}
else if (c && cmd == "accounts")
{
cout << "Accounts:" << endl;
u256 total = 0;
for (auto const& address: keyManager.accounts())
{
auto b = c->balanceAt(address);
cout << ((address == signingKey) ? "SIGNING " : " ") << ((address == beneficiary) ? "COINBASE " : " ") << keyManager.accountName(address) << " (" << address << "): " << formatBalance(b) << " = " << b << " wei" << endl;
total += b;
}
cout << "Total: " << formatBalance(total) << " = " << total << " wei" << endl;
}
else if (c && cmd == "transact")
{
auto const& bc =c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
if (iss.peek() != -1)
{
string hexAddr;
u256 amount;
u256 gasPrice;
u256 gas;
string sechex;
string sdata;
iss >> hexAddr >> amount >> gasPrice >> gas >> sechex >> sdata;
if (!gasPrice)
gasPrice = gasPricer->bid(priority);
cnote << "Data:";
cnote << sdata;
bytes data = dev::eth::parseData(sdata);
cnote << "Bytes:";
string sbd = asString(data);
bytes bbd = asBytes(sbd);
stringstream ssbd;
ssbd << bbd;
cnote << ssbd.str();
int ssize = sechex.length();
int size = hexAddr.length();
u256 minGas = (u256)Transaction::gasRequired(data, 0);
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
{
if (ssize > 0)
cwarn << "Invalid secret length:" << ssize;
}
else
{
try
{
Secret secret(fromHex(sechex));
Address dest = h160(fromHex(hexAddr));
c->submitTransaction(secret, amount, dest, data, gas, gasPrice);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
else
cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA";
}
else if (c && cmd == "transactnonce")
{
auto const& bc =c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
if (iss.peek() != -1)
{
string hexAddr;
u256 amount;
u256 gasPrice;
u256 gas;
string sechex;
string sdata;
u256 nonce;
iss >> hexAddr >> amount >> gasPrice >> gas >> sechex >> sdata >> nonce;
if (!gasPrice)
gasPrice = gasPricer->bid(priority);
cnote << "Data:";
cnote << sdata;
bytes data = dev::eth::parseData(sdata);
cnote << "Bytes:";
string sbd = asString(data);
bytes bbd = asBytes(sbd);
stringstream ssbd;
ssbd << bbd;
cnote << ssbd.str();
int ssize = sechex.length();
int size = hexAddr.length();
u256 minGas = (u256)Transaction::gasRequired(data, 0);
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
{
if (ssize > 0)
cwarn << "Invalid secret length:" << ssize;
}
else
{
try
{
Secret secret(fromHex(sechex));
Address dest = h160(fromHex(hexAddr));
c->submitTransaction(secret, amount, dest, data, gas, gasPrice, nonce);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
else
cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET DATA NONCE";
}
else if (c && cmd == "txcreate")
{
auto const& bc =c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
if (iss.peek() != -1)
{
u256 amount;
u256 gasPrice;
u256 gas;
string sechex;
string sdata;
iss >> amount >> gasPrice >> gas >> sechex >> sdata;
if (!gasPrice)
gasPrice = gasPricer->bid(priority);
cnote << "Data:";
cnote << sdata;
bytes data = dev::eth::parseData(sdata);
cnote << "Bytes:";
string sbd = asString(data);
bytes bbd = asBytes(sbd);
stringstream ssbd;
ssbd << bbd;
cnote << ssbd.str();
int ssize = sechex.length();
u256 minGas = (u256)Transaction::gasRequired(data, 0);
if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
{
if (ssize > 0)
cwarn << "Invalid secret length:" << ssize;
}
else
{
try
{
Secret secret(fromHex(sechex));
cout << " new contract address : " << c->submitTransaction(secret, amount, data, gas, gasPrice) << endl;
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
else
cwarn << "Require parameters: submitTransaction ADDRESS AMOUNT GASPRICE GAS SECRET INIT";
}
#if ETH_FATDB
else if (c && cmd == "listcontracts")
{
auto acs =c->addresses();
string ss;
for (auto const& i: acs)
if ( c->codeAt(i, PendingBlock).size())
{
ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]";
cout << ss << endl;
}
}
else if (c && cmd == "listaccounts")
{
auto acs =c->addresses();
string ss;
for (auto const& i: acs)
if ( c->codeAt(i, PendingBlock).empty())
{
ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]";
cout << ss << endl;
}
}
else if (c && cmd == "balanceat")
{
if (iss.peek() != -1)
{
string stringHash;
iss >> stringHash;
Address address = h160(fromHex(stringHash));
cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address)) << endl;
}
}
else if (c && cmd == "balanceatblock")
{
if (iss.peek() != -1)
{
string stringHash;
unsigned blocknumber;
iss >> stringHash >> blocknumber;
Address address = h160(fromHex(stringHash));
cout << "balance of " << stringHash << " is: " << toString(c->balanceAt(address, blocknumber)) << endl;
}
}
else if (c && cmd == "storageat")
{
if (iss.peek() != -1)
{
string stringHash;
iss >> stringHash;
Address address = h160(fromHex(stringHash));
cout << "storage at " << stringHash << " is: " << endl;
for (auto s: c->storageAt(address))
cout << toHex(s.first) << " : " << toHex(s.second) << endl;
}
}
else if (c && cmd == "storageatblock")
{
if (iss.peek() != -1)
{
string stringHash;
unsigned blocknumber;
iss >> stringHash >> blocknumber;
Address address = h160(fromHex(stringHash));
cout << "storage at " << stringHash << " is: " << endl;
for (auto s: c->storageAt(address, blocknumber))
cout << "\"0x" << toHex(s.first) << "\" : \"0x" << toHex(s.second) << "\"," << endl;
}
}
else if (c && cmd == "codeat")
{
if (iss.peek() != -1)
{
string stringHash;
iss >> stringHash;
Address address = h160(fromHex(stringHash));
cout << "code at " << stringHash << " is: " << toHex(c->codeAt(address)) << endl;
}
}
#endif
else if (c && cmd == "send")
{
if (iss.peek() != -1)
{
string hexAddr;
u256 amount;
iss >> hexAddr >> amount;
int size = hexAddr.length();
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else
{
auto const& bc =c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Transaction::gasRequired(bytes(), 0);
try
{
Address dest = h160(fromHex(hexAddr, WhenError::Throw));
c->submitTransaction(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, transaction rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "transaction rejected";
}
}
}
else
cwarn << "Require parameters: send ADDRESS AMOUNT";
}
else if (c && cmd == "contract")
{
auto const& bc =c->blockChain();
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
if (iss.peek() != -1)
{
u256 endowment;
u256 gas;
u256 gasPrice;
string sinit;
iss >> endowment >> gasPrice >> gas >> sinit;
trim_all(sinit);
int size = sinit.length();
bytes init;
cnote << "Init:";
cnote << sinit;
cnote << "Code size:" << size;
if (size < 1)
cwarn << "No code submitted";
else
{
cnote << "Assembled:";
stringstream ssc;
try
{
init = fromHex(sinit, WhenError::Throw);
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, code rejected";
cwarn << boost::diagnostic_information(_e);
init = bytes();
}
catch (...)
{
cwarn << "code rejected";
init = bytes();
}
ssc.str(string());
ssc << disassemble(init);
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Transaction::gasRequired(init, 0);
if (!init.size())
cwarn << "Contract creation aborted, no init code.";
else if (endowment < 0)
cwarn << "Invalid endowment";
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else
c->submitTransaction(keyManager.secret(signingKey, [&](){ return getAccountPassword(signingKey); }), endowment, init, gas, gasPrice);
}
else
cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX";
}
else if (c && cmd == "dumpreceipt")
{
unsigned block;
unsigned index;
iss >> block >> index;
dev::eth::TransactionReceipt r = c->blockChain().receipts(c->blockChain().numberHash(block)).receipts[index];
auto rb = r.rlp();
cout << "RLP: " << RLP(rb) << endl;
cout << "Hex: " << toHex(rb) << endl;
cout << r << endl;
}
else if (c && cmd == "reprocess")
{
string block;
iss >> block;
h256 blockHash;
try
{
if (block.size() == 64 || block.size() == 66)
blockHash = h256(block);
else
blockHash = c->blockChain().numberHash(stoi(block));
c->state(blockHash);
}
catch (...)
{}
}
else if (c && cmd == "dumptrace")
{
unsigned block;
unsigned index;
string filename;
string format;
iss >> block >> index >> filename >> format;
ofstream f;
f.open(filename);
dev::eth::State state = c->state(index + 1,c->blockChain().numberHash(block));
if (index < state.pending().size())
{
Executive e(state, c->blockChain(), 0);
Transaction t = state.pending()[index];
state = state.fromPending(index);
try
{
OnOpFunc oof;
if (format == "pretty")
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << endl << " STACK" << endl;
for (auto i: vm->stack())
f << (h256)i << endl;
std::string memDump = (
(vm->memory().size() > 1000) ?
" mem size greater than 1000 bytes " :
dev::memDump(vm->memory())
);
f << " MEMORY" << endl << memDump;
f << " STORAGE" << endl;
for (auto const& i: ext->state().storage(ext->myAddress))
f << showbase << hex << i.first << ": " << i.second << endl;
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << gas << " | -" << dec << gasCost << " | " << newMemSize << "x32";
};
else if (format == "standard")
oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
};
else if (format == "standard+")
oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE)
for (auto const& i: ext->state().storage(ext->myAddress))
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
};
e.initialize(t);
if (!e.execute())
e.go(oof);
e.finalize();
}
catch(Exception const& _e)
{
// TODO: a bit more information here. this is probably quite worrying as the transaction is already in the blockchain.
cwarn << diagnostic_information(_e);
}
}
}
else if (c && cmd == "inspect")
{
string rechex;
iss >> rechex;
if (rechex.length() != 40)
cwarn << "Invalid address length";
else
{
auto h = h160(fromHex(rechex));
stringstream s;
try
{
auto storage =c->storageAt(h, PendingBlock);
for (auto const& i: storage)
s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl;
s << endl << disassemble( c->codeAt(h, PendingBlock)) << endl;
string outFile = getDataDir() + "/" + rechex + ".evm";
ofstream ofs;
ofs.open(outFile, ofstream::binary);
ofs.write(s.str().c_str(), s.str().length());
ofs.close();
cnote << "Saved" << rechex << "to" << outFile;
}
catch (dev::InvalidTrie)
{
cwarn << "Corrupted trie.";
}
}
}
else if (cmd == "setsigningkey")
{
if (iss.peek() != -1)
{
string hexSec;
iss >> hexSec;
signingKey = Address(fromHex(hexSec));
}
else
cwarn << "Require parameter: setSecret HEXSECRETKEY";
}
else if (cmd == "setaddress")
{
if (iss.peek() != -1)
{
string hexAddr;
iss >> hexAddr;
if (hexAddr.length() != 40)
cwarn << "Invalid address length: " << hexAddr.length();
else
{
try
{
beneficiary = h160(fromHex(hexAddr, WhenError::Throw));
}
catch (BadHexCharacter& _e)
{
cwarn << "invalid hex character, coinbase rejected";
cwarn << boost::diagnostic_information(_e);
}
catch (...)
{
cwarn << "coinbase rejected";
}
}
}
else
cwarn << "Require parameter: setAddress HEXADDRESS";
}
else if (cmd == "exportconfig")
{
if (iss.peek() != -1)
{
string path;
iss >> path;
writeFile(path, rlpList(signingKey, beneficiary));
}
else
cwarn << "Require parameter: exportConfig PATH";
}
else if (cmd == "importconfig")
{
if (iss.peek() != -1)
{
string path;
iss >> path;
bytes b = contents(path);
if (b.size())
{
RLP config(b);
signingKey = config[0].toHash<Address>();
beneficiary = config[1].toHash<Address>();
}
else
cwarn << path << "has no content!";
}
else
cwarn << "Require parameter: importConfig PATH";
}
else if (cmd == "help")
interactiveHelp();
else if (cmd == "exit")
break;
else
cout << "Unrecognised command. Type 'help' for help in interactive mode." << endl;
}
}
void interactiveMode(eth::Client*, std::shared_ptr<eth::TrivialGasPricer>, WebThreeDirect&, KeyManager&, string&, string&, function<string(string const&)>, function<string(Address const&)>, NetworkPreferences, Address, Address, TransactionPriority) {}
int main(int argc, char** argv)
{
@ -1791,7 +1015,7 @@ int main(int argc, char** argv)
// TODO: expose sealant interface.
c->setShouldPrecomputeDAG(m.shouldPrecompute());
c->setTurboMining(m.minerType() == MinerCLI::MinerType::GPU);
c->setAddress(beneficiary);
c->setBeneficiary(beneficiary);
c->setNetworkId(networkId);
}

3
ethrpctest/CommandLineInterface.cpp

@ -114,7 +114,8 @@ void sighandler(int)
void CommandLineInterface::actOnInput()
{
BlockChainLoader bcl(m_json);
FixedClient client(bcl.bc(), bcl.state());
cerr << "void CommandLineInterface::actOnInput() FixedClient now accepts eth::Block!!!" << endl;
FixedClient client(bcl.bc(), eth::Block{}/*bcl.state()*/);
unique_ptr<FixedWebThreeServer> jsonrpcServer;
auto server = new jsonrpc::HttpServer(8080, "", "", 2);
jsonrpcServer.reset(new FixedWebThreeServer(*server, {}, &client));

20
ethvm/main.cpp

@ -21,11 +21,12 @@
*/
#include <fstream>
#include <iostream>
#include <ctime>
#include <boost/algorithm/string.hpp>
#include <libdevcore/CommonIO.h>
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include <libethereum/State.h>
#include <libethereum/Block.h>
#include <libethereum/Executive.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
@ -83,10 +84,11 @@ int main(int argc, char** argv)
Address sender = Address(69);
Address origin = Address(69);
u256 value = 0;
u256 gas = state.gasLimitRemaining();
u256 gas = Block().gasLimitRemaining();
u256 gasPrice = 0;
bool styledJson = true;
StandardTrace st;
EnvInfo envInfo;
for (int i = 1; i < argc; ++i)
{
@ -130,6 +132,18 @@ int main(int argc, char** argv)
value = u256(argv[++i]);
else if (arg == "--value" && i + 1 < argc)
value = u256(argv[++i]);
else if (arg == "--beneficiary" && i + 1 < argc)
envInfo.setBeneficiary(Address(argv[++i]));
else if (arg == "--number" && i + 1 < argc)
envInfo.setNumber(u256(argv[++i]));
else if (arg == "--difficulty" && i + 1 < argc)
envInfo.setDifficulty(u256(argv[++i]));
else if (arg == "--timestamp" && i + 1 < argc)
envInfo.setTimestamp(u256(argv[++i]));
else if (arg == "--gas-limit" && i + 1 < argc)
envInfo.setGasLimit(u256(argv[++i]));
else if (arg == "--value" && i + 1 < argc)
value = u256(argv[++i]);
else if (arg == "stats")
mode = Mode::Statistics;
else if (arg == "output")
@ -151,7 +165,7 @@ int main(int argc, char** argv)
data = code;
state.addBalance(sender, value);
Executive executive(state, eth::LastHashes(), 0);
Executive executive(state, envInfo);
ExecutionResult res;
executive.setResultRecipient(res);
Transaction t = eth::Transaction(value, gasPrice, gas, data, 0);

2
evmjit/libevmjit-cpp/Env.cpp

@ -44,7 +44,7 @@ extern "C"
EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash)
{
*o_hash = _env->blockhash(jit2eth(*_number));
*o_hash = _env->blockHash(jit2eth(*_number));
}
EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)

14
evmjit/libevmjit-cpp/JitVM.cpp

@ -23,8 +23,8 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
rejected |= _ext.currentBlock.number() > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp() > std::numeric_limits<decltype(m_data.timestamp)>::max();
rejected |= _ext.envInfo().number() > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.envInfo().timestamp() > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (rejected)
{
@ -41,11 +41,11 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
m_data.caller = eth2jit(fromAddress(_ext.caller));
m_data.origin = eth2jit(fromAddress(_ext.origin));
m_data.callValue = eth2jit(_ext.value);
m_data.coinBase = eth2jit(fromAddress(_ext.currentBlock.coinbaseAddress()));
m_data.difficulty = eth2jit(_ext.currentBlock.difficulty());
m_data.gasLimit = eth2jit(_ext.currentBlock.gasLimit());
m_data.number = static_cast<decltype(m_data.number)>(_ext.currentBlock.number());
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp());
m_data.coinBase = eth2jit(fromAddress(_ext.envInfo().beneficiary()));
m_data.difficulty = eth2jit(_ext.envInfo().difficulty());
m_data.gasLimit = eth2jit(_ext.envInfo().gasLimit());
m_data.number = static_cast<decltype(m_data.number)>(_ext.envInfo().number());
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.envInfo().timestamp());
m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2jit(_ext.codeHash);

2
evmjit/libevmjit/Compiler.cpp

@ -700,7 +700,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::BLOCKHASH:
{
auto number = stack.pop();
auto hash = _ext.blockhash(number);
auto hash = _ext.blockHash(number);
stack.push(hash);
break;
}

2
evmjit/libevmjit/Ext.cpp

@ -131,7 +131,7 @@ llvm::Value* Ext::balance(llvm::Value* _address)
return m_builder.CreateLoad(ret);
}
llvm::Value* Ext::blockhash(llvm::Value* _number)
llvm::Value* Ext::blockHash(llvm::Value* _number)
{
auto hash = getArgAlloca();
createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash});

2
evmjit/libevmjit/Ext.h

@ -51,7 +51,7 @@ public:
llvm::Value* calldataload(llvm::Value* _index);
llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize);
llvm::Value* call(llvm::Value* _callGas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress);
llvm::Value* blockhash(llvm::Value* _number);
llvm::Value* blockHash(llvm::Value* _number);
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize);
MemoryRef extcode(llvm::Value* _addr);

12
exp/main.cpp

@ -299,7 +299,7 @@ int main()
#elif 0
void mine(State& s, BlockChain const& _bc, SealEngineFace* _se)
{
s.commitToMine(_bc);
s.commitToSeal(_bc);
Notified<bytes> sealed;
_se->onSealGenerated([&](bytes const& sealedHeader){ sealed = sealedHeader; });
_se->generateSeal(s.info());
@ -329,8 +329,8 @@ int main()
OverlayDB stateDB = State::openDB(bc.genesisHash());
cnote << bc;
State s = bc.genesisState(stateDB);
s.setAddress(myMiner.address());
Block s = bc.genesisBlock(stateDB);
s.setBeneficiary(myMiner.address());
cnote << s;
// Sync up - this won't do much until we use the last state.
@ -362,8 +362,8 @@ int main()
cnote << s;
// Mine to get some ether and set in stone.
s.commitToMine(bc);
s.commitToMine(bc);
s.commitToSeal(bc);
s.commitToSeal(bc);
mine(s, bc, se);
bc.attemptImport(s.blockData(), stateDB);
@ -386,7 +386,7 @@ int main()
cdebug << "Path:" << tempDir;
Client c(&net, tempDir);
c.setAddress(myMiner.address());
c.setBeneficiary(myMiner.address());
this_thread::sleep_for(chrono::milliseconds(1000));

2
libdevcore/Exceptions.h

@ -60,6 +60,8 @@ struct BadRoot: virtual Exception { public: BadRoot(h256 const& _root): Exceptio
DEV_SIMPLE_EXCEPTION(FileError);
DEV_SIMPLE_EXCEPTION(Overflow);
DEV_SIMPLE_EXCEPTION(FailedInvariant);
DEV_SIMPLE_EXCEPTION(ValueTooLarge);
struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} };
struct ExternalFunctionFailure: virtual Exception { public: ExternalFunctionFailure(std::string _f): Exception("Function " + _f + "() failed.") {} };

5
libdevcore/Guards.h

@ -24,7 +24,12 @@
#include <mutex>
#include <condition_variable>
#include <atomic>
#pragma warning(push)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <boost/thread.hpp>
#pragma warning(pop)
#pragma GCC diagnostic pop
namespace dev
{

5
libdevcore/Log.h

@ -25,7 +25,12 @@
#include <ctime>
#include <chrono>
#pragma warning(push)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <boost/thread.hpp>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include "vector_ref.h"
#include "Common.h"
#include "CommonIO.h"

6
libethcore/BlockInfo.h

@ -97,7 +97,7 @@ public:
{
return m_parentHash == _cmp.parentHash() &&
m_sha3Uncles == _cmp.sha3Uncles() &&
m_coinbaseAddress == _cmp.coinbaseAddress() &&
m_coinbaseAddress == _cmp.beneficiary() &&
m_stateRoot == _cmp.stateRoot() &&
m_transactionsRoot == _cmp.transactionsRoot() &&
m_receiptsRoot == _cmp.receiptsRoot() &&
@ -134,7 +134,7 @@ public:
void setLogBloom(LogBloom const& _v) { m_logBloom = _v; noteDirty(); }
void setDifficulty(u256 const& _v) { m_difficulty = _v; noteDirty(); }
Address const& coinbaseAddress() const { return m_coinbaseAddress; }
Address const& beneficiary() const { return m_coinbaseAddress; }
h256 const& stateRoot() const { return m_stateRoot; }
h256 const& transactionsRoot() const { return m_transactionsRoot; }
h256 const& receiptsRoot() const { return m_receiptsRoot; }
@ -182,7 +182,7 @@ private:
inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi)
{
_out << _bi.hashWithout() << " " << _bi.parentHash() << " " << _bi.sha3Uncles() << " " << _bi.coinbaseAddress() << " " << _bi.stateRoot() << " " << _bi.transactionsRoot() << " " <<
_out << _bi.hashWithout() << " " << _bi.parentHash() << " " << _bi.sha3Uncles() << " " << _bi.beneficiary() << " " << _bi.stateRoot() << " " << _bi.transactionsRoot() << " " <<
_bi.receiptsRoot() << " " << _bi.logBloom() << " " << _bi.difficulty() << " " << _bi.number() << " " << _bi.gasLimit() << " " <<
_bi.gasUsed() << " " << _bi.timestamp();
return _out;

56
libethereum/Account.cpp

@ -20,11 +20,67 @@
*/
#include "Account.h"
#include <test/JsonSpiritHeaders.h>
#include <libethcore/Common.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace js = json_spirit;
#pragma GCC diagnostic ignored "-Wunused-variable"
const h256 Account::c_contractConceptionCodeHash;
AccountMap dev::eth::jsonToAccountMap(std::string const& _json, AccountMaskMap* o_mask)
{
auto u256Safe = [](std::string const& s) -> u256 {
bigint ret(s);
if (ret >= bigint(1) << 256)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State value is equal or greater than 2**256") );
return (u256)ret;
};
std::unordered_map<Address, Account> ret;
js::mValue val;
json_spirit::read_string(_json, val);
for (auto account: val.get_obj().count("alloc") ? val.get_obj()["alloc"].get_obj() : val.get_obj())
{
Address a(fromHex(account.first));
auto o = account.second.get_obj();
u256 balance = 0;
bool haveBalance = (o.count("wei") || o.count("finney") || o.count("balance"));
if (o.count("wei"))
balance = u256Safe(o["wei"].get_str());
else if (o.count("finney"))
balance = u256Safe(o["finney"].get_str()) * finney;
else if (o.count("balance"))
balance = u256Safe(o["balance"].get_str());
bool haveCode = o.count("code");
if (haveCode)
{
ret[a] = Account(balance, Account::ContractConception);
ret[a].setCode(fromHex(o["code"].get_str()));
}
else
ret[a] = Account(balance, Account::NormalCreation);
bool haveStorage = o.count("storage");
if (haveStorage)
for (pair<string, js::mValue> const& j: o["storage"].get_obj())
ret[a].setStorage(u256(j.first), u256(j.second.get_str()));
bool haveNonce = o.count("nonce");
if (haveNonce)
for (auto i = 0; i < u256Safe(o["nonce"].get_str()); ++i)
ret[a].incNonce();
if (o_mask)
(*o_mask)[a] = AccountMask(haveBalance, haveNonce, haveCode, haveStorage);
}
return ret;
}

41
libethereum/Account.h

@ -25,6 +25,7 @@
#include <libdevcore/RLP.h>
#include <libdevcore/TrieDB.h>
#include <libdevcore/SHA3.h>
#include <libethcore/Common.h>
namespace dev
{
@ -203,5 +204,45 @@ private:
static const h256 c_contractConceptionCodeHash;
};
class AccountMask
{
public:
AccountMask(bool _all = false):
m_hasBalance(_all),
m_hasNonce(_all),
m_hasCode(_all),
m_hasStorage(_all)
{}
AccountMask(
bool _hasBalance,
bool _hasNonce,
bool _hasCode,
bool _hasStorage
):
m_hasBalance(_hasBalance),
m_hasNonce(_hasNonce),
m_hasCode(_hasCode),
m_hasStorage(_hasStorage)
{}
bool allSet() const { return m_hasBalance && m_hasNonce && m_hasCode && m_hasStorage; }
bool hasBalance() const { return m_hasBalance; }
bool hasNonce() const { return m_hasNonce; }
bool hasCode() const { return m_hasCode; }
bool hasStorage() const { return m_hasStorage; }
private:
bool m_hasBalance;
bool m_hasNonce;
bool m_hasCode;
bool m_hasStorage;
};
using AccountMap = std::unordered_map<Address, Account>;
using AccountMaskMap = std::unordered_map<Address, AccountMask>;
AccountMap jsonToAccountMap(std::string const& _json, AccountMaskMap* o_mask = nullptr);
}
}

5
libethereum/BasicGasPricer.cpp

@ -19,7 +19,12 @@
* @date 2015
*/
#pragma warning(push)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <boost/math/distributions/normal.hpp>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include "BasicGasPricer.h"
#include "BlockChain.h"
using namespace std;

2
libethereum/BasicGasPricer.h

@ -37,7 +37,7 @@ public:
void setRefPrice(u256 _weiPerRef) { if ((bigint)m_refsPerBlock * _weiPerRef > std::numeric_limits<u256>::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_weiPerRef = _weiPerRef; }
void setRefBlockFees(u256 _refsPerBlock) { if ((bigint)m_weiPerRef * _refsPerBlock > std::numeric_limits<u256>::max() ) BOOST_THROW_EXCEPTION(Overflow() << errinfo_comment("ether price * block fees is larger than 2**256-1, choose a smaller number.") ); else m_refsPerBlock = _refsPerBlock; }
u256 ask(State const&) const override { return m_weiPerRef * m_refsPerBlock / m_gasPerBlock; }
u256 ask(Block const&) const override { return m_weiPerRef * m_refsPerBlock / m_gasPerBlock; }
u256 bid(TransactionPriority _p = TransactionPriority::Medium) const override { return m_octiles[(int)_p] > 0 ? m_octiles[(int)_p] : (m_weiPerRef * m_refsPerBlock / m_gasPerBlock); }
void update(BlockChain const& _bc) override;

812
libethereum/Block.cpp

@ -0,0 +1,812 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Block.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Block.h"
#include <ctime>
#include <boost/filesystem.hpp>
#include <boost/timer.hpp>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/StructuredLogger.h>
#include <libdevcore/TrieHash.h>
#include <libevmcore/Instruction.h>
#include <libethcore/Exceptions.h>
#include <libethcore/Params.h>
#include <libevm/VMFactory.h>
#include "BlockChain.h"
#include "Defaults.h"
#include "ExtVM.h"
#include "Executive.h"
#include "CachedAddressState.h"
#include "CanonBlockChain.h"
#include "TransactionQueue.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace fs = boost::filesystem;
#define ctrace clog(BlockTrace)
#define ETH_TIMED_ENACTMENTS 0
static const unsigned c_maxSyncTransactions = 256;
const char* BlockSafeExceptions::name() { return EthViolet "" EthBlue ""; }
const char* BlockDetail::name() { return EthViolet "" EthWhite ""; }
const char* BlockTrace::name() { return EthViolet "" EthGray ""; }
const char* BlockChat::name() { return EthViolet "" EthWhite ""; }
Block::Block(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress):
m_state(_db, _bs),
m_beneficiary(_coinbaseAddress),
m_blockReward(c_blockReward)
{
m_previousBlock.clear();
m_currentBlock.clear();
// assert(m_state.root() == m_previousBlock.stateRoot());
}
Block::Block(Block const& _s):
m_state(_s.m_state),
m_transactions(_s.m_transactions),
m_receipts(_s.m_receipts),
m_transactionSet(_s.m_transactionSet),
m_previousBlock(_s.m_previousBlock),
m_currentBlock(_s.m_currentBlock),
m_beneficiary(_s.m_beneficiary),
m_blockReward(_s.m_blockReward)
{
m_precommit = m_state;
m_committedToMine = false;
}
Block& Block::operator=(Block const& _s)
{
m_state = _s.m_state;
m_transactions = _s.m_transactions;
m_receipts = _s.m_receipts;
m_transactionSet = _s.m_transactionSet;
m_previousBlock = _s.m_previousBlock;
m_currentBlock = _s.m_currentBlock;
m_beneficiary = _s.m_beneficiary;
m_blockReward = _s.m_blockReward;
m_precommit = m_state;
m_committedToMine = false;
return *this;
}
void Block::resetCurrent()
{
m_transactions.clear();
m_receipts.clear();
m_transactionSet.clear();
m_currentBlock = BlockInfo();
m_currentBlock.setCoinbaseAddress(m_beneficiary);
m_currentBlock.setTimestamp(max(m_previousBlock.timestamp() + 1, (u256)time(0)));
m_currentBlock.populateFromParent(m_previousBlock);
// TODO: check.
m_state.setRoot(m_previousBlock.stateRoot());
m_precommit = m_state;
m_committedToMine = false;
}
PopulationStatistics Block::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir)
{
PopulationStatistics ret { 0.0, 0.0 };
if (!_bc.isKnown(_h))
{
// Might be worth throwing here.
cwarn << "Invalid block given for state population: " << _h;
BOOST_THROW_EXCEPTION(BlockNotFound() << errinfo_target(_h));
}
auto b = _bc.block(_h);
BlockInfo bi(b);
if (bi.number())
{
// Non-genesis:
// 1. Start at parent's end state (state root).
BlockInfo bip(_bc.block(bi.parentHash()));
sync(_bc, bi.parentHash(), bip);
// 2. Enact the block's transactions onto this state.
m_beneficiary = bi.beneficiary();
Timer t;
auto vb = _bc.verifyBlock(&b, function<void(Exception&)>(), _ir | ImportRequirements::TransactionBasic);
ret.verify = t.elapsed();
t.restart();
enact(vb, _bc);
ret.enact = t.elapsed();
}
else
{
// Genesis required:
// We know there are no transactions, so just populate directly.
m_state = State(m_state.db(), BaseState::Empty); // TODO: try with PreExisting.
sync(_bc, _h, bi);
}
return ret;
}
bool Block::sync(BlockChain const& _bc)
{
return sync(_bc, _bc.currentHash());
}
bool Block::sync(BlockChain const& _bc, h256 const& _block, BlockInfo const& _bi)
{
bool ret = false;
// BLOCK
BlockInfo bi = _bi ? _bi : _bc.info(_block);
#if ETH_PARANOIA
if (!bi)
while (1)
{
try
{
auto b = _bc.block(_block);
bi.populate(b);
break;
}
catch (Exception const& _e)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << diagnostic_information(_e) << endl;
}
catch (std::exception const& _e)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << _e.what() << endl;
}
}
#endif
if (bi == m_currentBlock)
{
// We mined the last block.
// Our state is good - we just need to move on to next.
m_previousBlock = m_currentBlock;
resetCurrent();
ret = true;
}
else if (bi == m_previousBlock)
{
// No change since last sync.
// Carry on as we were.
}
else
{
// New blocks available, or we've switched to a different branch. All change.
// Find most recent state dump and replay what's left.
// (Most recent state dump might end up being genesis.)
if (m_state.db().lookup(bi.stateRoot()).empty()) // TODO: API in State for this?
{
cwarn << "Unable to sync to" << bi.hash() << "; state root" << bi.stateRoot() << "not found in database.";
cwarn << "Database corrupt: contains block without stateRoot:" << bi;
cwarn << "Try rescuing the database by running: eth --rescue";
BOOST_THROW_EXCEPTION(InvalidStateRoot() << errinfo_target(bi.stateRoot()));
}
m_previousBlock = bi;
resetCurrent();
ret = true;
}
#if ALLOW_REBUILD
else
{
// New blocks available, or we've switched to a different branch. All change.
// Find most recent state dump and replay what's left.
// (Most recent state dump might end up being genesis.)
std::vector<h256> chain;
while (bi.number() != 0 && m_db.lookup(bi.stateRoot()).empty()) // while we don't have the state root of the latest block...
{
chain.push_back(bi.hash()); // push back for later replay.
bi.populate(_bc.block(bi.parentHash())); // move to parent.
}
m_previousBlock = bi;
resetCurrent();
// Iterate through in reverse, playing back each of the blocks.
try
{
for (auto it = chain.rbegin(); it != chain.rend(); ++it)
{
auto b = _bc.block(*it);
enact(&b, _bc, _ir);
cleanup(true);
}
}
catch (...)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << boost::current_exception_diagnostic_information() << endl;
exit(1);
}
resetCurrent();
ret = true;
}
#endif
return ret;
}
pair<TransactionReceipts, bool> Block::sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned msTimeout)
{
// TRANSACTIONS
pair<TransactionReceipts, bool> ret;
ret.second = false;
auto ts = _tq.topTransactions(c_maxSyncTransactions);
LastHashes lh;
auto deadline = chrono::steady_clock::now() + chrono::milliseconds(msTimeout);
for (int goodTxs = 1; goodTxs; )
{
goodTxs = 0;
for (auto const& t: ts)
if (!m_transactionSet.count(t.sha3()))
{
try
{
if (t.gasPrice() >= _gp.ask(*this))
{
// Timer t;
if (lh.empty())
lh = _bc.lastHashes();
execute(lh, t);
ret.first.push_back(m_receipts.back());
++goodTxs;
// cnote << "TX took:" << t.elapsed() * 1000;
}
else if (t.gasPrice() < _gp.ask(*this) * 9 / 10)
{
clog(StateTrace) << t.sha3() << "Dropping El Cheapo transaction (<90% of ask price)";
_tq.drop(t.sha3());
}
}
catch (InvalidNonce const& in)
{
bigint const& req = *boost::get_error_info<errinfo_required>(in);
bigint const& got = *boost::get_error_info<errinfo_got>(in);
if (req > got)
{
// too old
clog(StateTrace) << t.sha3() << "Dropping old transaction (nonce too low)";
_tq.drop(t.sha3());
}
else if (got > req + _tq.waiting(t.sender()))
{
// too new
clog(StateTrace) << t.sha3() << "Dropping new transaction (too many nonces ahead)";
_tq.drop(t.sha3());
}
else
_tq.setFuture(t.sha3());
}
catch (BlockGasLimitReached const& e)
{
bigint const& got = *boost::get_error_info<errinfo_got>(e);
if (got > m_currentBlock.gasLimit())
{
clog(StateTrace) << t.sha3() << "Dropping over-gassy transaction (gas > block's gas limit)";
_tq.drop(t.sha3());
}
else
{
// Temporarily no gas left in current block.
// OPTIMISE: could note this and then we don't evaluate until a block that does have the gas left.
// for now, just leave alone.
}
}
catch (Exception const& _e)
{
// Something else went wrong - drop it.
clog(StateTrace) << t.sha3() << "Dropping invalid transaction:" << diagnostic_information(_e);
_tq.drop(t.sha3());
}
catch (std::exception const&)
{
// Something else went wrong - drop it.
_tq.drop(t.sha3());
cwarn << t.sha3() << "Transaction caused low-level exception :(";
}
}
if (chrono::steady_clock::now() > deadline)
{
ret.second = true;
break;
}
}
return ret;
}
u256 Block::enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc)
{
#if ETH_TIMED_ENACTMENTS
Timer t;
double populateVerify;
double populateGrand;
double syncReset;
double enactment;
#endif
// Check family:
BlockInfo biParent = _bc.info(_block.info.parentHash());
_block.info.verifyParent(biParent);
#if ETH_TIMED_ENACTMENTS
populateVerify = t.elapsed();
t.restart();
#endif
BlockInfo biGrandParent;
if (biParent.number())
biGrandParent = _bc.info(biParent.parentHash());
#if ETH_TIMED_ENACTMENTS
populateGrand = t.elapsed();
t.restart();
#endif
sync(_bc, _block.info.parentHash(), BlockInfo());
resetCurrent();
#if ETH_TIMED_ENACTMENTS
syncReset = t.elapsed();
t.restart();
#endif
m_previousBlock = biParent;
auto ret = enact(_block, _bc);
#if ETH_TIMED_ENACTMENTS
enactment = t.elapsed();
if (populateVerify + populateGrand + syncReset + enactment > 0.5)
clog(StateChat) << "popVer/popGrand/syncReset/enactment = " << populateVerify << "/" << populateGrand << "/" << syncReset << "/" << enactment;
#endif
return ret;
}
u256 Block::enact(VerifiedBlockRef const& _block, BlockChain const& _bc)
{
DEV_TIMED_FUNCTION_ABOVE(500);
// m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE
assert(m_previousBlock.hash() == _block.info.parentHash());
assert(m_currentBlock.parentHash() == _block.info.parentHash());
assert(rootHash() == m_previousBlock.stateRoot());
#endif
if (m_currentBlock.parentHash() != m_previousBlock.hash())
// Internal client error.
BOOST_THROW_EXCEPTION(InvalidParentHash());
// Populate m_currentBlock with the correct values.
m_currentBlock.noteDirty();
m_currentBlock = _block.info;
// cnote << "playback begins:" << m_state.root();
// cnote << m_state;
LastHashes lh;
DEV_TIMED_ABOVE("lastHashes", 500)
lh = _bc.lastHashes((unsigned)m_previousBlock.number());
RLP rlp(_block.block);
vector<bytes> receipts;
// All ok with the block generally. Play back the transactions now...
unsigned i = 0;
DEV_TIMED_ABOVE("txExec", 500)
for (auto const& tr: _block.transactions)
{
try
{
LogOverride<ExecutiveWarnChannel> o(false);
execute(lh, tr);
}
catch (Exception& ex)
{
ex << errinfo_transactionIndex(i);
throw;
}
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
++i;
}
h256 receiptsRoot;
DEV_TIMED_ABOVE(".receiptsRoot()", 500)
receiptsRoot = orderedTrieRoot(receipts);
if (receiptsRoot != m_currentBlock.receiptsRoot())
{
InvalidReceiptsStateRoot ex;
ex << Hash256RequirementError(receiptsRoot, m_currentBlock.receiptsRoot());
ex << errinfo_receipts(receipts);
// ex << errinfo_vmtrace(vmTrace(_block.block, _bc, ImportRequirements::None));
BOOST_THROW_EXCEPTION(ex);
}
if (m_currentBlock.logBloom() != logBloom())
{
InvalidLogBloom ex;
ex << LogBloomRequirementError(logBloom(), m_currentBlock.logBloom());
ex << errinfo_receipts(receipts);
BOOST_THROW_EXCEPTION(ex);
}
// Initialise total difficulty calculation.
u256 tdIncrease = m_currentBlock.difficulty();
// Check uncles & apply their rewards to state.
if (rlp[2].itemCount() > 2)
{
TooManyUncles ex;
ex << errinfo_max(2);
ex << errinfo_got(rlp[2].itemCount());
BOOST_THROW_EXCEPTION(ex);
}
vector<BlockInfo> rewarded;
h256Hash excluded;
DEV_TIMED_ABOVE("allKin", 500)
excluded = _bc.allKinFrom(m_currentBlock.parentHash(), 6);
excluded.insert(m_currentBlock.hash());
unsigned ii = 0;
DEV_TIMED_ABOVE("uncleCheck", 500)
for (auto const& i: rlp[2])
{
try
{
auto h = sha3(i.data());
if (excluded.count(h))
{
UncleInChain ex;
ex << errinfo_comment("Uncle in block already mentioned");
ex << errinfo_unclesExcluded(excluded);
ex << errinfo_hash256(sha3(i.data()));
BOOST_THROW_EXCEPTION(ex);
}
excluded.insert(h);
// IgnoreSeal since it's a VerifiedBlock.
BlockInfo uncle(i.data(), IgnoreSeal, h, HeaderData);
BlockInfo uncleParent;
if (!_bc.isKnown(uncle.parentHash()))
BOOST_THROW_EXCEPTION(UnknownParent());
uncleParent = BlockInfo(_bc.block(uncle.parentHash()));
if ((bigint)uncleParent.number() < (bigint)m_currentBlock.number() - 7)
{
UncleTooOld ex;
ex << errinfo_uncleNumber(uncle.number());
ex << errinfo_currentNumber(m_currentBlock.number());
BOOST_THROW_EXCEPTION(ex);
}
else if (uncle.number() == m_currentBlock.number())
{
UncleIsBrother ex;
ex << errinfo_uncleNumber(uncle.number());
ex << errinfo_currentNumber(m_currentBlock.number());
BOOST_THROW_EXCEPTION(ex);
}
uncle.verifyParent(uncleParent);
rewarded.push_back(uncle);
++ii;
}
catch (Exception& ex)
{
ex << errinfo_uncleIndex(ii);
throw;
}
}
DEV_TIMED_ABOVE("applyRewards", 500)
applyRewards(rewarded);
// Commit all cached state changes to the state trie.
DEV_TIMED_ABOVE("commit", 500)
m_state.commit();
// Hash the state trie and check against the state_root hash in m_currentBlock.
if (m_currentBlock.stateRoot() != m_previousBlock.stateRoot() && m_currentBlock.stateRoot() != rootHash())
{
auto r = rootHash();
m_state.db().rollback(); // TODO: API in State for this?
BOOST_THROW_EXCEPTION(InvalidStateRoot() << Hash256RequirementError(r, m_currentBlock.stateRoot()));
}
if (m_currentBlock.gasUsed() != gasUsed())
{
// Rollback the trie.
m_state.db().rollback(); // TODO: API in State for this?
BOOST_THROW_EXCEPTION(InvalidGasUsed() << RequirementError(bigint(gasUsed()), bigint(m_currentBlock.gasUsed())));
}
return tdIncrease;
}
ExecutionResult Block::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp)
{
// Uncommitting is a non-trivial operation - only do it once we've verified as much of the
// transaction as possible.
uncommitToMine();
std::pair<ExecutionResult, TransactionReceipt> resultReceipt = m_state.execute(EnvInfo(info(), _lh, gasUsed()), _t, _p, _onOp);
if (_p == Permanence::Committed)
{
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(_t);
m_receipts.push_back(resultReceipt.second);
m_transactionSet.insert(_t.sha3());
}
return resultReceipt.first;
}
void Block::applyRewards(vector<BlockInfo> const& _uncleBlockHeaders)
{
u256 r = m_blockReward;
for (auto const& i: _uncleBlockHeaders)
{
m_state.addBalance(i.beneficiary(), m_blockReward * (8 + i.number() - m_currentBlock.number()) / 8);
r += m_blockReward / 32;
}
m_state.addBalance(m_currentBlock.beneficiary(), r);
}
void Block::commitToSeal(BlockChain const& _bc, bytes const& _extraData)
{
if (m_committedToMine)
uncommitToMine();
else
m_precommit = m_state;
vector<BlockInfo> uncleBlockHeaders;
RLPStream unclesData;
unsigned unclesCount = 0;
if (m_previousBlock.number() != 0)
{
// Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations.
clog(StateDetail) << "Checking " << m_previousBlock.hash() << ", parent=" << m_previousBlock.parentHash();
h256Hash excluded = _bc.allKinFrom(m_currentBlock.parentHash(), 6);
auto p = m_previousBlock.parentHash();
for (unsigned gen = 0; gen < 6 && p != _bc.genesisHash() && unclesCount < 2; ++gen, p = _bc.details(p).parent)
{
auto us = _bc.details(p).children;
assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent!
for (auto const& u: us)
if (!excluded.count(u)) // ignore any uncles/mainline blocks that we know about.
{
uncleBlockHeaders.push_back(_bc.info(u));
unclesData.appendRaw(_bc.headerData(u));
++unclesCount;
if (unclesCount == 2)
break;
}
}
}
BytesMap transactionsMap;
BytesMap receiptsMap;
RLPStream txs;
txs.appendList(m_transactions.size());
for (unsigned i = 0; i < m_transactions.size(); ++i)
{
RLPStream k;
k << i;
RLPStream receiptrlp;
m_receipts[i].streamRLP(receiptrlp);
receiptsMap.insert(std::make_pair(k.out(), receiptrlp.out()));
RLPStream txrlp;
m_transactions[i].streamRLP(txrlp);
transactionsMap.insert(std::make_pair(k.out(), txrlp.out()));
txs.appendRaw(txrlp.out());
}
txs.swapOut(m_currentTxs);
RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles);
// Apply rewards last of all.
applyRewards(uncleBlockHeaders);
// Commit any and all changes to the trie that are in the cache, then update the state root accordingly.
m_state.commit();
clog(StateDetail) << "Post-reward stateRoot:" << m_state.rootHash();
clog(StateDetail) << m_state;
clog(StateDetail) << *this;
m_currentBlock.setLogBloom(logBloom());
m_currentBlock.setGasUsed(gasUsed());
m_currentBlock.setRoots(hash256(transactionsMap), hash256(receiptsMap), sha3(m_currentUncles), m_state.rootHash());
m_currentBlock.setParentHash(m_previousBlock.hash());
m_currentBlock.setExtraData(_extraData);
if (m_currentBlock.extraData().size() > 32)
{
auto ed = m_currentBlock.extraData();
ed.resize(32);
m_currentBlock.setExtraData(ed);
}
m_committedToMine = true;
}
void Block::uncommitToMine()
{
if (m_committedToMine)
{
m_state = m_precommit;
m_committedToMine = false;
}
}
bool Block::sealBlock(bytesConstRef _header)
{
if (!m_committedToMine)
return false;
if (BlockInfo(_header, CheckNothing, h256{}, HeaderData).hashWithout() != m_currentBlock.hashWithout())
return false;
clog(StateDetail) << "Sealing block!";
// Compile block:
RLPStream ret;
ret.appendList(3);
ret.appendRaw(_header);
ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes);
m_currentBlock = BlockInfo(_header, CheckNothing, h256(), HeaderData);
cnote << "Mined " << m_currentBlock.hash() << "(parent: " << m_currentBlock.parentHash() << ")";
// TODO: move into Sealer
StructuredLogger::minedNewBlock(
m_currentBlock.hash().abridged(),
"", // Can't give the nonce here.
"", //TODO: chain head hash here ??
m_currentBlock.parentHash().abridged()
);
// 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_precommit = m_state;
return true;
}
State Block::fromPending(unsigned _i) const
{
State ret = m_state;
_i = min<unsigned>(_i, m_transactions.size());
if (!_i)
ret.setRoot(m_previousBlock.stateRoot());
else
ret.setRoot(m_receipts[_i - 1].stateRoot());
return ret;
}
LogBloom Block::logBloom() const
{
LogBloom ret;
for (TransactionReceipt const& i: m_receipts)
ret |= i.bloom();
return ret;
}
void Block::cleanup(bool _fullCommit)
{
if (_fullCommit)
{
// Commit the new trie to disk.
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot() << "=" << rootHash() << "=" << toHex(asBytes(db().lookup(rootHash())));
try
{
EnforceRefs er(db(), true);
rootHash();
}
catch (BadRoot const&)
{
clog(StateChat) << "Trie corrupt! :-(";
throw;
}
m_state.db().commit(); // TODO: State API for this?
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot() << "=" << rootHash() << "=" << toHex(asBytes(db().lookup(rootHash())));
m_previousBlock = m_currentBlock;
m_currentBlock.populateFromParent(m_previousBlock);
clog(StateTrace) << "finalising enactment. current -> previous, hash is" << m_previousBlock.hash();
}
else
m_state.db().rollback(); // TODO: State API for this?
resetCurrent();
}
string Block::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
RLP rlp(_block);
cleanup(false);
BlockInfo bi(_block, (_ir & ImportRequirements::ValidSeal) ? CheckEverything : IgnoreSeal);
m_currentBlock = bi;
m_currentBlock.verifyInternals(_block);
m_currentBlock.noteDirty();
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number());
string ret;
unsigned i = 0;
for (auto const& tr: rlp[1])
{
StandardTrace st;
st.setShowMnemonics();
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, st.onOp());
ret += (ret.empty() ? "[" : ",") + st.json();
++i;
}
return ret.empty() ? "[]" : (ret + "]");
}
std::ostream& dev::eth::operator<<(std::ostream& _out, Block const& _s)
{
(void)_s;
return _out;
}

310
libethereum/Block.h

@ -0,0 +1,310 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Block.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <array>
#include <unordered_map>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/TrieDB.h>
#include <libdevcrypto/OverlayDB.h>
#include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h>
#include <libethcore/Miner.h>
#include <libevm/ExtVMFace.h>
#include "Account.h"
#include "Transaction.h"
#include "TransactionReceipt.h"
#include "AccountDiff.h"
#include "GasPricer.h"
#include "State.h"
namespace dev
{
namespace test { class ImportTest; class StateLoader; }
namespace eth
{
class BlockChain;
class State;
class TransactionQueue;
struct VerifiedBlockRef;
struct BlockChat: public LogChannel { static const char* name(); static const int verbosity = 4; };
struct BlockTrace: public LogChannel { static const char* name(); static const int verbosity = 5; };
struct BlockDetail: public LogChannel { static const char* name(); static const int verbosity = 14; };
struct BlockSafeExceptions: public LogChannel { static const char* name(); static const int verbosity = 21; };
struct PopulationStatistics
{
double verify;
double enact;
};
/**
* @brief Active model of a block within the block chain.
* Keeps track of all transactions, receipts and state for a particular block. Can apply all
* needed transforms of the state for rewards and contains logic for sealing the block.
*/
class Block
{
friend class ExtVM;
friend class dev::test::ImportTest;
friend class dev::test::StateLoader;
friend class Executive;
friend class BlockChain;
public:
/// Default constructor; creates with a blank database prepopulated with the genesis block.
Block(): m_state(OverlayDB(), BaseState::Empty) {}
/// Basic state object from database.
/// Use the default when you already have a database and you just want to make a Block object
/// which uses it. If you have no preexisting database then set BaseState to something other
/// than BaseState::PreExisting in order to prepopulate the Trie.
/// You can also set the beneficiary address.
explicit Block(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address());
/// Copy state object.
Block(Block const& _s);
/// Copy state object.
Block& operator=(Block const& _s);
/// Get the beneficiary address for any transactions we do and rewards we get.
Address beneficiary() const { return m_beneficiary; }
/// Set the beneficiary address for any transactions we do and rewards we get.
/// This causes a complete reset of current block.
void setBeneficiary(Address const& _id) { m_beneficiary = _id; resetCurrent(); }
// Account-getters. All operate on the final state.
/// Get an account's balance.
/// @returns 0 if the address has never been used.
u256 balance(Address const& _address) const { return m_state.balance(_address); }
/// Get the number of transactions a particular address has sent (used for the transaction nonce).
/// @returns 0 if the address has never been used.
u256 transactionsFrom(Address const& _address) const { return m_state.transactionsFrom(_address); }
/// Check if the address is in use.
bool addressInUse(Address const& _address) const { return m_state.addressInUse(_address); }
/// Check if the address contains executable code.
bool addressHasCode(Address const& _address) const { return m_state.addressHasCode(_address); }
/// Get the root of the storage of an account.
h256 storageRoot(Address const& _contract) const { return m_state.storageRoot(_contract); }
/// Get the value of a storage position of an account.
/// @returns 0 if no account exists at that address.
u256 storage(Address const& _contract, u256 const& _memory) const { return m_state.storage(_contract, _memory); }
/// Get the storage of an account.
/// @note This is expensive. Don't use it unless you need to.
/// @returns std::unordered_map<u256, u256> if no account exists at that address.
std::unordered_map<u256, u256> storage(Address const& _contract) const { return m_state.storage(_contract); }
/// Get the code of an account.
/// @returns bytes() if no account exists at that address.
bytes const& code(Address const& _contract) const { return m_state.code(_contract); }
/// Get the code hash of an account.
/// @returns EmptySHA3 if no account exists at that address or if there is no code associated with the address.
h256 codeHash(Address const& _contract) const { return m_state.codeHash(_contract); }
// General information from state
/// Get the backing state object.
State const& state() const { return m_state; }
/// Open a DB - useful for passing into the constructor & keeping for other states that are necessary.
OverlayDB const& db() const { return m_state.db(); }
/// The hash of the root of our state tree.
h256 rootHash() const { return m_state.rootHash(); }
/// @returns the set containing all addresses currently in use in Ethereum.
/// @throws InterfaceNotSupported if compiled without ETH_FATDB.
std::unordered_map<Address, u256> addresses() const { return m_state.addresses(); }
// For altering accounts behind-the-scenes
/// Get a mutable State object which is backing this block.
/// @warning Only use this is you know what you're doing. If you use it while constructing a
/// normal sealable block, don't expect things to work right.
State& mutableState() { return m_state; }
// Information concerning ongoing transactions
/// Get the remaining gas limit in this block.
u256 gasLimitRemaining() const { return m_currentBlock.gasLimit() - gasUsed(); }
/// Get the list of pending transactions.
Transactions const& pending() const { return m_transactions; }
/// Get the list of hashes of pending transactions.
h256Hash const& pendingHashes() const { return m_transactionSet; }
/// 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.
LogEntries const& log(unsigned _i) const { return m_receipts[_i].log(); }
/// 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.
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.
/// If (_i == pending().size()) returns the final state of the block, prior to rewards.
State fromPending(unsigned _i) const;
/// @returns the StateDiff caused by the pending transaction of index @a _i.
StateDiff pendingDiff(unsigned _i) const { return fromPending(_i).diff(fromPending(_i + 1), true); }
// State-change operations
/// Construct state object from arbitrary point in blockchain.
PopulationStatistics populateFromChain(BlockChain const& _bc, h256 const& _hash, ImportRequirements::value _ir = ImportRequirements::None);
/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.
ExecutionResult execute(LastHashes const& _lh, Transaction const& _t, Permanence _p = Permanence::Committed, OnOpFunc const& _onOp = OnOpFunc());
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
/// @returns a list of receipts one for each transaction placed from the queue into the state and bool, true iff there are more transactions to be processed.
std::pair<TransactionReceipts, bool> sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned _msTimeout = 100);
/// Sync our state with the block chain.
/// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue.
bool sync(BlockChain const& _bc);
/// Sync with the block chain, but rather than synching to the latest block, instead sync to the given block.
bool sync(BlockChain const& _bc, h256 const& _blockHash, BlockInfo const& _bi = BlockInfo());
/// Execute all transactions within a given block.
/// @returns the additional total difficulty.
u256 enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc);
/// Returns back to a pristine state after having done a playback.
/// @arg _fullCommit if true flush everything out to disk. If false, this effectively only validates
/// the block since all state changes are ultimately reversed.
void cleanup(bool _fullCommit);
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();
// Sealing
/// Prepares the current state for mining.
/// Commits all transactions into the trie, compiles uncles and transactions list, applies all
/// rewards and populates the current block header with the appropriate hashes.
/// The only thing left to do after this is to actually mine().
///
/// This may be called multiple times and without issue.
void commitToSeal(BlockChain const& _bc, bytes const& _extraData = {});
/// Pass in a solution to the proof-of-work.
/// @returns true iff we were previously committed to mining.
/// TODO: verify it prior to calling this.
/** Commit to DB and build the final block if the previous call to mine()'s result is completion.
* Typically looks like:
* @code
* while (notYetMined)
* {
* // lock
* commitToSeal(_blockChain); // will call uncommitToMine if a repeat.
* completeMine();
* // unlock
* @endcode
*/
bool sealBlock(bytes const& _header) { return sealBlock(&_header); }
bool sealBlock(bytesConstRef _header);
/// Get the complete current block, including valid nonce.
/// Only valid after mine() returns true.
bytes const& blockData() const { return m_currentBytes; }
/// Get the header information on the present block.
BlockInfo const& info() const { return m_currentBlock; }
private:
/// Undo the changes to the state for committing to mine.
void uncommitToMine();
/// Retrieve all information about a given address into the cache.
/// If _requireMemory is true, grab the full memory should it be a contract item.
/// If _forceCreate is true, then insert a default item into the cache, in the case it doesn't
/// exist in the DB.
void ensureCached(Address const& _a, bool _requireCode, bool _forceCreate) const;
/// Retrieve all information about a given address into a cache.
void ensureCached(std::unordered_map<Address, Account>& _cache, Address const& _a, bool _requireCode, bool _forceCreate) const;
/// Execute the given block, assuming it corresponds to m_currentBlock.
/// Throws on failure.
u256 enact(VerifiedBlockRef const& _block, BlockChain const& _bc);
/// Finalise the block, applying the earned rewards.
void applyRewards(std::vector<BlockInfo> const& _uncleBlockHeaders);
/// @returns gas used by transactions thus far executed.
u256 gasUsed() const { return m_receipts.size() ? m_receipts.back().gasUsed() : 0; }
/// Provide a standard VM trace for debugging purposes.
std::string vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir);
State m_state; ///< Our state tree, as an OverlayDB DB.
Transactions m_transactions; ///< The current list of transactions that we've included in the state.
TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts.
h256Hash m_transactionSet; ///< The set of transaction hashes that we've included in the state.
State m_precommit; ///< State at the point immediately prior to rewards.
BlockInfo m_previousBlock; ///< The previous block's information.
BlockInfo m_currentBlock; ///< The current block's information.
bytes m_currentBytes; ///< The current block.
bool m_committedToMine = false; ///< Have we committed to mine on the present m_currentBlock?
bytes m_currentTxs; ///< The RLP-encoded block of transactions.
bytes m_currentUncles; ///< The RLP-encoded block of uncles.
Address m_beneficiary; ///< Our address (i.e. the address to which fees go).
u256 m_blockReward;
friend std::ostream& operator<<(std::ostream& _out, Block const& _s);
};
std::ostream& operator<<(std::ostream& _out, Block const& _s);
}
}

29
libethereum/BlockChain.cpp

@ -40,6 +40,7 @@
#include <liblll/Compiler.h>
#include "GenesisInfo.h"
#include "State.h"
#include "Block.h"
#include "Utility.h"
#include "Defaults.h"
using namespace std;
@ -146,13 +147,13 @@ static const unsigned c_minCacheSize = 1024 * 1024 * 32;
#endif
BlockChain::BlockChain(bytes const& _genesisBlock, std::unordered_map<Address, Account> const& _genesisState, std::string const& _path):
BlockChain::BlockChain(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path):
m_dbPath(_path)
{
open(_genesisBlock, _genesisState, _path);
}
void BlockChain::open(bytes const& _genesisBlock, std::unordered_map<Address, Account> const& _genesisState, std::string const& _path)
void BlockChain::open(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path)
{
// initialise deathrow.
m_cacheUsage.resize(c_collectionQueueSize);
@ -215,7 +216,12 @@ unsigned BlockChain::openDatabase(std::string const& _path, WithExisting _we)
}
else
{
cwarn << "Database already open. You appear to have another instance of ethereum running. Bailing.";
cwarn <<
"Database " <<
(chainPath + "/blocks") <<
"or " <<
(extrasPath + "/extras") <<
"already open. You appear to have another instance of ethereum running. Bailing.";
BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen());
}
}
@ -295,7 +301,7 @@ void BlockChain::rebuild(std::string const& _path, std::function<void(unsigned,
ldb::DB::Open(o, extrasPath + "/extras", &m_extrasDB);
// Open a fresh state DB
State s = genesisState(State::openDB(path, m_genesisHash, WithExisting::Kill));
Block s = genesisBlock(State::openDB(path, m_genesisHash, WithExisting::Kill));
// Clear all memos ready for replay.
m_details.clear();
@ -554,7 +560,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
{
// Check transactions are valid and that they result in a state equivalent to our state_root.
// Get total difficulty increase and update state, checking it.
State s(_db);
Block s(_db);
auto tdIncrease = s.enactOn(_block, *this);
for (unsigned i = 0; i < s.pending().size(); ++i)
@ -676,7 +682,7 @@ ImportRoute BlockChain::import(VerifiedBlockRef const& _block, OverlayDB const&
h256s alteredBlooms;
{
LogBloom blockBloom = tbi.logBloom();
blockBloom.shiftBloom<3>(sha3(tbi.coinbaseAddress().ref()));
blockBloom.shiftBloom<3>(sha3(tbi.beneficiary().ref()));
// Pre-memoize everything we need before locking x_blocksBlooms
for (unsigned level = 0, index = (unsigned)tbi.number(); level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize)
@ -1266,13 +1272,12 @@ bytes BlockChain::headerData(h256 const& _hash) const
return BlockInfo::extractHeader(&m_blocks[_hash]).data().toBytes();
}
State BlockChain::genesisState(OverlayDB const& _db)
Block BlockChain::genesisBlock(OverlayDB const& _db)
{
State ret(_db, BaseState::Empty);
dev::eth::commit(m_genesisState, ret.m_state); // bit horrible. maybe consider a better way of constructing it?
ret.m_state.db()->commit(); // have to use this db() since it's the one that has been altered with the above commit.
ret.m_previousBlock = BlockInfo(&m_genesisBlock);
ret.resetCurrent();
Block ret(_db, BaseState::Empty);
dev::eth::commit(m_genesisState, ret.mutableState().m_state); // bit horrible. maybe consider a better way of constructing it?
ret.mutableState().db().commit(); // have to use this db() since it's the one that has been altered with the above commit.
ret.m_previousBlock = BlockInfo(m_genesisBlock);
return ret;
}

12
libethereum/BlockChain.h

@ -58,6 +58,7 @@ namespace eth
static const h256s NullH256s;
class State;
class Block;
struct AlreadyHaveBlock: virtual Exception {};
struct UnknownParent: virtual Exception {};
@ -89,7 +90,6 @@ enum {
};
using ProgressCallback = std::function<void(unsigned, unsigned)>;
using StateDefinition = std::unordered_map<Address, Account>;
class VersionChecker
{
@ -106,7 +106,7 @@ class BlockChain
public:
/// Doesn't open the database - if you want it open it's up to you to subclass this and open it
/// in the constructor there.
BlockChain(bytes const& _genesisBlock, StateDefinition const& _genesisState, std::string const& _path);
BlockChain(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path);
~BlockChain();
/// Reopen everything.
@ -286,7 +286,7 @@ public:
template <class T> void setOnBad(T const& _t) { m_onBad = _t; }
/// Get a pre-made genesis State object.
State genesisState(OverlayDB const& _db);
Block genesisBlock(OverlayDB const& _db);
/// Verify block and prepare it for enactment
virtual VerifiedBlockRef verifyBlock(bytesConstRef _block, std::function<void(Exception&)> const& _onBad, ImportRequirements::value _ir = ImportRequirements::OutOfOrderChecks) const = 0;
@ -296,7 +296,7 @@ protected:
/// Initialise everything and ready for openning the database.
// TODO: rename to init
void open(bytes const& _genesisBlock, std::unordered_map<Address, Account> const& _genesisState, std::string const& _path);
void open(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path);
/// Open the database.
// TODO: rename to open.
unsigned openDatabase(std::string const& _path, WithExisting _we);
@ -400,7 +400,7 @@ class FullBlockChain: public BlockChain
public:
using BlockHeader = typename Sealer::BlockHeader;
FullBlockChain(bytes const& _genesisBlock, StateDefinition const& _genesisState, std::string const& _path, WithExisting _we, ProgressCallback const& _pc = ProgressCallback()):
FullBlockChain(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path, WithExisting _we, ProgressCallback const& _pc = ProgressCallback()):
BlockChain(_genesisBlock, _genesisState, _path)
{
openDatabase(_path, _we, _pc);
@ -505,7 +505,7 @@ public:
protected:
/// Constructor for derived classes to use when they'll open the chain db afterwards.
FullBlockChain(bytes const& _genesisBlock, StateDefinition const& _genesisState, std::string const& _path):
FullBlockChain(bytes const& _genesisBlock, AccountMap const& _genesisState, std::string const& _path):
BlockChain(_genesisBlock, _genesisState, _path)
{}
};

29
libethereum/CanonBlockChain.cpp

@ -21,7 +21,6 @@
#include "CanonBlockChain.h"
#include <test/JsonSpiritHeaders.h>
#include <boost/filesystem.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
@ -30,6 +29,7 @@
#include <libethcore/BlockInfo.h>
#include <libethcore/Params.h>
#include <liblll/Compiler.h>
#include <test/JsonSpiritHeaders.h>
#include "GenesisInfo.h"
#include "State.h"
#include "Defaults.h"
@ -104,32 +104,11 @@ bytes CanonBlockChain<Ethash>::createGenesisBlock()
return block.out();
}
unordered_map<Address, Account> CanonBlockChain<Ethash>::createGenesisState()
AccountMap const& CanonBlockChain<Ethash>::createGenesisState()
{
static std::unordered_map<Address, Account> s_ret;
static AccountMap s_ret;
if (s_ret.empty())
{
js::mValue val;
js::read_string(s_genesisStateJSON.empty() ? c_network == Network::Frontier ? c_genesisInfoFrontier : c_genesisInfoOlympic : s_genesisStateJSON, val);
for (auto account: val.get_obj()["alloc"].get_obj())
{
u256 balance;
if (account.second.get_obj().count("wei"))
balance = u256(account.second.get_obj()["wei"].get_str());
else if (account.second.get_obj().count("balance"))
balance = u256(account.second.get_obj()["balance"].get_str());
else if (account.second.get_obj().count("finney"))
balance = u256(account.second.get_obj()["finney"].get_str()) * finney;
if (account.second.get_obj().count("code"))
{
s_ret[Address(fromHex(account.first))] = Account(balance, Account::ContractConception);
s_ret[Address(fromHex(account.first))].setCode(fromHex(account.second.get_obj()["code"].get_str()));
}
else
s_ret[Address(fromHex(account.first))] = Account(balance, Account::NormalCreation);
}
}
s_ret = jsonToAccountMap(s_genesisStateJSON.empty() ? c_network == Network::Frontier ? c_genesisInfoFrontier : c_genesisInfoOlympic : s_genesisStateJSON);
return s_ret;
}

4
libethereum/CanonBlockChain.h

@ -52,7 +52,7 @@ class CanonBlockChain: public FullBlockChain<Sealer>
public:
CanonBlockChain(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()): CanonBlockChain<Sealer>(std::string(), _we, _pc) {}
CanonBlockChain(std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()):
FullBlockChain<Sealer>(createGenesisBlock(), StateDefinition(), _path, _we, _pc)
FullBlockChain<Sealer>(createGenesisBlock(), AccountMap(), _path)
{
BlockChain::openDatabase(_path, _we, _pc);
}
@ -93,7 +93,7 @@ public:
/// @returns the genesis block as its RLP-encoded byte array.
/// @note This is slow as it's constructed anew each call. Consider genesis() instead.
static std::unordered_map<Address, Account> createGenesisState();
static AccountMap const& createGenesisState();
/// Alter all the genesis block's state by giving a JSON string with account details.
/// @warning Unless you're very careful, make sure you call this right at the start of the

85
libethereum/Client.cpp

@ -40,6 +40,7 @@
#include "Executive.h"
#include "EthereumHost.h"
#include "Utility.h"
#include "Block.h"
#include "TransactionQueue.h"
using namespace std;
@ -80,6 +81,11 @@ Client::Client(std::shared_ptr<GasPricer> _gp):
{
}
Client::~Client()
{
stopWorking();
}
void Client::init(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId)
{
// Cannot be opened until after blockchain is open, since BlockChain may upgrade the database.
@ -87,7 +93,7 @@ void Client::init(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _
// until after the construction.
m_stateDB = State::openDB(_dbPath, bc().genesisHash(), _forceAction);
// LAZY. TODO: move genesis state construction/commiting to stateDB openning and have this just take the root from the genesis block.
m_preMine = bc().genesisState(m_stateDB);
m_preMine = bc().genesisBlock(m_stateDB);
m_postMine = m_preMine;
m_bq.setChain(bc());
@ -114,9 +120,20 @@ void Client::init(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _
startWorking();
}
Client::~Client()
Block Client::asOf(h256 const& _block) const
{
stopWorking();
try
{
Block ret(m_stateDB);
ret.populateFromChain(bc(), _block);
return ret;
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return Block();
}
}
ImportResult Client::queueBlock(bytes const& _block, bool _isSafe)
@ -344,15 +361,15 @@ void Client::reopenChain(WithExisting _we)
WriteGuard l2(x_preMine);
WriteGuard l3(x_working);
m_preMine = State();
m_postMine = State();
m_working = State();
m_preMine = Block();
m_postMine = Block();
m_working = Block();
m_stateDB = OverlayDB();
bc().reopen(_we);
m_stateDB = State::openDB(Defaults::dbPath(), bc().genesisHash(), _we);
m_preMine = bc().genesisState(m_stateDB);
m_preMine = bc().genesisBlock(m_stateDB);
m_postMine = m_preMine;
}
@ -502,12 +519,12 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
ExecutionResult ret;
try
{
State temp;
Block temp;
clog(ClientDetail) << "Nonce at " << _dest << " pre:" << m_preMine.transactionsFrom(_dest) << " post:" << m_postMine.transactionsFrom(_dest);
DEV_READ_GUARDED(x_postMine)
temp = m_postMine;
temp.addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp, LastHashes(), 0);
temp.mutableState().addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp);
e.setResultRecipient(ret);
if (!e.call(_dest, _from, _value, _gasPrice, &_data, _gas))
e.go();
@ -615,14 +632,14 @@ void Client::resyncStateFromChain()
if (!isMajorSyncing())
{
bool preChanged = false;
State newPreMine;
Block newPreMine;
DEV_READ_GUARDED(x_preMine)
newPreMine = m_preMine;
// TODO: use m_postMine to avoid re-evaluating our own blocks.
preChanged = newPreMine.sync(bc());
if (preChanged || m_postMine.address() != m_preMine.address())
if (preChanged || m_postMine.beneficiary() != m_preMine.beneficiary())
{
if (isMining())
clog(ClientTrace) << "New block on chain.";
@ -653,7 +670,7 @@ void Client::resyncStateFromChain()
void Client::resetState()
{
State newPreMine;
Block newPreMine;
DEV_READ_GUARDED(x_preMine)
newPreMine = m_preMine;
@ -710,7 +727,7 @@ void Client::rejigMining()
{
clog(ClientTrace) << "Rejigging mining...";
DEV_WRITE_GUARDED(x_working)
m_working.commitToMine(bc(), m_extraData);
m_working.commitToSeal(bc(), m_extraData);
DEV_READ_GUARDED(x_working)
{
DEV_WRITE_GUARDED(x_postMine)
@ -814,56 +831,38 @@ void Client::checkWatchGarbage()
}
}
State Client::asOf(h256 const& _block) const
{
try
{
State ret(m_stateDB);
ret.populateFromChain(bc(), _block);
return ret;
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return State();
}
}
void Client::prepareForTransaction()
{
startWorking();
}
State Client::state(unsigned _txi, h256 _block) const
Block Client::block(h256 const& _blockHash, PopulationStatistics* o_stats) const
{
try
{
State ret(m_stateDB);
ret.populateFromChain(bc(), _block);
return ret.fromPending(_txi);
Block ret(m_stateDB);
PopulationStatistics s = ret.populateFromChain(bc(), _blockHash);
if (o_stats)
swap(s, *o_stats);
return ret;
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
ex << errinfo_block(bc().block(_blockHash));
onBadBlock(ex);
return State();
return Block();
}
}
State Client::state(h256 const& _block, PopulationStatistics* o_stats) const
State Client::state(unsigned _txi, h256 const& _blockHash) const
{
try
{
State ret(m_stateDB);
PopulationStatistics s = ret.populateFromChain(bc(), _block);
if (o_stats)
swap(s, *o_stats);
return ret;
return block(_blockHash).fromPending(_txi);
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
ex << errinfo_block(bc().block(_blockHash));
onBadBlock(ex);
return State();
}

27
libethereum/Client.h

@ -40,7 +40,7 @@
#include <libethcore/ABI.h>
#include <libp2p/Common.h>
#include "CanonBlockChain.h"
#include "State.h"
#include "Block.h"
#include "CommonNet.h"
#include "ClientBase.h"
@ -100,12 +100,17 @@ public:
virtual u256 gasLimitRemaining() const override { return m_postMine.gasLimitRemaining(); }
// [PRIVATE API - only relevant for base clients, not available in general]
dev::eth::State state(unsigned _txi, h256 _block) const;
dev::eth::State state(h256 const& _block, PopulationStatistics* o_stats = nullptr) const;
/// Get the block.
dev::eth::Block block(h256 const& _blockHash, PopulationStatistics* o_stats = nullptr) const;
/// Get the state of the given block part way through execution, immediately before transaction
/// index @a _txi.
dev::eth::State state(unsigned _txi, h256 const& _block) const;
/// Get the state of the currently pending block part way through execution, immediately before
/// transaction index @a _txi.
dev::eth::State state(unsigned _txi) const;
/// Get the object representing the current state of Ethereum.
dev::eth::State postState() const { ReadGuard l(x_postMine); return m_postMine; }
dev::eth::Block postState() const { ReadGuard l(x_postMine); return m_postMine; }
/// Get the object representing the current canonical blockchain.
BlockChain const& blockChain() const { return bc(); }
/// Get some information on the block queue.
@ -122,7 +127,7 @@ public:
// Mining stuff:
virtual void setAddress(Address _us) override { WriteGuard l(x_preMine); m_preMine.setAddress(_us); }
virtual void setBeneficiary(Address _us) override { WriteGuard l(x_preMine); m_preMine.setBeneficiary(_us); }
/// Check block validity prior to mining.
bool miningParanoia() const { return m_paranoia; }
@ -212,9 +217,9 @@ protected:
/// Returns the state object for the full block (i.e. the terminal state) for index _h.
/// Works properly with LatestBlock and PendingBlock.
using ClientBase::asOf;
virtual State asOf(h256 const& _block) const override;
virtual State preMine() const override { ReadGuard l(x_preMine); return m_preMine; }
virtual State postMine() const override { ReadGuard l(x_postMine); return m_postMine; }
virtual Block asOf(h256 const& _block) const override;
virtual Block preMine() const override { ReadGuard l(x_preMine); return m_preMine; }
virtual Block postMine() const override { ReadGuard l(x_postMine); return m_postMine; }
virtual void prepareForTransaction() override;
/// Collate the changed filters for the bloom filter of the given pending transaction.
@ -295,11 +300,11 @@ protected:
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
mutable SharedMutex x_preMine; ///< Lock on m_preMine.
State m_preMine; ///< The present state of the client.
Block m_preMine; ///< The present state of the client.
mutable SharedMutex x_postMine; ///< Lock on m_postMine.
State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
Block m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added).
mutable SharedMutex x_working; ///< Lock on m_working.
State m_working; ///< The state of the client which we're mining (i.e. it'll have all the rewards added), while we're actually working on it.
Block m_working; ///< The state of the client which we're mining (i.e. it'll have all the rewards added), while we're actually working on it.
BlockInfo m_miningInfo; ///< The header we're attempting to mine on (derived from m_postMine).
bool remoteActive() const; ///< Is there an active and valid remote worker?
bool m_remoteWorking = false; ///< Has the remote worker recently been reset?

23
libethereum/ClientBase.cpp

@ -36,7 +36,7 @@ const char* WorkInChannel::name() { return EthOrange "⚒" EthGreen "▬▶"; }
const char* WorkOutChannel::name() { return EthOrange "" EthNavy "◀▬"; }
const char* WorkChannel::name() { return EthOrange "" EthWhite " "; }
State ClientBase::asOf(BlockNumber _h) const
Block ClientBase::asOf(BlockNumber _h) const
{
if (_h == PendingBlock)
return postMine();
@ -68,12 +68,12 @@ ExecutionResult ClientBase::call(Address const& _from, u256 _value, Address _des
ExecutionResult ret;
try
{
State temp = asOf(_blockNumber);
Block temp = asOf(_blockNumber);
u256 n = temp.transactionsFrom(_from);
Transaction t(_value, _gasPrice, _gas, _dest, _data, n);
t.forceSender(_from);
if (_ff == FudgeFactor::Lenient)
temp.addBalance(_from, (u256)(t.gas() * t.gasPrice() + t.value()));
temp.mutableState().addBalance(_from, (u256)(t.gas() * t.gasPrice() + t.value()));
ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted);
}
catch (...)
@ -88,14 +88,13 @@ ExecutionResult ClientBase::create(Address const& _from, u256 _value, bytes cons
ExecutionResult ret;
try
{
State temp = asOf(_blockNumber);
Block temp = asOf(_blockNumber);
u256 n = temp.transactionsFrom(_from);
// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret));
Transaction t(_value, _gasPrice, _gas, _data, n);
t.forceSender(_from);
if (_ff == FudgeFactor::Lenient)
temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
temp.mutableState().addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
ret = temp.execute(bc().lastHashes(), t, Permanence::Reverted);
}
catch (...)
@ -165,7 +164,7 @@ LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const
// Handle pending transactions differently as they're not on the block chain.
if (begin > bc().number())
{
State temp = postMine();
Block temp = postMine();
for (unsigned i = 0; i < temp.pending().size(); ++i)
{
// Might have a transaction that contains a matching log.
@ -440,14 +439,14 @@ h256s ClientBase::pendingHashes() const
StateDiff ClientBase::diff(unsigned _txi, h256 _block) const
{
State st = asOf(_block);
return st.fromPending(_txi).diff(st.fromPending(_txi + 1), true);
Block b = asOf(_block);
return b.fromPending(_txi).diff(b.fromPending(_txi + 1), true);
}
StateDiff ClientBase::diff(unsigned _txi, BlockNumber _block) const
{
State st = asOf(_block);
return st.fromPending(_txi).diff(st.fromPending(_txi + 1), true);
Block b = asOf(_block);
return b.fromPending(_txi).diff(b.fromPending(_txi + 1), true);
}
Addresses ClientBase::addresses(BlockNumber _block) const
@ -465,7 +464,7 @@ u256 ClientBase::gasLimitRemaining() const
Address ClientBase::address() const
{
return preMine().address();
return preMine().beneficiary();
}
h256 ClientBase::hashFromNumber(BlockNumber _number) const

15
libethereum/ClientBase.h

@ -26,10 +26,13 @@
#include "Interface.h"
#include "LogFilter.h"
#include "TransactionQueue.h"
#include "Block.h"
namespace dev {
namespace dev
{
namespace eth {
namespace eth
{
struct InstalledFilter
{
@ -163,16 +166,16 @@ public:
virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::hashrate")); }
virtual WorkingProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::miningProgress")); }
State asOf(BlockNumber _h) const;
Block asOf(BlockNumber _h) const;
protected:
/// The interface that must be implemented in any class deriving this.
/// {
virtual BlockChain& bc() = 0;
virtual BlockChain const& bc() const = 0;
virtual State asOf(h256 const& _h) const = 0;
virtual State preMine() const = 0;
virtual State postMine() const = 0;
virtual Block asOf(h256 const& _h) const = 0;
virtual Block preMine() const = 0;
virtual Block postMine() const = 0;
virtual void prepareForTransaction() = 0;
/// }

37
libethereum/Executive.cpp

@ -30,6 +30,7 @@
#include "ExtVM.h"
#include "Precompiled.h"
#include "BlockChain.h"
#include "Block.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -142,9 +143,27 @@ string StandardTrace::json(bool _styled) const
return _styled ? Json::StyledWriter().write(*m_trace) : Json::FastWriter().write(*m_trace);
}
Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
Executive::Executive(Block& _s, BlockChain const& _bc, unsigned _level):
Executive(_s, _bc.lastHashes(unsigned(_s.info().number() - 1)), _level)
{}
Executive::Executive(Block& _s, LastHashes const& _lh, unsigned _level):
m_s(_s.mutableState()),
m_envInfo(_s.info(), _lh),
m_depth(_level)
{}
Executive::Executive(State& _s, BlockChain const& _bc, EnvInfo const& _envInfo, unsigned _level):
m_s(_s),
m_envInfo(_envInfo),
m_depth(_level)
{
m_envInfo.setLastHashes(_bc.lastHashes((unsigned)m_envInfo.number() - 1));
}
Executive::Executive(State& _s, BlockChain const& _bc, unsigned _number, unsigned _level):
m_s(_s),
m_lastHashes(_bc.lastHashes((unsigned)_s.info().number() - 1)),
m_envInfo(_bc.info(_bc.numberHash(_number)), _bc.lastHashes(_number - 1)),
m_depth(_level)
{}
@ -169,12 +188,12 @@ void Executive::initialize(Transaction const& _transaction)
m_t = _transaction;
// Avoid transactions that would take us beyond the block gas limit.
u256 startGasUsed = m_s.gasUsed();
if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit())
u256 startGasUsed = m_envInfo.gasUsed();
if (startGasUsed + (bigint)m_t.gas() > m_envInfo.gasLimit())
{
clog(ExecutiveWarnChannel) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit() - startGasUsed) << " Got" << m_t.gas();
clog(ExecutiveWarnChannel) << "Too much gas used in this block: Require <" << (m_envInfo.gasLimit() - startGasUsed) << " Got" << m_t.gas();
m_excepted = TransactionException::BlockGasLimitReached;
BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit() - startGasUsed), (bigint)m_t.gas()));
BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_envInfo.gasLimit() - startGasUsed), (bigint)m_t.gas()));
}
// Check gas cost is enough.
@ -265,7 +284,7 @@ bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address co
m_outRef = _p.out; // Save ref to expected output buffer to be used in go()
bytes const& c = m_s.code(_p.codeAddress);
h256 codeHash = m_s.codeHash(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, codeHash, m_depth);
m_ext = make_shared<ExtVM>(m_s, m_envInfo, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, codeHash, m_depth);
}
}
@ -285,7 +304,7 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
// Execute _init.
if (!_init.empty())
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, sha3(_init), m_depth);
m_ext = make_shared<ExtVM>(m_s, m_envInfo, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, sha3(_init), m_depth);
m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception);
m_s.transferBalance(_sender, m_newAddress, _endowment);
@ -406,7 +425,7 @@ void Executive::finalize()
m_s.addBalance(m_t.sender(), m_gas * m_t.gasPrice());
u256 feesEarned = (m_t.gas() - m_gas) * m_t.gasPrice();
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress(), feesEarned);
m_s.addBalance(m_envInfo.beneficiary(), feesEarned);
}
// Suicides...

39
libethereum/Executive.h

@ -32,10 +32,14 @@ namespace Json
namespace dev
{
class OverlayDB;
namespace eth
{
class State;
class Block;
class BlockChain;
class ExtVM;
struct Manifest;
@ -84,10 +88,32 @@ private:
class Executive
{
public:
/// Basic constructor.
Executive(State& _s, LastHashes const& _lh, unsigned _level = 0): m_s(_s), m_lastHashes(_lh), m_depth(_level) {}
/// Basic constructor.
Executive(State& _s, BlockChain const& _bc, unsigned _level = 0);
/// Simple constructor; executive will operate on given state, with the given environment info.
Executive(State& _s, EnvInfo const& _envInfo, unsigned _level = 0): m_s(_s), m_envInfo(_envInfo), m_depth(_level) {}
/** Easiest constructor.
* Creates executive to operate on the state of end of the given block, populating environment
* info from given Block and the LastHashes portion from the BlockChain.
*/
Executive(Block& _s, BlockChain const& _bc, unsigned _level = 0);
/** LastHashes-split constructor.
* Creates executive to operate on the state of end of the given block, populating environment
* info accordingly, with last hashes given explicitly.
*/
Executive(Block& _s, LastHashes const& _lh = LastHashes(), unsigned _level = 0);
/** Partially-automated split constructor; executive will operate on given state, with the given
* environment info, populating the last hashes from the given chain.
*/
Executive(State& _s, BlockChain const& _bc, EnvInfo const& _envInfo, unsigned _level = 0);
/**
* Automated split constructor; executive will operate on given state, with the environment info
* populated from the given chain.
* @note This will only work when the state to be operated on is already in the chain.
*/
Executive(State& _s, BlockChain const& _bc, unsigned _number, unsigned _level = 0);
Executive(Executive const&) = delete;
void operator=(Executive) = delete;
@ -147,8 +173,9 @@ public:
private:
State& m_s; ///< The state to which this operation/transaction is applied.
LastHashes m_lastHashes;
std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required. shared_ptr used only to allow ExtVM forward reference.
// TODO: consider changign to EnvInfo const& to avoid LastHashes copy at every CALL/CREATE
EnvInfo m_envInfo; ///< Information on the runtime environment.
std::shared_ptr<ExtVM> m_ext; ///< The VM externality object for the VM execution or null if no VM is required. shared_ptr used only to allow ExtVM forward reference. This field does *NOT* survive this object.
bytesRef m_outRef; ///< Reference to "expected output" buffer.
ExecutionResult* m_res = nullptr; ///< Optional storage for execution results.
Address m_newAddress; ///< The address of the created contract in the case of create() being called.

10
libethereum/ExtVM.cpp

@ -88,14 +88,14 @@ void go(unsigned _depth, Executive& _e, OnOpFunc const& _onOp)
bool ExtVM::call(CallParameters& _p)
{
Executive e(m_s, lastHashes, depth + 1);
Executive e(m_s, envInfo(), depth + 1);
if (!e.call(_p, gasPrice, origin))
{
#if __clang__ // Enabled for clang only as the problem affects OSX
#if __clang__ // Enabled for clang only as the problem affects OSX
go(depth, e, _p.onOp);
#else
#else
e.go(_p.onOp);
#endif
#endif
e.accrueSubState(sub);
}
_p.gas = e.gas();
@ -108,7 +108,7 @@ h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
Executive e(m_s, lastHashes, depth + 1);
Executive e(m_s, envInfo(), depth + 1);
if (!e.create(myAddress, _endowment, gasPrice, io_gas, _code, origin))
{
go(depth, e, _onOp);

4
libethereum/ExtVM.h

@ -39,8 +39,8 @@ class ExtVM: public ExtVMFace
{
public:
/// Full constructor.
ExtVM(State& _s, LastHashes const& _lh, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, h256 const& _codeHash, unsigned _depth = 0):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _codeHash, _s.m_previousBlock, _s.m_currentBlock, _lh, _depth), m_s(_s), m_origCache(_s.m_cache)
ExtVM(State& _s, EnvInfo const& _envInfo, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, h256 const& _codeHash, unsigned _depth = 0):
ExtVMFace(_envInfo, _myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _codeHash, _depth), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
}

6
libethereum/GasPricer.h

@ -28,7 +28,7 @@ namespace dev
namespace eth
{
class State;
class Block;
class BlockChain;
enum class TransactionPriority
@ -48,7 +48,7 @@ public:
GasPricer() = default;
virtual ~GasPricer() = default;
virtual u256 ask(State const&) const = 0;
virtual u256 ask(Block const&) const = 0;
virtual u256 bid(TransactionPriority _p = TransactionPriority::Medium) const = 0;
virtual void update(BlockChain const&) {}
@ -64,7 +64,7 @@ public:
void setBid(u256 const& _bid) { m_bid = _bid; }
u256 ask() const { return m_ask; }
u256 ask(State const&) const override { return m_ask; }
u256 ask(Block const&) const override { return m_ask; }
u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return m_bid; }
private:

2
libethereum/Interface.h

@ -194,7 +194,7 @@ public:
// [MINING API]:
/// Set the coinbase address.
virtual void setAddress(Address _us) = 0;
virtual void setBeneficiary(Address _us) = 0;
/// Get the coinbase address.
virtual Address address() const = 0;

4
libethereum/LogFilter.cpp

@ -22,7 +22,7 @@
#include "LogFilter.h"
#include <libdevcore/SHA3.h>
#include "State.h"
#include "Block.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -80,7 +80,7 @@ bool LogFilter::matches(LogBloom _bloom) const
return true;
}
bool LogFilter::matches(State const& _s, unsigned _i) const
bool LogFilter::matches(Block const& _s, unsigned _i) const
{
return matches(_s.receipt(_i)).size() > 0;
}

3
libethereum/LogFilter.h

@ -41,6 +41,7 @@ namespace eth
std::ostream& operator<<(std::ostream& _out, dev::eth::LogFilter const& _s);
class State;
class Block;
class LogFilter
{
@ -65,7 +66,7 @@ public:
std::vector<LogBloom> bloomPossibilities() const;
bool matches(LogBloom _bloom) const;
bool matches(State const& _s, unsigned _i) const;
bool matches(Block const& _b, unsigned _i) const;
LogEntries matches(TransactionReceipt const& _r) const;
LogFilter address(Address _a) { m_addresses.insert(_a); return *this; }

799
libethereum/State.cpp

@ -54,6 +54,25 @@ const char* StateDetail::name() { return EthViolet "⚙" EthWhite " ◌"; }
const char* StateTrace::name() { return EthViolet "" EthGray ""; }
const char* StateChat::name() { return EthViolet "" EthWhite ""; }
State::State(OverlayDB const& _db, BaseState _bs):
m_db(_db),
m_state(&m_db)
{
if (_bs != BaseState::PreExisting)
// Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly.
m_state.init();
paranoia("end of normal construction.", true);
}
State::State(State const& _s):
m_db(_s.m_db),
m_state(&m_db, _s.m_state.root(), Verification::Skip),
m_cache(_s.m_cache),
m_touched(_s.m_touched)
{
paranoia("after state cloning (copy cons).", true);
}
OverlayDB State::openDB(std::string const& _basePath, h256 const& _genesisHash, WithExisting _we)
{
std::string path = _basePath.empty() ? Defaults::get()->m_dbPath : _basePath;
@ -83,7 +102,10 @@ OverlayDB State::openDB(std::string const& _basePath, h256 const& _genesisHash,
else
{
cwarn << status.ToString();
cwarn << "Database already open. You appear to have another instance of ethereum running. Bailing.";
cwarn <<
"Database " <<
(path + "/state") <<
"already open. You appear to have another instance of ethereum running. Bailing.";
BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen());
}
}
@ -92,80 +114,10 @@ OverlayDB State::openDB(std::string const& _basePath, h256 const& _genesisHash,
return OverlayDB(db);
}
State::State(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress):
m_db(_db),
m_state(&m_db),
m_ourAddress(_coinbaseAddress),
m_blockReward(c_blockReward)
{
if (_bs != BaseState::PreExisting)
// Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly.
m_state.init();
paranoia("beginning of Genesis construction.", true);
m_previousBlock.clear();
m_currentBlock.clear();
// assert(m_state.root() == m_previousBlock.stateRoot());
paranoia("end of normal construction.", true);
}
PopulationStatistics State::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir)
{
PopulationStatistics ret { 0.0, 0.0 };
if (!_bc.isKnown(_h))
{
// Might be worth throwing here.
cwarn << "Invalid block given for state population: " << _h;
BOOST_THROW_EXCEPTION(BlockNotFound() << errinfo_target(_h));
}
auto b = _bc.block(_h);
BlockInfo bi(b);
if (bi.number())
{
// Non-genesis:
// 1. Start at parent's end state (state root).
BlockInfo bip(_bc.block(bi.parentHash()));
sync(_bc, bi.parentHash(), bip);
// 2. Enact the block's transactions onto this state.
m_ourAddress = bi.coinbaseAddress();
Timer t;
auto vb = _bc.verifyBlock(&b, function<void(Exception&)>(), _ir | ImportRequirements::TransactionBasic);
ret.verify = t.elapsed();
t.restart();
enact(vb, _bc);
ret.enact = t.elapsed();
}
else
{
// Genesis required:
// We know there are no transactions, so just populate directly.
m_state.init();
sync(_bc, _h, bi);
}
return ret;
}
State::State(State const& _s):
m_db(_s.m_db),
m_state(&m_db, _s.m_state.root(), Verification::Skip),
m_transactions(_s.m_transactions),
m_receipts(_s.m_receipts),
m_transactionSet(_s.m_transactionSet),
m_touched(_s.m_touched),
m_cache(_s.m_cache),
m_previousBlock(_s.m_previousBlock),
m_currentBlock(_s.m_currentBlock),
m_ourAddress(_s.m_ourAddress),
m_blockReward(_s.m_blockReward)
void State::populateFrom(AccountMap const& _map)
{
paranoia("after state cloning (copy cons).", true);
eth::commit(_map, m_state);
commit();
}
void State::paranoia(std::string const& _when, bool _enforceRefs) const
@ -188,18 +140,9 @@ State& State::operator=(State const& _s)
{
m_db = _s.m_db;
m_state.open(&m_db, _s.m_state.root(), Verification::Skip);
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;
m_currentBlock = _s.m_currentBlock;
m_ourAddress = _s.m_ourAddress;
m_blockReward = _s.m_blockReward;
m_lastTx = _s.m_lastTx;
m_touched = _s.m_touched;
paranoia("after state cloning (assignment op)", true);
m_committedToMine = false;
return *this;
}
@ -280,159 +223,6 @@ void State::commit()
m_cache.clear();
}
bool State::sync(BlockChain const& _bc)
{
return sync(_bc, _bc.currentHash());
}
bool State::sync(BlockChain const& _bc, h256 const& _block, BlockInfo const& _bi)
{
bool ret = false;
// BLOCK
BlockInfo bi = _bi ? _bi : _bc.info(_block);
#if ETH_PARANOIA
if (!bi)
while (1)
{
try
{
auto b = _bc.block(_block);
bi.populate(b);
break;
}
catch (Exception const& _e)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << diagnostic_information(_e) << endl;
}
catch (std::exception const& _e)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << _e.what() << endl;
}
}
#endif
if (bi == m_currentBlock)
{
// We mined the last block.
// Our state is good - we just need to move on to next.
m_previousBlock = m_currentBlock;
resetCurrent();
ret = true;
}
else if (bi == m_previousBlock)
{
// No change since last sync.
// Carry on as we were.
}
else
{
// New blocks available, or we've switched to a different branch. All change.
// Find most recent state dump and replay what's left.
// (Most recent state dump might end up being genesis.)
if (m_db.lookup(bi.stateRoot()).empty())
{
cwarn << "Unable to sync to" << bi.hash() << "; state root" << bi.stateRoot() << "not found in database.";
cwarn << "Database corrupt: contains block without stateRoot:" << bi;
cwarn << "Try rescuing the database by running: eth --rescue";
BOOST_THROW_EXCEPTION(InvalidStateRoot() << errinfo_target(bi.stateRoot()));
}
m_previousBlock = bi;
resetCurrent();
ret = true;
}
#if ALLOW_REBUILD
else
{
// New blocks available, or we've switched to a different branch. All change.
// Find most recent state dump and replay what's left.
// (Most recent state dump might end up being genesis.)
std::vector<h256> chain;
while (bi.number() != 0 && m_db.lookup(bi.stateRoot()).empty()) // while we don't have the state root of the latest block...
{
chain.push_back(bi.hash()); // push back for later replay.
bi.populate(_bc.block(bi.parentHash())); // move to parent.
}
m_previousBlock = bi;
resetCurrent();
// Iterate through in reverse, playing back each of the blocks.
try
{
for (auto it = chain.rbegin(); it != chain.rend(); ++it)
{
auto b = _bc.block(*it);
enact(&b, _bc, _ir);
cleanup(true);
}
}
catch (...)
{
// TODO: Slightly nicer handling? :-)
cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl;
cerr << boost::current_exception_diagnostic_information() << endl;
exit(1);
}
resetCurrent();
ret = true;
}
#endif
return ret;
}
u256 State::enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc)
{
#if ETH_TIMED_ENACTMENTS
Timer t;
double populateVerify;
double populateGrand;
double syncReset;
double enactment;
#endif
// Check family:
BlockInfo biParent = _bc.info(_block.info.parentHash());
_block.info.verifyParent(biParent);
#if ETH_TIMED_ENACTMENTS
populateVerify = t.elapsed();
t.restart();
#endif
BlockInfo biGrandParent;
if (biParent.number())
biGrandParent = _bc.info(biParent.parentHash());
#if ETH_TIMED_ENACTMENTS
populateGrand = t.elapsed();
t.restart();
#endif
sync(_bc, _block.info.parentHash(), BlockInfo());
resetCurrent();
#if ETH_TIMED_ENACTMENTS
syncReset = t.elapsed();
t.restart();
#endif
m_previousBlock = biParent;
auto ret = enact(_block, _bc);
#if ETH_TIMED_ENACTMENTS
enactment = t.elapsed();
if (populateVerify + populateGrand + syncReset + enactment > 0.5)
clog(StateChat) << "popVer/popGrand/syncReset/enactment = " << populateVerify << "/" << populateGrand << "/" << syncReset << "/" << enactment;
#endif
return ret;
}
unordered_map<Address, u256> State::addresses() const
{
#if ETH_FATDB
@ -449,496 +239,14 @@ unordered_map<Address, u256> State::addresses() const
#endif
}
void State::resetCurrent()
void State::setRoot(h256 const& _r)
{
m_transactions.clear();
m_receipts.clear();
m_transactionSet.clear();
m_cache.clear();
m_touched.clear();
m_currentBlock = BlockInfo();
m_currentBlock.setCoinbaseAddress(m_ourAddress);
m_currentBlock.setTimestamp(max(m_previousBlock.timestamp() + 1, (u256)time(0)));
m_currentBlock.populateFromParent(m_previousBlock);
// TODO: check.
m_lastTx = m_db;
m_state.setRoot(m_previousBlock.stateRoot());
m_committedToMine = false;
m_state.setRoot(_r);
paranoia("begin resetCurrent", true);
}
pair<TransactionReceipts, bool> State::sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned msTimeout)
{
// TRANSACTIONS
pair<TransactionReceipts, bool> ret;
ret.second = false;
auto ts = _tq.topTransactions(c_maxSyncTransactions);
LastHashes lh;
auto deadline = chrono::steady_clock::now() + chrono::milliseconds(msTimeout);
for (int goodTxs = 1; goodTxs; )
{
goodTxs = 0;
for (auto const& t: ts)
if (!m_transactionSet.count(t.sha3()))
{
try
{
if (t.gasPrice() >= _gp.ask(*this))
{
// Timer t;
if (lh.empty())
lh = _bc.lastHashes();
execute(lh, t);
ret.first.push_back(m_receipts.back());
++goodTxs;
// cnote << "TX took:" << t.elapsed() * 1000;
}
else if (t.gasPrice() < _gp.ask(*this) * 9 / 10)
{
clog(StateTrace) << t.sha3() << "Dropping El Cheapo transaction (<90% of ask price)";
_tq.drop(t.sha3());
}
}
catch (InvalidNonce const& in)
{
bigint const& req = *boost::get_error_info<errinfo_required>(in);
bigint const& got = *boost::get_error_info<errinfo_got>(in);
if (req > got)
{
// too old
clog(StateTrace) << t.sha3() << "Dropping old transaction (nonce too low)";
_tq.drop(t.sha3());
}
else if (got > req + _tq.waiting(t.sender()))
{
// too new
clog(StateTrace) << t.sha3() << "Dropping new transaction (too many nonces ahead)";
_tq.drop(t.sha3());
}
else
_tq.setFuture(t.sha3());
}
catch (BlockGasLimitReached const& e)
{
bigint const& got = *boost::get_error_info<errinfo_got>(e);
if (got > m_currentBlock.gasLimit())
{
clog(StateTrace) << t.sha3() << "Dropping over-gassy transaction (gas > block's gas limit)";
_tq.drop(t.sha3());
}
else
{
// Temporarily no gas left in current block.
// OPTIMISE: could note this and then we don't evaluate until a block that does have the gas left.
// for now, just leave alone.
}
}
catch (Exception const& _e)
{
// Something else went wrong - drop it.
clog(StateTrace) << t.sha3() << "Dropping invalid transaction:" << diagnostic_information(_e);
_tq.drop(t.sha3());
}
catch (std::exception const&)
{
// Something else went wrong - drop it.
_tq.drop(t.sha3());
cwarn << t.sha3() << "Transaction caused low-level exception :(";
}
}
if (chrono::steady_clock::now() > deadline)
{
ret.second = true;
break;
}
}
return ret;
}
string State::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir)
{
RLP rlp(_block);
cleanup(false);
BlockInfo bi(_block, (_ir & ImportRequirements::ValidSeal) ? CheckEverything : IgnoreSeal);
m_currentBlock = bi;
m_currentBlock.verifyInternals(_block);
m_currentBlock.noteDirty();
LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number());
string ret;
unsigned i = 0;
for (auto const& tr: rlp[1])
{
StandardTrace st;
st.setShowMnemonics();
execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, st.onOp());
ret += (ret.empty() ? "[" : ",") + st.json();
++i;
}
return ret.empty() ? "[]" : (ret + "]");
}
u256 State::enact(VerifiedBlockRef const& _block, BlockChain const& _bc)
{
DEV_TIMED_FUNCTION_ABOVE(500);
// m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE
assert(m_previousBlock.hash() == _block.info.parentHash());
assert(m_currentBlock.parentHash() == _block.info.parentHash());
assert(rootHash() == m_previousBlock.stateRoot());
#endif
if (m_currentBlock.parentHash() != m_previousBlock.hash())
// Internal client error.
BOOST_THROW_EXCEPTION(InvalidParentHash());
// Populate m_currentBlock with the correct values.
m_currentBlock.noteDirty();
m_currentBlock = _block.info;
// cnote << "playback begins:" << m_state.root();
// cnote << m_state;
LastHashes lh;
DEV_TIMED_ABOVE("lastHashes", 500)
lh = _bc.lastHashes((unsigned)m_previousBlock.number());
RLP rlp(_block.block);
vector<bytes> receipts;
// All ok with the block generally. Play back the transactions now...
unsigned i = 0;
DEV_TIMED_ABOVE("txExec", 500)
for (auto const& tr: _block.transactions)
{
try
{
LogOverride<ExecutiveWarnChannel> o(false);
execute(lh, tr);
}
catch (Exception& ex)
{
ex << errinfo_transactionIndex(i);
throw;
}
RLPStream receiptRLP;
m_receipts.back().streamRLP(receiptRLP);
receipts.push_back(receiptRLP.out());
++i;
}
h256 receiptsRoot;
DEV_TIMED_ABOVE(".receiptsRoot()", 500)
receiptsRoot = orderedTrieRoot(receipts);
if (receiptsRoot != m_currentBlock.receiptsRoot())
{
InvalidReceiptsStateRoot ex;
ex << Hash256RequirementError(receiptsRoot, m_currentBlock.receiptsRoot());
ex << errinfo_receipts(receipts);
// ex << errinfo_vmtrace(vmTrace(_block.block, _bc, ImportRequirements::None));
BOOST_THROW_EXCEPTION(ex);
}
if (m_currentBlock.logBloom() != logBloom())
{
InvalidLogBloom ex;
ex << LogBloomRequirementError(logBloom(), m_currentBlock.logBloom());
ex << errinfo_receipts(receipts);
BOOST_THROW_EXCEPTION(ex);
}
// Initialise total difficulty calculation.
u256 tdIncrease = m_currentBlock.difficulty();
// Check uncles & apply their rewards to state.
if (rlp[2].itemCount() > 2)
{
TooManyUncles ex;
ex << errinfo_max(2);
ex << errinfo_got(rlp[2].itemCount());
BOOST_THROW_EXCEPTION(ex);
}
vector<BlockInfo> rewarded;
h256Hash excluded;
DEV_TIMED_ABOVE("allKin", 500)
excluded = _bc.allKinFrom(m_currentBlock.parentHash(), 6);
excluded.insert(m_currentBlock.hash());
unsigned ii = 0;
DEV_TIMED_ABOVE("uncleCheck", 500)
for (auto const& i: rlp[2])
{
try
{
auto h = sha3(i.data());
if (excluded.count(h))
{
UncleInChain ex;
ex << errinfo_comment("Uncle in block already mentioned");
ex << errinfo_unclesExcluded(excluded);
ex << errinfo_hash256(sha3(i.data()));
BOOST_THROW_EXCEPTION(ex);
}
excluded.insert(h);
// IgnoreSeal since it's a VerifiedBlock.
BlockInfo uncle(i.data(), IgnoreSeal, h, HeaderData);
BlockInfo uncleParent;
if (!_bc.isKnown(uncle.parentHash()))
BOOST_THROW_EXCEPTION(UnknownParent());
uncleParent = BlockInfo(_bc.block(uncle.parentHash()));
if ((bigint)uncleParent.number() < (bigint)m_currentBlock.number() - 7)
{
UncleTooOld ex;
ex << errinfo_uncleNumber(uncle.number());
ex << errinfo_currentNumber(m_currentBlock.number());
BOOST_THROW_EXCEPTION(ex);
}
else if (uncle.number() == m_currentBlock.number())
{
UncleIsBrother ex;
ex << errinfo_uncleNumber(uncle.number());
ex << errinfo_currentNumber(m_currentBlock.number());
BOOST_THROW_EXCEPTION(ex);
}
uncle.verifyParent(uncleParent);
rewarded.push_back(uncle);
++ii;
}
catch (Exception& ex)
{
ex << errinfo_uncleIndex(ii);
throw;
}
}
DEV_TIMED_ABOVE("applyRewards", 500)
applyRewards(rewarded);
// Commit all cached state changes to the state trie.
DEV_TIMED_ABOVE("commit", 500)
commit();
// Hash the state trie and check against the state_root hash in m_currentBlock.
if (m_currentBlock.stateRoot() != m_previousBlock.stateRoot() && m_currentBlock.stateRoot() != rootHash())
{
auto r = rootHash();
m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidStateRoot() << Hash256RequirementError(r, m_currentBlock.stateRoot()));
}
if (m_currentBlock.gasUsed() != gasUsed())
{
// Rollback the trie.
m_db.rollback();
BOOST_THROW_EXCEPTION(InvalidGasUsed() << RequirementError(bigint(gasUsed()), bigint(m_currentBlock.gasUsed())));
}
return tdIncrease;
}
void State::cleanup(bool _fullCommit)
{
if (_fullCommit)
{
paranoia("immediately before database commit", true);
// Commit the new trie to disk.
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot() << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
try
{
EnforceRefs er(m_db, true);
rootHash();
}
catch (BadRoot const&)
{
clog(StateChat) << "Trie corrupt! :-(";
throw;
}
m_db.commit();
if (isChannelVisible<StateTrace>()) // Avoid calling toHex if not needed
clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot() << "=" << rootHash() << "=" << toHex(asBytes(m_db.lookup(rootHash())));
paranoia("immediately after database commit", true);
m_previousBlock = m_currentBlock;
m_currentBlock.populateFromParent(m_previousBlock);
clog(StateTrace) << "finalising enactment. current -> previous, hash is" << m_previousBlock.hash();
}
else
m_db.rollback();
resetCurrent();
}
void State::uncommitToMine()
{
if (m_committedToMine)
{
m_cache.clear();
if (!m_transactions.size())
m_state.setRoot(m_previousBlock.stateRoot());
else
m_state.setRoot(m_receipts.back().stateRoot());
m_db = m_lastTx;
paranoia("Uncommited to mine", true);
m_committedToMine = false;
}
}
LogBloom State::logBloom() const
{
LogBloom ret;
for (TransactionReceipt const& i: m_receipts)
ret |= i.bloom();
return ret;
}
void State::commitToMine(BlockChain const& _bc, bytes const& _extraData)
{
uncommitToMine();
m_lastTx = m_db;
vector<BlockInfo> uncleBlockHeaders;
RLPStream unclesData;
unsigned unclesCount = 0;
if (m_previousBlock.number() != 0)
{
// Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations.
clog(StateDetail) << "Checking " << m_previousBlock.hash() << ", parent=" << m_previousBlock.parentHash();
h256Hash excluded = _bc.allKinFrom(m_currentBlock.parentHash(), 6);
auto p = m_previousBlock.parentHash();
for (unsigned gen = 0; gen < 6 && p != _bc.genesisHash() && unclesCount < 2; ++gen, p = _bc.details(p).parent)
{
auto us = _bc.details(p).children;
assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent!
for (auto const& u: us)
if (!excluded.count(u)) // ignore any uncles/mainline blocks that we know about.
{
uncleBlockHeaders.push_back(_bc.info(u));
unclesData.appendRaw(_bc.headerData(u));
++unclesCount;
if (unclesCount == 2)
break;
}
}
}
BytesMap transactionsMap;
BytesMap receiptsMap;
RLPStream txs;
txs.appendList(m_transactions.size());
for (unsigned i = 0; i < m_transactions.size(); ++i)
{
RLPStream k;
k << i;
RLPStream receiptrlp;
m_receipts[i].streamRLP(receiptrlp);
receiptsMap.insert(std::make_pair(k.out(), receiptrlp.out()));
RLPStream txrlp;
m_transactions[i].streamRLP(txrlp);
transactionsMap.insert(std::make_pair(k.out(), txrlp.out()));
txs.appendRaw(txrlp.out());
}
txs.swapOut(m_currentTxs);
RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles);
// Apply rewards last of all.
applyRewards(uncleBlockHeaders);
// Commit any and all changes to the trie that are in the cache, then update the state root accordingly.
commit();
clog(StateDetail) << "Post-reward stateRoot:" << m_state.root();
clog(StateDetail) << m_state;
clog(StateDetail) << *this;
m_currentBlock.setLogBloom(logBloom());
m_currentBlock.setGasUsed(gasUsed());
m_currentBlock.setRoots(hash256(transactionsMap), hash256(receiptsMap), sha3(m_currentUncles), m_state.root());
m_currentBlock.setParentHash(m_previousBlock.hash());
m_currentBlock.setExtraData(_extraData);
if (m_currentBlock.extraData().size() > 32)
{
auto ed = m_currentBlock.extraData();
ed.resize(32);
m_currentBlock.setExtraData(ed);
}
m_committedToMine = true;
}
bool State::sealBlock(bytesConstRef _header)
{
if (!m_committedToMine)
return false;
// Check that this header is indeed for this block.
if (BlockInfo(_header, CheckNothing, h256{}, HeaderData).hashWithout() != m_currentBlock.hashWithout())
return false;
// Looks good!
clog(StateDetail) << "Sealing block!";
// Compile block:
RLPStream ret;
ret.appendList(3);
ret.appendRaw(_header);
ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes);
m_currentBlock = BlockInfo(_header, CheckNothing, h256(), HeaderData);
cnote << "Mined " << m_currentBlock.hash() << "(parent: " << m_currentBlock.parentHash() << ")";
// TODO: move into Sealer
StructuredLogger::minedNewBlock(
m_currentBlock.hash().abridged(),
"", // Can't give the nonce here.
"", //TODO: chain head hash here ??
m_currentBlock.parentHash().abridged()
);
// 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;
return true;
}
bool State::addressInUse(Address const& _id) const
{
ensureCached(_id, false, false);
@ -1138,7 +446,7 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const
return true;
}
ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp)
std::pair<ExecutionResult, TransactionReceipt> State::execute(EnvInfo const& _envInfo, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp)
{
auto onOp = _onOp;
#if ETH_VMTRACE
@ -1154,17 +462,13 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
// Create and initialize the executive. This will throw fairly cheaply and quickly if the
// transaction is bad in any way.
Executive e(*this, _lh, 0);
Executive e(*this, _envInfo);
ExecutionResult res;
e.setResultRecipient(res);
e.initialize(_t);
// Uncommitting is a non-trivial operation - only do it once we've verified as much of the
// transaction as possible.
uncommitToMine();
// OK - transaction looks valid - execute.
u256 startGasUsed = gasUsed();
u256 startGasUsed = _envInfo.gasUsed();
#if ETH_PARANOIA
ctrace << "Executing" << e.t() << "on" << h;
ctrace << toHex(e.t().rlp());
@ -1183,7 +487,7 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
else
{
commit();
#if ETH_PARANOIA && !ETH_FATDB
ctrace << "Executed; now" << rootHash();
ctrace << old.diff(*this);
@ -1200,45 +504,10 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per
}
}
#endif
// 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(e.t());
m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs()));
m_transactionSet.insert(e.t().sha3());
}
return res;
}
State State::fromPending(unsigned _i) const
{
State ret = *this;
ret.m_cache.clear();
_i = min<unsigned>(_i, m_transactions.size());
if (!_i)
ret.m_state.setRoot(m_previousBlock.stateRoot());
else
ret.m_state.setRoot(m_receipts[_i - 1].stateRoot());
while (ret.m_transactions.size() > _i)
{
ret.m_transactionSet.erase(ret.m_transactions.back().sha3());
ret.m_transactions.pop_back();
ret.m_receipts.pop_back();
}
return ret;
}
void State::applyRewards(vector<BlockInfo> const& _uncleBlockHeaders)
{
u256 r = m_blockReward;
for (auto const& i: _uncleBlockHeaders)
{
addBalance(i.coinbaseAddress(), m_blockReward * (8 + i.number() - m_currentBlock.number()) / 8);
r += m_blockReward / 32;
}
addBalance(m_currentBlock.coinbaseAddress(), r);
return make_pair(res, TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs()));
}
std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s)

153
libethereum/State.h

@ -85,16 +85,10 @@ enum class Permanence
Committed
};
struct PopulationStatistics
{
double verify;
double enact;
};
/**
* @brief Model of the current state of the ledger.
* Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block).
* Should maintain ledger as of last N blocks, also, in case we end up on the wrong branch.
* @brief Model of an Ethereum state, essentially a facade for the trie.
* Allows you to query the state of accounts, and has built-in caching for various aspects of the
* state.
*/
class State
{
@ -113,7 +107,7 @@ public:
/// which uses it. If you have no preexisting database then set BaseState to something other
/// than BaseState::PreExisting in order to prepopulate the Trie.
/// You can also set the coinbase address.
explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting, Address _coinbaseAddress = Address());
explicit State(OverlayDB const& _db, BaseState _bs = BaseState::PreExisting);
/// Copy state object.
State(State const& _s);
@ -121,66 +115,23 @@ public:
/// Copy state object.
State& operator=(State const& _s);
/// Construct state object from arbitrary point in blockchain.
PopulationStatistics populateFromChain(BlockChain const& _bc, h256 const& _hash, ImportRequirements::value _ir = ImportRequirements::None);
/// Set the coinbase address for any transactions we do.
/// This causes a complete reset of current block.
void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); }
Address address() const { return m_ourAddress; }
/// Open a DB - useful for passing into the constructor & keeping for other states that are necessary.
static OverlayDB openDB(std::string const& _path, h256 const& _genesisHash, WithExisting _we = WithExisting::Trust);
static OverlayDB openDB(h256 const& _genesisHash, WithExisting _we = WithExisting::Trust) { return openDB(std::string(), _genesisHash, _we); }
OverlayDB const& db() const { return m_db; }
OverlayDB& db() { return m_db; }
/// Populate the state from the given AccountMap. Just uses dev::eth::commit().
void populateFrom(AccountMap const& _map);
/// @returns the set containing all addresses currently in use in Ethereum.
/// @warning This is slowslowslow. Don't use it unless you want to lock the object for seconds or minutes at a time.
/// @throws InterfaceNotSupported if compiled without ETH_FATDB.
std::unordered_map<Address, u256> addresses() const;
/// Get the header information on the present block.
BlockInfo const& info() const { return m_currentBlock; }
/// Prepares the current state for mining.
/// Commits all transactions into the trie, compiles uncles and transactions list, applies all
/// rewards and populates the current block header with the appropriate hashes.
/// The only thing left to do after this is to actually mine().
///
/// This may be called multiple times and without issue.
void commitToMine(BlockChain const& _bc, bytes const& _extraData = {});
/// Pass in a solution to the proof-of-work.
/// @returns true iff we were previously committed to mining.
/// TODO: verify it prior to calling this.
/** Commit to DB and build the final block if the previous call to mine()'s result is completion.
* Typically looks like:
* @code
* while (notYetMined)
* {
* // lock
* commitToMine(_blockChain); // will call uncommitToMine if a repeat.
* completeMine();
* // unlock
* @endcode
*/
bool sealBlock(bytes const& _header) { return sealBlock(&_header); }
bool sealBlock(bytesConstRef _header);
/// Get the complete current block, including valid nonce.
/// Only valid after mine() returns true.
bytes const& blockData() const { return m_currentBytes; }
/// Sync our transactions, killing those from the queue that we have and assimilating those that we don't.
/// @returns a list of receipts one for each transaction placed from the queue into the state and bool, true iff there are more transactions to be processed.
std::pair<TransactionReceipts, bool> sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned _msTimeout = 100);
/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.
ExecutionResult execute(LastHashes const& _lh, Transaction const& _t, Permanence _p = Permanence::Committed, OnOpFunc const& _onOp = OnOpFunc());
/// Get the remaining gas limit in this block.
u256 gasLimitRemaining() const { return m_currentBlock.gasLimit() - gasUsed(); }
/// This will change the state accordingly.
std::pair<ExecutionResult, TransactionReceipt> execute(EnvInfo const& _envInfo, Transaction const& _t, Permanence _p = Permanence::Committed, OnOpFunc const& _onOp = OnOpFunc());
/// Check if the address is in use.
bool addressInUse(Address const& _address) const;
@ -246,63 +197,18 @@ public:
/// The hash of the root of our state tree.
h256 rootHash() const { return m_state.root(); }
/// Get the list of pending transactions.
Transactions const& pending() const { return m_transactions; }
/// Get the list of hashes of pending transactions.
h256Hash const& pendingHashes() const { return m_transactionSet; }
/// 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.
LogEntries const& log(unsigned _i) const { return m_receipts[_i].log(); }
/// 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.
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.
/// If (_i == pending().size()) returns the final state of the block, prior to rewards.
State fromPending(unsigned _i) const;
/// @returns the StateDiff caused by the pending transaction of index @a _i.
StateDiff pendingDiff(unsigned _i) const { return fromPending(_i).diff(fromPending(_i + 1), true); }
/// @return the difference between this state (origin) and @a _c (destination).
/// @param _quick if true doesn't check all addresses possible (/very/ slow for a full chain)
/// but rather only those touched by the transactions in creating the two States.
StateDiff diff(State const& _c, bool _quick = false) const;
/// Sync our state with the block chain.
/// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue.
bool sync(BlockChain const& _bc);
/// Sync with the block chain, but rather than synching to the latest block, instead sync to the given block.
bool sync(BlockChain const& _bc, h256 const& _blockHash, BlockInfo const& _bi = BlockInfo());
/// Execute all transactions within a given block.
/// @returns the additional total difficulty.
u256 enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc);
/// Returns back to a pristine state after having done a playback.
/// @arg _fullCommit if true flush everything out to disk. If false, this effectively only validates
/// the block since all state changes are ultimately reversed.
void cleanup(bool _fullCommit);
/// Commit all changes waiting in the address cache to the DB.
void commit();
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();
/// Resets any uncommitted changes to the cache.
void setRoot(h256 const& _root);
private:
/// Undo the changes to the state for committing to mine.
void uncommitToMine();
/// Retrieve all information about a given address into the cache.
/// If _requireMemory is true, grab the full memory should it be a contract item.
/// If _forceCreate is true, then insert a default item into the cache, in the case it doesn't
@ -312,45 +218,16 @@ private:
/// Retrieve all information about a given address into a cache.
void ensureCached(std::unordered_map<Address, Account>& _cache, Address const& _a, bool _requireCode, bool _forceCreate) const;
/// Execute the given block, assuming it corresponds to m_currentBlock.
/// Throws on failure.
u256 enact(VerifiedBlockRef const& _block, BlockChain const& _bc);
/// Finalise the block, applying the earned rewards.
void applyRewards(std::vector<BlockInfo> const& _uncleBlockHeaders);
/// @returns gas used by transactions thus far executed.
u256 gasUsed() const { return m_receipts.size() ? m_receipts.back().gasUsed() : 0; }
/// Debugging only. Good for checking the Trie is in shape.
bool isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const;
/// Debugging only. Good for checking the Trie is in shape.
void paranoia(std::string const& _when, bool _enforceRefs = false) const;
/// Provide a standard VM trace for debugging purposes.
std::string vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir);
OverlayDB m_db; ///< Our overlay for the state tree.
SecureTrieDB<Address, OverlayDB> m_state; ///< Our state tree, as an OverlayDB DB.
Transactions m_transactions; ///< The current list of transactions that we've included in the state.
TransactionReceipts m_receipts; ///< The corresponding list of transaction receipts.
h256Hash m_transactionSet; ///< The set of transaction hashes that we've included in the state.
OverlayDB m_lastTx;
AddressHash m_touched; ///< Tracks all addresses touched by transactions so far.
mutable std::unordered_map<Address, Account> m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed.
BlockInfo m_previousBlock; ///< The previous block's information.
BlockInfo m_currentBlock; ///< The current block's information.
bytes m_currentBytes; ///< The current block.
bool m_committedToMine = false; ///< Have we committed to mine on the present m_currentBlock?
bytes m_currentTxs; ///< The RLP-encoded block of transactions.
bytes m_currentUncles; ///< The RLP-encoded block of uncles.
Address m_ourAddress; ///< Our address (i.e. the address to which fees go).
u256 m_blockReward;
AddressHash m_touched; ///< Tracks all addresses touched so far.
static std::string c_defaultPath;
@ -360,7 +237,7 @@ private:
std::ostream& operator<<(std::ostream& _out, State const& _s);
template <class DB>
AddressHash commit(std::unordered_map<Address, Account> const& _cache, SecureTrieDB<Address, DB>& _state)
AddressHash commit(AccountMap const& _cache, SecureTrieDB<Address, DB>& _state)
{
AddressHash ret;
for (auto const& i: _cache)

6
libevm/ExtVMFace.cpp

@ -24,7 +24,8 @@
using namespace dev;
using namespace dev::eth;
ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth):
ExtVMFace::ExtVMFace(EnvInfo const& _envInfo, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, unsigned _depth):
m_envInfo(_envInfo),
myAddress(_myAddress),
caller(_caller),
origin(_origin),
@ -33,8 +34,5 @@ ExtVMFace::ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256
data(_data),
code(std::move(_code)),
codeHash(_codeHash),
lastHashes(_lh),
previousBlock(_previousBlock),
currentBlock(_currentBlock),
depth(_depth)
{}

66
libevm/ExtVMFace.h

@ -162,6 +162,57 @@ struct CallParameters
OnOpFunc onOp;
};
class EnvInfo
{
public:
EnvInfo() {}
EnvInfo(BlockInfo const& _current, LastHashes const& _lh = LastHashes(), u256 const& _gasUsed = u256()):
m_number(_current.number()),
m_beneficiary(_current.beneficiary()),
m_timestamp(_current.timestamp()),
m_difficulty(_current.difficulty()),
m_gasLimit(_current.gasLimit()),
m_lastHashes(_lh),
m_gasUsed(_gasUsed)
{}
EnvInfo(BlockInfo const& _current, LastHashes&& _lh, u256 const& _gasUsed = u256()):
m_number(_current.number()),
m_beneficiary(_current.beneficiary()),
m_timestamp(_current.timestamp()),
m_difficulty(_current.difficulty()),
m_gasLimit(_current.gasLimit()),
m_lastHashes(_lh),
m_gasUsed(_gasUsed)
{}
u256 const& number() const { return m_number; }
Address const& beneficiary() const { return m_beneficiary; }
u256 const& timestamp() const { return m_timestamp; }
u256 const& difficulty() const { return m_difficulty; }
u256 const& gasLimit() const { return m_gasLimit; }
LastHashes const& lastHashes() const { return m_lastHashes; }
u256 const& gasUsed() const { return m_gasUsed; }
void setNumber(u256 const& _v) { m_number = _v; }
void setBeneficiary(Address const& _v) { m_beneficiary = _v; }
void setTimestamp(u256 const& _v) { m_timestamp = _v; }
void setDifficulty(u256 const& _v) { m_difficulty = _v; }
void setGasLimit(u256 const& _v) { m_gasLimit = _v; }
void setLastHashes(LastHashes const& _lh) { m_lastHashes = _lh; }
void setLastHashes(LastHashes&& _lh) { m_lastHashes = _lh; }
void setGasUsed(u256 const& _v) { m_gasUsed = _v; }
private:
u256 m_number;
Address m_beneficiary;
u256 m_timestamp;
u256 m_difficulty;
u256 m_gasLimit;
LastHashes m_lastHashes;
u256 m_gasUsed;
};
/**
* @brief Interface and null implementation of the class for specifying VM externalities.
*/
@ -172,7 +223,7 @@ public:
ExtVMFace() = default;
/// Full constructor.
ExtVMFace(Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, LastHashes const& _lh, unsigned _depth);
ExtVMFace(EnvInfo const& _envInfo, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytes _code, h256 const& _codeHash, unsigned _depth);
virtual ~ExtVMFace() = default;
@ -216,11 +267,19 @@ public:
virtual void revert() {}
/// Hash of a block if within the last 256 blocks, or h256() otherwise.
h256 blockhash(u256 _number) { return _number < currentBlock.number() && _number >= (std::max<u256>(256, currentBlock.number()) - 256) ? lastHashes[(unsigned)(currentBlock.number() - 1 - _number)] : h256(); }
h256 blockHash(u256 _number) { return _number < envInfo().number() && _number >= (std::max<u256>(256, envInfo().number()) - 256) ? envInfo().lastHashes()[(unsigned)(envInfo().number() - 1 - _number)] : h256(); }
/// Get the code at the given location in code ROM.
byte getCode(u256 _n) const { return _n < code.size() ? code[(size_t)_n] : 0; }
/// Get the execution environment information.
EnvInfo const& envInfo() const { return m_envInfo; }
private:
EnvInfo const& m_envInfo;
public:
// TODO: make private
Address myAddress; ///< Address associated with executing code (a contract, or contract-to-be).
Address caller; ///< Address which sent the message (either equal to origin or a contract).
Address origin; ///< Original transactor.
@ -229,9 +288,6 @@ public:
bytesConstRef data; ///< Current input data.
bytes code; ///< Current code that is executing.
h256 codeHash; ///< SHA3 hash of the executing code
LastHashes lastHashes; ///< Most recent 256 blocks' hashes.
BlockInfo previousBlock; ///< The previous block's information. TODO: PoC-8: REMOVE
BlockInfo currentBlock; ///< The current block's information.
SubState sub; ///< Sub-band VM state (suicides, refund counter, logs).
unsigned depth = 0; ///< Depth of the present call.
};

12
libevm/VM.cpp

@ -398,22 +398,22 @@ bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
m_stack.push_back(_ext.gasPrice);
break;
case Instruction::BLOCKHASH:
m_stack.back() = (u256)_ext.blockhash(m_stack.back());
m_stack.back() = (u256)_ext.blockHash(m_stack.back());
break;
case Instruction::COINBASE:
m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress());
m_stack.push_back((u160)_ext.envInfo().beneficiary());
break;
case Instruction::TIMESTAMP:
m_stack.push_back(_ext.currentBlock.timestamp());
m_stack.push_back(_ext.envInfo().timestamp());
break;
case Instruction::NUMBER:
m_stack.push_back(_ext.currentBlock.number());
m_stack.push_back(_ext.envInfo().number());
break;
case Instruction::DIFFICULTY:
m_stack.push_back(_ext.currentBlock.difficulty());
m_stack.push_back(_ext.envInfo().difficulty());
break;
case Instruction::GASLIMIT:
m_stack.push_back(_ext.currentBlock.gasLimit());
m_stack.push_back(_ext.envInfo().gasLimit());
break;
case Instruction::PUSH1:
case Instruction::PUSH2:

5
libevmasm/KnownState.h

@ -29,7 +29,12 @@
#include <tuple>
#include <memory>
#include <ostream>
#pragma warning(push)
#pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Wredeclared-class-member"
#include <boost/bimap.hpp>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h>
#include <libevmasm/ExpressionClasses.h>

20
libtestutils/BlockChainLoader.cpp

@ -19,11 +19,10 @@
* @date 2015
*/
#include <json/writer.h>
#include <libethereum/CanonBlockChain.h>
#include "BlockChainLoader.h"
#include "StateLoader.h"
#include "Common.h"
using namespace std;
using namespace dev;
using namespace dev::test;
@ -31,21 +30,24 @@ using namespace dev::eth;
BlockChainLoader::BlockChainLoader(Json::Value const& _json)
{
// load genesisBlock
bytes genesisBl = fromHex(_json["genesisRLP"].asString());
Json::FastWriter a;
m_bc.reset(new FullBlockChain<Ethash>(genesisBl, jsonToAccountMap( a.write(_json["pre"])), m_dir.path(), WithExisting::Kill));
// load pre state
StateLoader sl(_json["pre"], m_dir.path());
m_state = sl.state();
m_block = m_bc->genesisBlock(State::openDB(m_dir.path(), m_bc->genesisHash(), WithExisting::Kill));
// load genesisBlock
m_bc.reset(new FullBlockChain<Ethash>(fromHex(_json["genesisRLP"].asString()), sl.stateDefinition(), m_dir.path(), WithExisting::Kill));
assert(m_state.rootHash() == m_bc->info().stateRoot());
assert(m_block.rootHash() == m_bc->info().stateRoot());
// load blocks
for (auto const& block: _json["blocks"])
{
bytes rlp = fromHex(block["rlp"].asString());
m_bc->import(rlp, m_state.db());
m_bc->import(rlp, state().db());
}
// sync state
m_state.sync(*m_bc);
m_block.sync(*m_bc);
}

7
libtestutils/BlockChainLoader.h

@ -24,7 +24,7 @@
#include <json/json.h>
#include <libdevcore/TransientDirectory.h>
#include <libethereum/BlockChain.h>
#include <libethereum/State.h>
#include <libethereum/Block.h>
namespace dev
{
@ -40,12 +40,13 @@ class BlockChainLoader
public:
BlockChainLoader(Json::Value const& _json);
eth::BlockChain const& bc() const { return *m_bc; }
eth::State const& state() const { return m_state; }
eth::State const& state() const { return m_block.state(); } // TODO remove?
eth::Block const& block() const { return m_block; }
private:
TransientDirectory m_dir;
std::unique_ptr<eth::BlockChain> m_bc;
eth::State m_state;
eth::Block m_block;
};
}

4
libtestutils/FixedClient.cpp

@ -25,10 +25,10 @@ using namespace dev;
using namespace dev::eth;
using namespace dev::test;
eth::State FixedClient::asOf(h256 const& _h) const
Block FixedClient::asOf(h256 const& _h) const
{
ReadGuard l(x_stateDB);
State ret(m_state.db());
Block ret(m_block.db());
ret.populateFromChain(bc(), _h);
return ret;
}

12
libtestutils/FixedClient.h

@ -37,7 +37,7 @@ namespace test
class FixedClient: public dev::eth::ClientBase
{
public:
FixedClient(eth::BlockChain const& _bc, eth::State _state) : m_bc(_bc), m_state(_state) {}
FixedClient(eth::BlockChain const& _bc, eth::Block const& _block) : m_bc(_bc), m_block(_block) {}
virtual ~FixedClient() {}
// stub
@ -45,15 +45,15 @@ public:
virtual eth::BlockChain& bc() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("FixedClient::bc()")); }
virtual eth::BlockChain const& bc() const override { return m_bc; }
using ClientBase::asOf;
virtual eth::State asOf(h256 const& _h) const override;
virtual eth::State preMine() const override { ReadGuard l(x_stateDB); return m_state; }
virtual eth::State postMine() const override { ReadGuard l(x_stateDB); return m_state; }
virtual void setAddress(Address _us) override { WriteGuard l(x_stateDB); m_state.setAddress(_us); }
virtual eth::Block asOf(h256 const& _h) const override;
virtual eth::Block preMine() const override { ReadGuard l(x_stateDB); return m_block; }
virtual eth::Block postMine() const override { ReadGuard l(x_stateDB); return m_block; }
virtual void setBeneficiary(Address _us) override { WriteGuard l(x_stateDB); m_block.setBeneficiary(_us); }
virtual void prepareForTransaction() override {}
private:
eth::BlockChain const& m_bc;
eth::State m_state;
eth::Block m_block;
mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine.
};

57
libtestutils/StateLoader.cpp

@ -1,57 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file StateLoader.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
#include "StateLoader.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::test;
StateLoader::StateLoader(Json::Value const& _json, std::string const& _dbPath):
m_state(State::openDB(_dbPath, h256{}, WithExisting::Kill), BaseState::Empty)
{
for (string const& name: _json.getMemberNames())
{
Json::Value o = _json[name];
Address address = Address(name);
bytes code = fromHex(o["code"].asString().substr(2));
if (!code.empty())
{
m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::ContractConception);
m_state.m_cache[address].setCode(std::move(code));
}
else
m_state.m_cache[address] = Account(u256(o["balance"].asString()), Account::NormalCreation);
for (string const& j: o["storage"].getMemberNames())
m_state.setStorage(address, u256(j), u256(o["storage"][j].asString()));
for (auto i = 0; i < u256(o["nonce"].asString()); ++i)
m_state.noteSending(address);
m_state.ensureCached(address, false, false);
}
m_state.commit();
}

49
libtestutils/StateLoader.h

@ -1,49 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file StateLoader.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
#pragma once
#include <json/json.h>
#include <libdevcore/TransientDirectory.h>
#include <libethereum/State.h>
#include <libethereum/BlockChain.h>
namespace dev
{
namespace test
{
/**
* @brief Friend of State, loads State from given JSON object
*/
class StateLoader
{
public:
StateLoader(Json::Value const& _json, std::string const& _dbPath);
eth::State const& state() const { return m_state; }
eth::StateDefinition const& stateDefinition() const { return m_state.m_cache; }
private:
eth::State m_state;
};
}
}

2
libweb3jsonrpc/JsonHelper.cpp

@ -90,7 +90,7 @@ Json::Value toJson(dev::eth::BlockInfo const& _bi)
res["hash"] = toJS(_bi.hash());
res["parentHash"] = toJS(_bi.parentHash());
res["sha3Uncles"] = toJS(_bi.sha3Uncles());
res["miner"] = toJS(_bi.coinbaseAddress());
res["miner"] = toJS(_bi.beneficiary());
res["stateRoot"] = toJS(_bi.stateRoot());
res["transactionsRoot"] = toJS(_bi.transactionsRoot());
res["difficulty"] = toJS(_bi.difficulty());

15
libweb3jsonrpc/WebThreeStubServer.cpp

@ -29,6 +29,7 @@
#include <libdevcore/CommonJS.h>
#include <libethcore/KeyManager.h>
#include <libethereum/Executive.h>
#include <libethereum/Block.h>
#include <libwebthree/WebThree.h>
#include "JsonHelper.h"
using namespace std;
@ -218,7 +219,7 @@ bool WebThreeStubServer::admin_eth_setMiningBenefactor(std::string const& _uuidO
if (m_setMiningBenefactor)
m_setMiningBenefactor(a);
else
m_web3.ethereum()->setAddress(a);
m_web3.ethereum()->setBeneficiary(a);
return true;
}
@ -256,7 +257,7 @@ Json::Value WebThreeStubServer::admin_eth_reprocess(std::string const& _blockNum
ADMIN_GUARD;
Json::Value ret;
PopulationStatistics ps;
m_web3.ethereum()->state(blockHash(_blockNumberOrHash), &ps);
m_web3.ethereum()->block(blockHash(_blockNumberOrHash), &ps);
ret["enact"] = ps.enact;
ret["verify"] = ps.verify;
ret["total"] = ps.verify + ps.enact;
@ -270,16 +271,14 @@ Json::Value WebThreeStubServer::admin_eth_vmTrace(std::string const& _blockNumbe
Json::Value ret;
auto c = m_web3.ethereum();
State state = c->state(_txIndex + 1, blockHash(_blockNumberOrHash));
if (_txIndex < 0)
throw jsonrpc::JsonRpcException("Negative index");
if ((unsigned)_txIndex < state.pending().size())
Block block = c->block(blockHash(_blockNumberOrHash));
if ((unsigned)_txIndex < block.pending().size())
{
Transaction t = block.pending()[_txIndex];
State state = block.fromPending(_txIndex);
Executive e(state, bc(), 0);
Transaction t = state.pending()[_txIndex];
state = state.fromPending(_txIndex);
try
{
StandardTrace st;

71
mix/MixClient.cpp

@ -93,10 +93,10 @@ void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts
h256 stateRoot = accountState.root();
m_bc.reset();
m_bc.reset(new MixBlockChain(m_dbPath, stateRoot));
State s(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address());
s.sync(bc());
m_state = s;
m_startState = m_state;
Block b(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address());
b.sync(bc());
m_preMine = b;
m_postMine = b;
WriteGuard lx(x_executions);
m_executions.clear();
}
@ -122,12 +122,13 @@ Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secre
return ret;
}
ExecutionResult MixClient::debugTransaction(Transaction const& _t, State const& _state, LastHashes const& _lastHashes, bool _call)
// TODO: prototype changed - will need rejigging.
ExecutionResult MixClient::debugTransaction(Transaction const& _t, State const& _state, EnvInfo const& _envInfo, bool _call)
{
State execState = _state;
execState.addBalance(_t.sender(), _t.gas() * _t.gasPrice()); //give it enough balance for gas estimation
eth::ExecutionResult er;
Executive execution(execState, _lastHashes, 0);
Executive execution(execState, _envInfo);
execution.setResultRecipient(er);
execution.initialize(_t);
execution.execute();
@ -239,35 +240,31 @@ ExecutionResult MixClient::debugTransaction(Transaction const& _t, State const&
if (_t.isCreation())
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce())));
if (!_call)
d.transactionIndex = m_state.pending().size();
d.transactionIndex = m_postMine.pending().size();
d.executonIndex = m_executions.size();
return d;
}
void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call, bool _gasAuto, Secret const& _secret)
void MixClient::executeTransaction(Transaction const& _t, Block& _block, bool _call, bool _gasAuto, Secret const& _secret)
{
Transaction t = _gasAuto ? replaceGas(_t, m_state.gasLimitRemaining()) : _t;
// do debugging run first
LastHashes lastHashes(256);
lastHashes[0] = bc().numberHash(bc().number());
for (unsigned i = 1; i < 256; ++i)
lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256();
Transaction t = _gasAuto ? replaceGas(_t, m_postMine.gasLimitRemaining()) : _t;
ExecutionResult d = debugTransaction(t, _state, lastHashes, _call);
// do debugging run first
EnvInfo envInfo(bc().info(), bc().lastHashes());
ExecutionResult d = debugTransaction(t, _block.state(), envInfo, _call);
// execute on a state
if (!_call)
{
t = _gasAuto ? replaceGas(_t, d.gasUsed, _secret) : _t;
eth::ExecutionResult er = _state.execute(lastHashes, t);
if (t.isCreation() && _state.code(d.contractAddress).empty())
eth::ExecutionResult const& er = _block.execute(envInfo.lastHashes(), t);
if (t.isCreation() && _block.state().code(d.contractAddress).empty())
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment"));
d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend;
LocalisedLogEntries logs;
TransactionReceipt const& tr = _state.receipt(_state.pending().size() - 1);
TransactionReceipt const& tr = _block.receipt(_block.pending().size() - 1);
//auto trHash = _state.pending().at(_state.pending().size() - 1).sha3();
LogEntries le = tr.log();
if (le.size())
for (unsigned j = 0; j < le.size(); ++j)
@ -281,15 +278,15 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
void MixClient::mine()
{
WriteGuard l(x_state);
m_state.commitToMine(bc());
m_postMine.commitToSeal(bc());
NoProof::BlockHeader h(m_state.info());
NoProof::BlockHeader h(m_postMine.info());
RLPStream header;
h.streamRLP(header);
m_state.sealBlock(header.out());
bc().import(m_state.blockData(), m_state.db(), ImportRequirements::Everything & ~ImportRequirements::ValidSeal);
m_state.sync(bc());
m_startState = m_state;
m_postMine.sealBlock(header.out());
bc().import(m_postMine.blockData(), m_stateDB, ImportRequirements::Everything & ~ImportRequirements::ValidSeal);
m_postMine.sync(bc());
m_preMine = m_postMine;
}
ExecutionResult MixClient::lastExecution() const
@ -304,10 +301,10 @@ ExecutionResult MixClient::execution(unsigned _index) const
return m_executions.at(_index);
}
State MixClient::asOf(h256 const& _block) const
Block MixClient::asOf(h256 const& _block) const
{
ReadGuard l(x_state);
State ret(m_stateDB);
Block ret(m_stateDB);
ret.populateFromChain(bc(), _block);
return ret;
}
@ -317,23 +314,23 @@ pair<h256, Address> MixClient::submitTransaction(eth::TransactionSkeleton const&
WriteGuard l(x_state);
TransactionSkeleton ts = _ts;
ts.from = toAddress(_secret);
ts.nonce = m_state.transactionsFrom(ts.from);
ts.nonce = m_postMine.transactionsFrom(ts.from);
eth::Transaction t(ts, _secret);
executeTransaction(t, m_state, false, _gasAuto, _secret);
executeTransaction(t, m_postMine, false, _gasAuto, _secret);
return make_pair(t.sha3(), toAddress(ts.from, ts.nonce));
}
dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto, FudgeFactor _ff)
{
(void)_blockNumber;
State temp = asOf(eth::PendingBlock);
u256 n = temp.transactionsFrom(_from);
Block block = asOf(eth::PendingBlock);
u256 n = block.transactionsFrom(_from);
Transaction t(_value, _gasPrice, _gas, _dest, _data, n);
t.forceSender(_from);
if (_ff == FudgeFactor::Lenient)
temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
block.mutableState().addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp, true, _gasAuto);
executeTransaction(t, block, true, _gasAuto);
return lastExecution().result;
}
@ -346,7 +343,7 @@ dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, b
{
(void)_blockNumber;
u256 n;
State temp;
Block temp;
{
ReadGuard lr(x_state);
temp = asOf(eth::PendingBlock);
@ -355,7 +352,7 @@ dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, b
Transaction t(_value, _gasPrice, _gas, _data, n);
t.forceSender(_from);
if (_ff == FudgeFactor::Lenient)
temp.addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
temp.mutableState().addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value()));
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
executeTransaction(t, temp, true, false);
return lastExecution().result;
@ -367,10 +364,10 @@ eth::BlockInfo MixClient::blockInfo() const
return BlockInfo(bc().block());
}
void MixClient::setAddress(Address _us)
void MixClient::setBeneficiary(Address _us)
{
WriteGuard l(x_state);
m_state.setAddress(_us);
m_postMine.setBeneficiary(_us);
}
void MixClient::startMining()

17
mix/MixClient.h

@ -33,6 +33,7 @@
namespace dev
{
namespace eth { class EnvInfo; }
namespace mix
{
@ -89,8 +90,8 @@ public:
virtual std::pair<h256, Address> submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret) override { return submitTransaction(_ts, _secret, false); }
std::pair<h256, Address> submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto);
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict);
ExecutionResult debugTransaction(dev::eth::Transaction const& _t, eth:: State const& _state, eth::LastHashes const& _lastHashes, bool _call);
void setAddress(Address _us) override;
ExecutionResult debugTransaction(dev::eth::Transaction const& _t, eth:: State const& _state, eth::EnvInfo const& _envInfo, bool _call);
void setBeneficiary(Address _us) override;
void startMining() override;
void stopMining() override;
bool isMining() const override;
@ -108,19 +109,19 @@ public:
protected:
/// ClientBase methods
using ClientBase::asOf;
virtual dev::eth::State asOf(h256 const& _block) const override;
virtual dev::eth::Block asOf(h256 const& _block) const override;
virtual dev::eth::BlockChain& bc() override { return *m_bc; }
virtual dev::eth::BlockChain const& bc() const override { return *m_bc; }
virtual dev::eth::State preMine() const override { ReadGuard l(x_state); return m_startState; }
virtual dev::eth::State postMine() const override { ReadGuard l(x_state); return m_state; }
virtual dev::eth::Block preMine() const override { ReadGuard l(x_state); return m_preMine; }
virtual dev::eth::Block postMine() const override { ReadGuard l(x_state); return m_postMine; }
virtual void prepareForTransaction() override {}
private:
void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret());
void executeTransaction(dev::eth::Transaction const& _t, eth::Block& _block, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret());
dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret());
eth::State m_state;
eth::State m_startState;
eth::Block m_preMine;
eth::Block m_postMine;
OverlayDB m_stateDB;
std::unique_ptr<MixBlockChain> m_bc;
mutable boost::shared_mutex x_state;

2
solc/docker_emscripten/Dockerfile

@ -62,7 +62,7 @@ RUN git remote add -f solidityjs https://github.com/chriseth/cpp-ethereum
# TODO this should be a proper merge but somehow causes problems
# NOTE that we only get the latest commit of that branch
RUN git cherry-pick solidityjs/solidity-js
RUN emcmake cmake -DETH_STATIC=1 -DSOLIDITY=ON -DGUI=0 -DCMAKE_CXX_COMPILER=/home/user/emsdk_portable/emscripten/master/em++ -DCMAKE_C_COMPILER=/home/user/emsdk_portable/emscripten/master/emcc
RUN emcmake cmake -DMINER=0 -DETHKEY=0 -DSERPENT=0 -DTESTS=0 -DETHASHCL=0 -DJSCONSOLE=0 -DEVMJIT=0 -DETH_STATIC=1 -DSOLIDITY=1 -DGUI=0 -DCMAKE_CXX_COMPILER=/home/user/emsdk_portable/emscripten/master/em++ -DCMAKE_C_COMPILER=/home/user/emsdk_portable/emscripten/master/emcc
RUN emmake make -j 6 soljson
WORKDIR /home/user/cpp-ethereum/solc

200
test/TestHelper.cpp

@ -25,6 +25,7 @@
#include <chrono>
#include <libethcore/EthashAux.h>
#include <libethereum/Client.h>
#include <libevm/ExtVMFace.h>
#include <liblll/Compiler.h>
#include <libevm/VMFactory.h>
#include "Stats.h"
@ -61,10 +62,10 @@ void connectClients(Client& c1, Client& c2)
#endif
}
void mine(State& s, BlockChain const& _bc)
void mine(Block& s, BlockChain const& _bc)
{
std::unique_ptr<SealEngineFace> sealer(Ethash::createSealEngine());
s.commitToMine(_bc);
s.commitToSeal(_bc);
Notified<bytes> sealed;
sealer->onSealGenerated([&](bytes const& sealedHeader){ sealed = sealedHeader; });
sealer->generateSeal(s.info());
@ -94,22 +95,43 @@ struct MissingFields : virtual Exception {};
bigint const c_max256plus1 = bigint(1) << 256;
ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller):
m_statePre(OverlayDB(), eth::BaseState::Empty, Address(_o["env"].get_obj()["currentCoinbase"].get_str())),
m_statePost(OverlayDB(), eth::BaseState::Empty, Address(_o["env"].get_obj()["currentCoinbase"].get_str())),
m_TestObject(_o)
ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller, testType testTemplate):
m_statePre(OverlayDB(), eth::BaseState::Empty),
m_statePost(OverlayDB(), eth::BaseState::Empty),
m_testObject(_o)
{
importEnv(_o["env"].get_obj());
importState(_o["pre"].get_obj(), m_statePre);
importTransaction(_o["transaction"].get_obj());
if (!isFiller)
if (testTemplate == testType::StateTests)
{
importState(_o["post"].get_obj(), m_statePost);
m_environment.sub.logs = importLog(_o["logs"].get_array());
importEnv(_o["env"].get_obj());
importTransaction(_o["transaction"].get_obj());
importState(_o["pre"].get_obj(), m_statePre);
if (!isFiller)
{
if (_o.count("post"))
importState(_o["post"].get_obj(), m_statePost);
else
importState(_o["postState"].get_obj(), m_statePost);
m_logsExpected = importLog(_o["logs"].get_array());
}
}
}
//executes an imported transacton on preState
bytes ImportTest::executeTest()
{
ExecutionResult res;
eth::State tmpState = m_statePre;
std::pair<ExecutionResult, TransactionReceipt> execOut = m_statePre.execute(m_envInfo, m_transaction);
res = execOut.first;
m_logs = execOut.second.log();
m_statePre.commit();
m_statePost = m_statePre;
m_statePre = tmpState;
return res.output;
}
json_spirit::mObject& ImportTest::makeAllFieldsHex(json_spirit::mObject& _o)
{
static const set<string> hashes {"bloom" , "coinbase", "hash", "mixHash", "parentHash", "receiptTrie",
@ -138,105 +160,35 @@ json_spirit::mObject& ImportTest::makeAllFieldsHex(json_spirit::mObject& _o)
void ImportTest::importEnv(json_spirit::mObject& _o)
{
assert(_o.count("previousHash") > 0);
assert(_o.count("currentGasLimit") > 0);
assert(_o.count("currentDifficulty") > 0);
assert(_o.count("currentDifficulty") > 0);
assert(_o.count("currentNumber") > 0);
assert(_o.count("currentTimestamp") > 0);
assert(_o.count("currentCoinbase") > 0);
assert(_o.count("currentNumber") > 0);
RLPStream rlpStream;
rlpStream.appendList(BlockInfo::BasicFields);
rlpStream << h256(_o["previousHash"].get_str());
rlpStream << EmptyListSHA3;
rlpStream << Address(_o["currentCoinbase"].get_str());
rlpStream << h256(); // stateRoot
rlpStream << EmptyTrie; // transactionTrie
rlpStream << EmptyTrie; // receiptTrie
rlpStream << LogBloom(); // bloom
rlpStream << toInt(_o["currentDifficulty"]);
rlpStream << toInt(_o["currentNumber"]);
rlpStream << toInt(_o["currentGasLimit"]);
rlpStream << 0; //gasUsed
rlpStream << toInt(_o["currentTimestamp"]);
rlpStream << std::string(); //extra data
m_environment.currentBlock = BlockInfo(rlpStream.out(), CheckEverything, h256{}, HeaderData);
m_statePre.m_previousBlock = m_environment.previousBlock;
m_statePre.m_currentBlock = m_environment.currentBlock;
m_envInfo.setGasLimit(toInt(_o["currentGasLimit"]));
m_envInfo.setDifficulty(toInt(_o["currentDifficulty"]));
m_envInfo.setNumber(toInt(_o["currentNumber"]));
m_envInfo.setTimestamp(toInt(_o["currentTimestamp"]));
m_envInfo.setBeneficiary(Address(_o["currentCoinbase"].get_str()));
m_envInfo.setLastHashes( lastHashes( m_envInfo.number() ) );
}
// import state from not fully declared json_spirit::mObject, writing to _stateOptionsMap which fields were defined in json
void ImportTest::importState(json_spirit::mObject& _o, State& _state, stateOptionsMap& _stateOptionsMap)
{
for (auto& i: _o)
{
json_spirit::mObject o = i.second.get_obj();
ImportStateOptions stateOptions;
u256 balance = 0;
u256 nonce = 0;
if (o.count("balance") > 0)
{
stateOptions.m_bHasBalance = true;
if (bigint(o["balance"].get_str()) >= c_max256plus1)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'balance' is equal or greater than 2**256") );
balance = toInt(o["balance"]);
}
if (o.count("nonce") > 0)
{
stateOptions.m_bHasNonce = true;
if (bigint(o["nonce"].get_str()) >= c_max256plus1)
BOOST_THROW_EXCEPTION(ValueTooLarge() << errinfo_comment("State 'nonce' is equal or greater than 2**256") );
nonce = toInt(o["nonce"]);
}
Address address = Address(i.first);
bytes code;
if (o.count("code") > 0)
{
code = importCode(o);
stateOptions.m_bHasCode = true;
}
if (!code.empty())
{
_state.m_cache[address] = Account(balance, Account::ContractConception);
_state.m_cache[address].setCode(std::move(code));
}
else
_state.m_cache[address] = Account(balance, Account::NormalCreation);
if (o.count("storage") > 0)
{
stateOptions.m_bHasStorage = true;
for (auto const& j: o["storage"].get_obj())
_state.setStorage(address, toInt(j.first), toInt(j.second));
}
for (int i = 0; i < nonce; ++i)
_state.noteSending(address);
_state.ensureCached(address, false, false);
_stateOptionsMap[address] = stateOptions;
}
void ImportTest::importState(json_spirit::mObject& _o, State& _state, AccountMaskMap& o_mask)
{
std::string jsondata = json_spirit::write_string((json_spirit::mValue)_o, false);
_state.populateFrom(jsonToAccountMap(jsondata, &o_mask));
}
void ImportTest::importState(json_spirit::mObject& _o, State& _state)
{
stateOptionsMap importedMap;
importState(_o, _state, importedMap);
for (auto& stateOptionMap : importedMap)
{
AccountMaskMap mask;
importState(_o, _state, mask);
for (auto const& i: mask)
//check that every parameter was declared in state object
if (!stateOptionMap.second.isAllSet())
if (!i.second.allSet())
BOOST_THROW_EXCEPTION(MissingFields() << errinfo_comment("Import State: Missing state fields!"));
}
}
void ImportTest::importTransaction(json_spirit::mObject& _o)
@ -285,7 +237,7 @@ void ImportTest::importTransaction(json_spirit::mObject& _o)
}
}
void ImportTest::checkExpectedState(State const& _stateExpect, State const& _statePost, stateOptionsMap const _expectedStateOptions, WhenError _throw)
void ImportTest::compareStates(State const& _stateExpect, State const& _statePost, AccountMaskMap const _expectedStateOptions, WhenError _throw)
{
#define CHECK(a,b) \
{ \
@ -300,7 +252,7 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta
CHECK(_statePost.addressInUse(a.first), "Filling Test: " << a.first << " missing expected address!");
if (_statePost.addressInUse(a.first))
{
ImportStateOptions addressOptions(true);
AccountMask addressOptions(true);
if(_expectedStateOptions.size())
{
try
@ -314,15 +266,15 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta
}
}
if (addressOptions.m_bHasBalance)
if (addressOptions.hasBalance())
CHECK((_stateExpect.balance(a.first) == _statePost.balance(a.first)),
"Check State: " << a.first << ": incorrect balance " << _statePost.balance(a.first) << ", expected " << _stateExpect.balance(a.first));
if (addressOptions.m_bHasNonce)
if (addressOptions.hasNonce())
CHECK((_stateExpect.transactionsFrom(a.first) == _statePost.transactionsFrom(a.first)),
"Check State: " << a.first << ": incorrect nonce " << _statePost.transactionsFrom(a.first) << ", expected " << _stateExpect.transactionsFrom(a.first));
if (addressOptions.m_bHasStorage)
if (addressOptions.hasStorage())
{
unordered_map<u256, u256> stateStorage = _statePost.storage(a.first);
for (auto const& s: _stateExpect.storage(a.first))
@ -336,52 +288,52 @@ void ImportTest::checkExpectedState(State const& _stateExpect, State const& _sta
"Check State: " << a.first << ": incorrect storage [" << s.first << "] = " << toHex(s.second) << ", expected [" << s.first << "] = " << toHex(stateStorage[s.first]));
}
if (addressOptions.m_bHasCode)
if (addressOptions.hasCode())
CHECK((_stateExpect.code(a.first) == _statePost.code(a.first)),
"Check State: " << a.first << ": incorrect code '" << toHex(_statePost.code(a.first)) << "', expected '" << toHex(_stateExpect.code(a.first)) << "'");
}
}
}
void ImportTest::exportTest(bytes const& _output, State const& _statePost)
void ImportTest::exportTest(bytes const& _output)
{
// export output
m_TestObject["out"] = (_output.size() > 4096 && !Options::get().fulloutput) ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add);
m_testObject["out"] = (_output.size() > 4096 && !Options::get().fulloutput) ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add);
// compare expected output with post output
if (m_TestObject.count("expectOut") > 0)
if (m_testObject.count("expectOut") > 0)
{
std::string warning = "Check State: Error! Unexpected output: " + m_TestObject["out"].get_str() + " Expected: " + m_TestObject["expectOut"].get_str();
std::string warning = "Check State: Error! Unexpected output: " + m_testObject["out"].get_str() + " Expected: " + m_testObject["expectOut"].get_str();
if (Options::get().checkState)
{TBOOST_CHECK_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning);}
{TBOOST_CHECK_MESSAGE((m_testObject["out"].get_str() == m_testObject["expectOut"].get_str()), warning);}
else
TBOOST_WARN_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning);
TBOOST_WARN_MESSAGE((m_testObject["out"].get_str() == m_testObject["expectOut"].get_str()), warning);
m_TestObject.erase(m_TestObject.find("expectOut"));
m_testObject.erase(m_testObject.find("expectOut"));
}
// export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());
// export logs
m_testObject["logs"] = exportLog(m_logs);
// compare expected state with post state
if (m_TestObject.count("expect") > 0)
if (m_testObject.count("expect") > 0)
{
stateOptionsMap stateMap;
eth::AccountMaskMap stateMap;
State expectState(OverlayDB(), eth::BaseState::Empty);
importState(m_TestObject["expect"].get_obj(), expectState, stateMap);
checkExpectedState(expectState, _statePost, stateMap, Options::get().checkState ? WhenError::Throw : WhenError::DontThrow);
m_TestObject.erase(m_TestObject.find("expect"));
importState(m_testObject["expect"].get_obj(), expectState, stateMap);
compareStates(expectState, m_statePost, stateMap, Options::get().checkState ? WhenError::Throw : WhenError::DontThrow);
m_testObject.erase(m_testObject.find("expect"));
}
// export post state
m_TestObject["post"] = fillJsonWithState(_statePost);
m_TestObject["postStateRoot"] = toHex(_statePost.rootHash().asBytes());
m_testObject["post"] = fillJsonWithState(m_statePost);
m_testObject["postStateRoot"] = toHex(m_statePost.rootHash().asBytes());
// export pre state
m_TestObject["pre"] = fillJsonWithState(m_statePre);
m_TestObject["env"] = makeAllFieldsHex(m_TestObject["env"].get_obj());
m_TestObject["transaction"] = makeAllFieldsHex(m_TestObject["transaction"].get_obj());
m_testObject["pre"] = fillJsonWithState(m_statePre);
m_testObject["env"] = makeAllFieldsHex(m_testObject["env"].get_obj());
m_testObject["transaction"] = makeAllFieldsHex(m_testObject["transaction"].get_obj());
}
json_spirit::mObject fillJsonWithTransaction(Transaction _txn)

40
test/TestHelper.h

@ -32,6 +32,7 @@
#include <libevm/ExtVMFace.h>
#include <libtestutils/Common.h>
#ifdef NOBOOST
#define TBOOST_REQUIRE(arg) if(arg == false) throw dev::Exception();
#define TBOOST_REQUIRE_EQUAL(arg1, arg2) if(arg1 != arg2) throw dev::Exception();
@ -62,7 +63,7 @@ class State;
void mine(Client& c, int numBlocks);
void connectClients(Client& c1, Client& c2);
void mine(State& _s, BlockChain const& _bc);
void mine(Block& _s, BlockChain const& _bc);
void mine(Ethash::BlockHeader& _bi);
}
@ -122,45 +123,44 @@ namespace test
} \
while (0)
struct ImportStateOptions
enum class testType
{
ImportStateOptions(bool _bSetAll = false):m_bHasBalance(_bSetAll), m_bHasNonce(_bSetAll), m_bHasCode(_bSetAll), m_bHasStorage(_bSetAll) {}
bool isAllSet() {return m_bHasBalance && m_bHasNonce && m_bHasCode && m_bHasStorage;}
bool m_bHasBalance;
bool m_bHasNonce;
bool m_bHasCode;
bool m_bHasStorage;
StateTests,
BlockChainTests,
Other
};
typedef std::map<Address, ImportStateOptions> stateOptionsMap;
class ImportTest
{
public:
ImportTest(json_spirit::mObject& _o): m_TestObject(_o) {}
ImportTest(json_spirit::mObject& _o, bool isFiller);
ImportTest(json_spirit::mObject& _o, bool isFiller, testType testTemplate = testType::StateTests);
// imports
void importEnv(json_spirit::mObject& _o);
static void importState(json_spirit::mObject& _o, eth::State& _state);
static void importState(json_spirit::mObject& _o, eth::State& _state, stateOptionsMap& _stateOptionsMap);
static void importState(json_spirit::mObject& _o, eth::State& _state, eth::AccountMaskMap& o_mask);
void importTransaction(json_spirit::mObject& _o);
static json_spirit::mObject& makeAllFieldsHex(json_spirit::mObject& _o);
void exportTest(bytes const& _output, eth::State const& _statePost);
static void checkExpectedState(eth::State const& _stateExpect, eth::State const& _statePost, stateOptionsMap const _expectedStateOptions = stateOptionsMap(), WhenError _throw = WhenError::Throw);
bytes executeTest();
void exportTest(bytes const& _output);
static void compareStates(eth::State const& _stateExpect, eth::State const& _statePost, eth::AccountMaskMap const _expectedStateOptions = eth::AccountMaskMap(), WhenError _throw = WhenError::Throw);
eth::State m_statePre;
eth::State m_statePost;
eth::ExtVMFace m_environment;
eth::Transaction m_transaction;
eth::EnvInfo m_envInfo;
eth::Transaction m_transaction;
eth::LogEntries m_logs;
eth::LogEntries m_logsExpected;
private:
json_spirit::mObject& m_TestObject;
json_spirit::mObject& m_testObject;
};
class ZeroGasPricer: public eth::GasPricer
{
protected:
u256 ask(eth::State const&) const override { return 0; }
u256 ask(eth::Block const&) const override { return 0; }
u256 bid(eth::TransactionPriority = eth::TransactionPriority::Medium) const override { return 0; }
};
@ -205,7 +205,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin);
void doBlockchainTests(json_spirit::mValue& _v, bool _fillin);
void doRlpTests(json_spirit::mValue& v, bool _fillin);
template<typename mapType>
/*template<typename mapType>
void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs)
{
for (auto& resultPair : _resultAddrs)
@ -216,7 +216,7 @@ void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs)
TBOOST_ERROR("Missing result address " << resultAddr);
}
TBOOST_CHECK((_expectedAddrs == _resultAddrs));
}
}*/
class Options
{

4
test/TestUtils.cpp

@ -101,7 +101,9 @@ void ClientBaseFixture::enumerateClients(std::function<void(Json::Value const&,
{
enumerateBlockchains([&callback](Json::Value const& _json, BlockChain const& _bc, State _state) -> void
{
FixedClient client(_bc, _state);
cerr << "void ClientBaseFixture::enumerateClients. FixedClient now accepts block not sate!" << endl;
_state.commit(); //unused variable. remove this line
FixedClient client(_bc, eth::Block {});
callback(_json, client);
});
}

418
test/contracts/AuctionRegistrar.cpp

@ -0,0 +1,418 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2015
* Tests for a fixed fee registrar contract.
*/
#include <string>
#include <tuple>
#include <boost/test/unit_test.hpp>
#include <libdevcore/Hash.h>
#include <libethcore/ABI.h>
#include <test/libsolidity/solidityExecutionFramework.h>
using namespace std;
namespace dev
{
namespace solidity
{
namespace test
{
namespace
{
static char const* registrarCode = R"DELIMITER(
//sol
contract NameRegister {
function addr(string _name) constant returns (address o_owner);
function name(address _owner) constant returns (string o_name);
}
contract Registrar is NameRegister {
event Changed(string indexed name);
event PrimaryChanged(string indexed name, address indexed addr);
function owner(string _name) constant returns (address o_owner);
function addr(string _name) constant returns (address o_address);
function subRegistrar(string _name) constant returns (address o_subRegistrar);
function content(string _name) constant returns (bytes32 o_content);
function name(address _owner) constant returns (string o_name);
}
contract AuctionSystem {
event AuctionEnded(string indexed _name, address _winner);
event NewBid(string indexed _name, address _bidder, uint _value);
/// Function that is called once an auction ends.
function onAuctionEnd(string _name) internal;
function bid(string _name, address _bidder, uint _value) internal {
var auction = m_auctions[_name];
if (auction.endDate > 0 && now > auction.endDate)
{
AuctionEnded(_name, auction.highestBidder);
onAuctionEnd(_name);
delete m_auctions[_name];
return;
}
if (msg.value > auction.highestBid)
{
// new bid on auction
auction.secondHighestBid = auction.highestBid;
auction.sumOfBids += _value;
auction.highestBid = _value;
auction.highestBidder = _bidder;
auction.endDate = now + c_biddingTime;
NewBid(_name, _bidder, _value);
}
}
uint constant c_biddingTime = 7 days;
struct Auction {
address highestBidder;
uint highestBid;
uint secondHighestBid;
uint sumOfBids;
uint endDate;
}
mapping(string => Auction) m_auctions;
}
contract GlobalRegistrar is Registrar, AuctionSystem {
struct Record {
address owner;
address primary;
address subRegistrar;
bytes32 content;
uint renewalDate;
}
uint constant c_renewalInterval = 1 years;
uint constant c_freeBytes = 12;
function Registrar() {
// TODO: Populate with hall-of-fame.
}
function() {
// prevent people from just sending funds to the registrar
__throw();
}
function onAuctionEnd(string _name) internal {
var auction = m_auctions[_name];
var record = m_toRecord[_name];
if (record.owner != 0)
record.owner.send(auction.sumOfBids - auction.highestBid / 100);
else
auction.highestBidder.send(auction.highestBid - auction.secondHighestBid);
record.renewalDate = now + c_renewalInterval;
record.owner = auction.highestBidder;
Changed(_name);
}
function reserve(string _name) external {
if (bytes(_name).length == 0)
__throw();
bool needAuction = requiresAuction(_name);
if (needAuction)
{
if (now < m_toRecord[_name].renewalDate)
__throw();
bid(_name, msg.sender, msg.value);
}
else
{
Record record = m_toRecord[_name];
if (record.owner != 0)
__throw();
m_toRecord[_name].owner = msg.sender;
Changed(_name);
}
}
function requiresAuction(string _name) internal returns (bool) {
return bytes(_name).length < c_freeBytes;
}
modifier onlyrecordowner(string _name) { if (m_toRecord[_name].owner == msg.sender) _ }
function transfer(string _name, address _newOwner) onlyrecordowner(_name) {
m_toRecord[_name].owner = _newOwner;
Changed(_name);
}
function disown(string _name) onlyrecordowner(_name) {
if (stringsEqual(m_toName[m_toRecord[_name].primary], _name))
{
PrimaryChanged(_name, m_toRecord[_name].primary);
m_toName[m_toRecord[_name].primary] = "";
}
delete m_toRecord[_name];
Changed(_name);
}
function setAddress(string _name, address _a, bool _primary) onlyrecordowner(_name) {
m_toRecord[_name].primary = _a;
if (_primary)
{
PrimaryChanged(_name, _a);
m_toName[_a] = _name;
}
Changed(_name);
}
function setSubRegistrar(string _name, address _registrar) onlyrecordowner(_name) {
m_toRecord[_name].subRegistrar = _registrar;
Changed(_name);
}
function setContent(string _name, bytes32 _content) onlyrecordowner(_name) {
m_toRecord[_name].content = _content;
Changed(_name);
}
function stringsEqual(string storage _a, string memory _b) internal returns (bool) {
bytes storage a = bytes(_a);
bytes memory b = bytes(_b);
if (a.length != b.length)
return false;
// @todo unroll this loop
for (uint i = 0; i < a.length; i ++)
if (a[i] != b[i])
return false;
return true;
}
function owner(string _name) constant returns (address) { return m_toRecord[_name].owner; }
function addr(string _name) constant returns (address) { return m_toRecord[_name].primary; }
function subRegistrar(string _name) constant returns (address) { return m_toRecord[_name].subRegistrar; }
function content(string _name) constant returns (bytes32) { return m_toRecord[_name].content; }
function name(address _addr) constant returns (string o_name) { return m_toName[_addr]; }
function __throw() internal {
// workaround until we have "throw"
uint[] x; x[1];
}
mapping (address => string) m_toName;
mapping (string => Record) m_toRecord;
}
)DELIMITER";
static unique_ptr<bytes> s_compiledRegistrar;
class AuctionRegistrarTestFramework: public ExecutionFramework
{
protected:
void deployRegistrar()
{
if (!s_compiledRegistrar)
{
m_optimize = true;
m_compiler.reset(false, m_addStandardSources);
m_compiler.addSource("", registrarCode);
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
s_compiledRegistrar.reset(new bytes(m_compiler.getBytecode("GlobalRegistrar")));
}
sendMessage(*s_compiledRegistrar, true);
BOOST_REQUIRE(!m_output.empty());
}
using ContractInterface = ExecutionFramework::ContractInterface;
class RegistrarInterface: public ContractInterface
{
public:
RegistrarInterface(ExecutionFramework& _framework): ContractInterface(_framework) {}
void reserve(string const& _name)
{
callString("reserve", _name);
}
u160 owner(string const& _name)
{
return callStringReturnsAddress("owner", _name);
}
void setAddress(string const& _name, u160 const& _address, bool _primary)
{
callStringAddressBool("setAddress", _name, _address, _primary);
}
u160 addr(string const& _name)
{
return callStringReturnsAddress("addr", _name);
}
string name(u160 const& _addr)
{
return callAddressReturnsString("name", _addr);
}
void setSubRegistrar(string const& _name, u160 const& _address)
{
callStringAddress("setSubRegistrar", _name, _address);
}
u160 subRegistrar(string const& _name)
{
return callStringReturnsAddress("subRegistrar", _name);
}
void setContent(string const& _name, h256 const& _content)
{
callStringBytes32("setContent", _name, _content);
}
h256 content(string const& _name)
{
return callStringReturnsBytes32("content", _name);
}
void transfer(string const& _name, u160 const& _target)
{
return callStringAddress("transfer", _name, _target);
}
void disown(string const& _name)
{
return callString("disown", _name);
}
};
};
}
/// This is a test suite that tests optimised code!
BOOST_FIXTURE_TEST_SUITE(SolidityAuctionRegistrar, AuctionRegistrarTestFramework)
BOOST_AUTO_TEST_CASE(creation)
{
deployRegistrar();
}
BOOST_AUTO_TEST_CASE(reserve)
{
// Test that reserving works for long strings
deployRegistrar();
vector<string> names{"abcabcabcabcabc", "defdefdefdefdef", "ghighighighighighighighighighighighighighighi"};
m_sender = Address(0x123);
RegistrarInterface registrar(*this);
// should not work
registrar.reserve("");
BOOST_CHECK_EQUAL(registrar.owner(""), u160(0));
for (auto const& name: names)
{
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), u160(0x123));
}
}
BOOST_AUTO_TEST_CASE(double_reserve_long)
{
// Test that it is not possible to re-reserve from a different address.
deployRegistrar();
string name = "abcabcabcabcabcabcabcabcabcabca";
m_sender = Address(0x123);
RegistrarInterface registrar(*this);
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), u160(0x123));
m_sender = Address(0x124);
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), u160(0x123));
}
BOOST_AUTO_TEST_CASE(properties)
{
// Test setting and retrieving the various properties works.
deployRegistrar();
RegistrarInterface registrar(*this);
string names[] = {"abcaeouoeuaoeuaoeu", "defncboagufra,fui", "ghagpyajfbcuajouhaeoi"};
size_t addr = 0x9872543;
for (string const& name: names)
{
addr++;
size_t sender = addr + 10007;
m_sender = Address(sender);
// setting by sender works
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), u160(sender));
registrar.setAddress(name, addr, true);
BOOST_CHECK_EQUAL(registrar.addr(name), u160(addr));
registrar.setSubRegistrar(name, addr + 20);
BOOST_CHECK_EQUAL(registrar.subRegistrar(name), u160(addr + 20));
registrar.setContent(name, h256(u256(addr + 90)));
BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90)));
// but not by someone else
m_sender = Address(h256(addr + 10007 - 1));
BOOST_CHECK_EQUAL(registrar.owner(name), u160(sender));
registrar.setAddress(name, addr + 1, true);
BOOST_CHECK_EQUAL(registrar.addr(name), u160(addr));
registrar.setSubRegistrar(name, addr + 20 + 1);
BOOST_CHECK_EQUAL(registrar.subRegistrar(name), u160(addr + 20));
registrar.setContent(name, h256(u256(addr + 90 + 1)));
BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90)));
}
}
BOOST_AUTO_TEST_CASE(transfer)
{
deployRegistrar();
string name = "abcaoeguaoucaeoduceo";
m_sender = Address(0x123);
RegistrarInterface registrar(*this);
registrar.reserve(name);
registrar.setContent(name, h256(u256(123)));
registrar.transfer(name, u160(555));
BOOST_CHECK_EQUAL(registrar.owner(name), u160(555));
BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(123)));
}
BOOST_AUTO_TEST_CASE(disown)
{
deployRegistrar();
string name = "abcaoeguaoucaeoduceo";
m_sender = Address(0x123);
RegistrarInterface registrar(*this);
registrar.reserve(name);
registrar.setContent(name, h256(u256(123)));
registrar.setAddress(name, u160(124), true);
registrar.setSubRegistrar(name, u160(125));
// someone else tries disowning
m_sender = Address(0x128);
registrar.disown(name);
BOOST_CHECK_EQUAL(registrar.owner(name), 0x123);
m_sender = Address(0x123);
registrar.disown(name);
BOOST_CHECK_EQUAL(registrar.owner(name), 0);
BOOST_CHECK_EQUAL(registrar.addr(name), 0);
BOOST_CHECK_EQUAL(registrar.subRegistrar(name), 0);
BOOST_CHECK_EQUAL(registrar.content(name), h256());
}
//@todo:
// - reverse lookup
// - actual auction
BOOST_AUTO_TEST_SUITE_END()
}
}
} // end namespaces

5
test/contracts/FixedFeeRegistrar.cpp

@ -35,6 +35,9 @@ namespace solidity
namespace test
{
namespace
{
static char const* registrarCode = R"DELIMITER(
//sol FixedFeeRegistrar
// Simple global registrar with fixed-fee reservations.
@ -130,6 +133,8 @@ protected:
u256 const m_fee = u256("69000000000000000000");
};
}
/// This is a test suite that tests optimised code!
BOOST_FIXTURE_TEST_SUITE(SolidityFixedFeeRegistrar, RegistrarTestFramework)

4
test/contracts/Wallet.cpp

@ -545,7 +545,7 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer)
// 4 owners, set required to 3
BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
// check that balance is and stays zero at destination address
h256 opHash("8f27f478ebcfaf28b0c354f4809ace8087000d668b89c8bc3b1b608bfdbe6654");
h256 opHash("6244b4fa93f73e09db0ae52750095ca0364a76b72bc01723c97011fcb876cc9e");
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
m_sender = Address(0x12);
BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
@ -596,7 +596,7 @@ BOOST_AUTO_TEST_CASE(revoke_transaction)
BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
// create a transaction
Address deployer = m_sender;
h256 opHash("8f27f478ebcfaf28b0c354f4809ace8087000d668b89c8bc3b1b608bfdbe6654");
h256 opHash("6244b4fa93f73e09db0ae52750095ca0364a76b72bc01723c97011fcb876cc9e");
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
m_sender = Address(0x12);
BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));

23
test/fuzzTesting/CMakeLists.txt

@ -8,32 +8,11 @@ include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libethereum/transaction.cpp" "../libethereum/state.cpp" "../libevm/vm.cpp" "../libethereum/blockchain.cpp" "../libdevcore/rlp.cpp")
add_executable(createRandomVMTest "./createRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp")
add_executable(createRandomStateTest "./createRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp")
add_executable(checkRandomVMTest "./checkRandomVMTest.cpp" "../libevm/vm.cpp" "../TestHelper.cpp" "../Stats.cpp" )
add_executable(checkRandomStateTest "./checkRandomStateTest.cpp" "../TestHelper.cpp" "../Stats.cpp")
add_executable(createRandomTest "./createRandomTest.cpp" "../TestHelper.cpp" "../Stats.cpp" "fuzzHelper.cpp" "../libdevcore/rlp.cpp" "../libethereum/transactionTests.cpp" "../libethereum/state.cpp" "../libevm/vm.cpp" "../libethereum/blockchain.cpp")
list(APPEND SRCS "./fuzzHelper.cpp")
add_sources(${SRCS})
target_link_libraries(createRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomVMTest ethereum)
target_link_libraries(createRandomVMTest ethcore)
target_link_libraries(createRandomVMTest testutils)
target_link_libraries(createRandomStateTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomStateTest ethereum)
target_link_libraries(createRandomStateTest ethcore)
target_link_libraries(createRandomStateTest testutils)
target_link_libraries(checkRandomVMTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(checkRandomVMTest ethereum)
target_link_libraries(checkRandomVMTest ethcore)
target_link_libraries(checkRandomVMTest testutils)
target_link_libraries(checkRandomStateTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(checkRandomStateTest ethereum)
target_link_libraries(checkRandomStateTest ethcore)
target_link_libraries(checkRandomStateTest testutils)
target_link_libraries(createRandomTest ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
target_link_libraries(createRandomTest ethereum)
target_link_libraries(createRandomTest ethcore)

2
test/libethcore/keymanager.cpp

@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(KeyManagerKeysFile)
TransientDirectory tmpDir;
km.setKeysFile(tmpDir.path());
BOOST_CHECK(!km.exists());
BOOST_CHECK_THROW(km.create(password), FileError);
BOOST_CHECK_THROW(km.create(password), boost::filesystem::filesystem_error);
km.setKeysFile(tmpDir.path() + "/notExistingDir/keysFile.json");
BOOST_CHECK_NO_THROW(km.create(password));
BOOST_CHECK(km.exists());

59
test/libethereum/BlockchainTestsFiller/bcInvalidHeaderTestFiller.json

@ -1120,5 +1120,64 @@
]
}
]
},
"ExtraData1024" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "131072",
"extraData" : "0x42",
"gasLimit" : "3141592",
"gasUsed" : "0",
"mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"nonce" : "0x0102030405060708",
"number" : "0",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a",
"timestamp" : "0x54c98c81",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"expect" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "5000000100"
}
},
"pre" : {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000",
"nonce" : "0",
"code" : "",
"storage": {}
},
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "100",
"nonce" : "0",
"code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG1 0 32 0) }",
"storage": {}
}
},
"blocks" : [
{
"blockHeader" : {
"extraData" : "0x01020304050607080910111213141516171819202122232410000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000"
},
"transactions" : [
{
"data" : "",
"gasLimit" : "50000",
"gasPrice" : "10",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "5000000000"
}
],
"uncleHeaders" : [
]
}
]
}
}

4
test/libethereum/BlockchainTestsFiller/bcValidBlockTestFiller.json

@ -889,7 +889,7 @@
]
},
"ExtraData1024" : {
"ExtraData32" : {
"genesisBlockHeader" : {
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
@ -929,7 +929,7 @@
"blocks" : [
{
"blockHeader" : {
"extraData" : "0x01020304050607080910111213141516171819202122232410000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000"
"extraData" : "0x0102030405060708091011121314151617181920212223242526272829303132"
},
"transactions" : [
{

2
test/libethereum/ClientBase.cpp

@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(blocks)
h256 expectedBlockInfoTransactionsRoot = h256(fromHex(_b["transactionsTrie"].asString()));
h256 expectedBlockInfoUncldeHash = h256(fromHex(_b["uncleHash"].asString()));
ETH_CHECK_EQUAL(expectedBlockInfoBloom, _blockInfo.logBloom());
ETH_CHECK_EQUAL(expectedBlockInfoCoinbase, _blockInfo.coinbaseAddress());
ETH_CHECK_EQUAL(expectedBlockInfoCoinbase, _blockInfo.beneficiary());
ETH_CHECK_EQUAL(expectedBlockInfoDifficulty, _blockInfo.difficulty());
ETH_CHECK_EQUAL_COLLECTIONS(
expectedBlockInfoExtraData.begin(),

1
test/libethereum/StateTestsFiller/stExampleFiller.json

@ -14,7 +14,6 @@
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000100000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
"0x" : "0x02"
}

169
test/libethereum/Transaction.cpp

@ -0,0 +1,169 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Transaction.cpp
* @author Dmitrii Khokhlov <winsvega@mail.ru>
* @date 2015
* Transaaction test functions.
*/
#include "test/TestHelper.h"
#include <libethcore/Exceptions.h>
#include <libevm/VMFace.h>
#include <libethcore/Common.h>
using namespace dev;
using namespace eth;
BOOST_AUTO_TEST_SUITE(libethereum)
BOOST_AUTO_TEST_CASE(TransactionGasRequired)
{
Transaction tr(fromHex("0xf86d800182521c94095e7baea6a6c7c4c2dfeb977efac326af552d870a8e0358ac39584bc98a7c979f984b031ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804"), CheckTransaction::None);
BOOST_CHECK_MESSAGE(tr.gasRequired() == 21952, "Transaction::GasRequired() has changed!");
}
BOOST_AUTO_TEST_CASE(TransactionConstructor)
{
bool wasException = false;
try
{
Transaction(fromHex("0xf86d800182521c94095e7baea6a6c7c4c2dfeb977efac326af552d870a8e0358ac39584bc98a7c979f984b031ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804"), CheckTransaction::Everything);
}
catch (OutOfGasIntrinsic)
{
wasException = true;
}
catch (Exception)
{
BOOST_ERROR("Exception thrown but expected OutOfGasIntrinsic instead");
}
BOOST_CHECK_MESSAGE(wasException, "Expected OutOfGasIntrinsic exception to be thrown at TransactionConstructor test");
}
BOOST_AUTO_TEST_CASE(ExecutionResultOutput)
{
std::stringstream buffer;
ExecutionResult exRes;
exRes.gasUsed = u256("12345");
exRes.newAddress = Address("a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
exRes.output = fromHex("001122334455");
buffer << exRes;
BOOST_CHECK_MESSAGE(buffer.str() == "{12345, a94f5374fce5edbc8e2a8697c15331677e6ebf0b, 001122334455}", "Error ExecutionResultOutput");
}
BOOST_AUTO_TEST_CASE(transactionExceptionOutput)
{
std::stringstream buffer;
buffer << TransactionException::BadInstruction;
BOOST_CHECK_MESSAGE(buffer.str() == "BadInstruction", "Error output TransactionException::BadInstruction");
buffer.str(std::string());
buffer << TransactionException::None;
BOOST_CHECK_MESSAGE(buffer.str() == "None", "Error output TransactionException::None");
buffer.str(std::string());
buffer << TransactionException::BadRLP;
BOOST_CHECK_MESSAGE(buffer.str() == "BadRLP", "Error output TransactionException::BadRLP");
buffer.str(std::string());
buffer << TransactionException::InvalidFormat;
BOOST_CHECK_MESSAGE(buffer.str() == "InvalidFormat", "Error output TransactionException::InvalidFormat");
buffer.str(std::string());
buffer << TransactionException::OutOfGasIntrinsic;
BOOST_CHECK_MESSAGE(buffer.str() == "OutOfGasIntrinsic", "Error output TransactionException::OutOfGasIntrinsic");
buffer.str(std::string());
buffer << TransactionException::InvalidSignature;
BOOST_CHECK_MESSAGE(buffer.str() == "InvalidSignature", "Error output TransactionException::InvalidSignature");
buffer.str(std::string());
buffer << TransactionException::InvalidNonce;
BOOST_CHECK_MESSAGE(buffer.str() == "InvalidNonce", "Error output TransactionException::InvalidNonce");
buffer.str(std::string());
buffer << TransactionException::NotEnoughCash;
BOOST_CHECK_MESSAGE(buffer.str() == "NotEnoughCash", "Error output TransactionException::NotEnoughCash");
buffer.str(std::string());
buffer << TransactionException::OutOfGasBase;
BOOST_CHECK_MESSAGE(buffer.str() == "OutOfGasBase", "Error output TransactionException::OutOfGasBase");
buffer.str(std::string());
buffer << TransactionException::BlockGasLimitReached;
BOOST_CHECK_MESSAGE(buffer.str() == "BlockGasLimitReached", "Error output TransactionException::BlockGasLimitReached");
buffer.str(std::string());
buffer << TransactionException::BadInstruction;
BOOST_CHECK_MESSAGE(buffer.str() == "BadInstruction", "Error output TransactionException::BadInstruction");
buffer.str(std::string());
buffer << TransactionException::BadJumpDestination;
BOOST_CHECK_MESSAGE(buffer.str() == "BadJumpDestination", "Error output TransactionException::BadJumpDestination");
buffer.str(std::string());
buffer << TransactionException::OutOfGas;
BOOST_CHECK_MESSAGE(buffer.str() == "OutOfGas", "Error output TransactionException::OutOfGas");
buffer.str(std::string());
buffer << TransactionException::OutOfStack;
BOOST_CHECK_MESSAGE(buffer.str() == "OutOfStack", "Error output TransactionException::OutOfStack");
buffer.str(std::string());
buffer << TransactionException::StackUnderflow;
BOOST_CHECK_MESSAGE(buffer.str() == "StackUnderflow", "Error output TransactionException::StackUnderflow");
buffer.str(std::string());
buffer << TransactionException(-1);
BOOST_CHECK_MESSAGE(buffer.str() == "Unknown", "Error output TransactionException::StackUnderflow");
buffer.str(std::string());
}
BOOST_AUTO_TEST_CASE(toTransactionExceptionConvert)
{
RLPException rlpEx("exception");//toTransactionException(*(dynamic_cast<Exception*>
BOOST_CHECK_MESSAGE(toTransactionException(rlpEx) == TransactionException::BadRLP, "RLPException !=> TransactionException");
OutOfGasIntrinsic oogEx;
BOOST_CHECK_MESSAGE(toTransactionException(oogEx) == TransactionException::OutOfGasIntrinsic, "OutOfGasIntrinsic !=> TransactionException");
InvalidSignature sigEx;
BOOST_CHECK_MESSAGE(toTransactionException(sigEx) == TransactionException::InvalidSignature, "InvalidSignature !=> TransactionException");
OutOfGasBase oogbEx;
BOOST_CHECK_MESSAGE(toTransactionException(oogbEx) == TransactionException::OutOfGasBase, "OutOfGasBase !=> TransactionException");
InvalidNonce nonceEx;
BOOST_CHECK_MESSAGE(toTransactionException(nonceEx) == TransactionException::InvalidNonce, "InvalidNonce !=> TransactionException");
NotEnoughCash cashEx;
BOOST_CHECK_MESSAGE(toTransactionException(cashEx) == TransactionException::NotEnoughCash, "NotEnoughCash !=> TransactionException");
BlockGasLimitReached blGasEx;
BOOST_CHECK_MESSAGE(toTransactionException(blGasEx) == TransactionException::BlockGasLimitReached, "BlockGasLimitReached !=> TransactionException");
BadInstruction badInsEx;
BOOST_CHECK_MESSAGE(toTransactionException(badInsEx) == TransactionException::BadInstruction, "BadInstruction !=> TransactionException");
BadJumpDestination badJumpEx;
BOOST_CHECK_MESSAGE(toTransactionException(badJumpEx) == TransactionException::BadJumpDestination, "BadJumpDestination !=> TransactionException");
OutOfGas oogEx2;
BOOST_CHECK_MESSAGE(toTransactionException(oogEx2) == TransactionException::OutOfGas, "OutOfGas !=> TransactionException");
OutOfStack oosEx;
BOOST_CHECK_MESSAGE(toTransactionException(oosEx) == TransactionException::OutOfStack, "OutOfStack !=> TransactionException");
StackUnderflow stackEx;
BOOST_CHECK_MESSAGE(toTransactionException(stackEx) == TransactionException::StackUnderflow, "StackUnderflow !=> TransactionException");
Exception notEx;
BOOST_CHECK_MESSAGE(toTransactionException(notEx) == TransactionException::Unknown, "Unexpected should be TransactionException::Unknown");
}
BOOST_AUTO_TEST_SUITE_END()

305
test/libethereum/blockchain.cpp

@ -27,6 +27,7 @@
#include <libethereum/CanonBlockChain.h>
#include <libethereum/TransactionQueue.h>
#include <test/TestHelper.h>
#include <libethereum/Block.h>
using namespace std;
using namespace json_spirit;
@ -50,6 +51,14 @@ void overwriteBlockHeader(BlockHeader& _current_BlockHeader, mObject& _blObj, co
void updatePoW(BlockHeader& _bi);
mArray importUncles(mObject const& _blObj, vector<BlockHeader>& _vBiUncles, vector<BlockHeader> const& _vBiBlocks, std::vector<blockSet> _blockSet);
//void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
//{
// if (_fillin == false)
// _v.get_bool();
// cerr << "BlockChainTests not implemented!" << endl;
//}
void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
{
for (auto& i: _v.get_obj())
@ -63,20 +72,20 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
cerr << i.first << endl;
TBOOST_REQUIRE(o.count("genesisBlockHeader"));
TBOOST_REQUIRE(o.count("pre"));
ImportTest importer(o["pre"].get_obj());
ImportTest importer(o, _fillin, testType::BlockChainTests);
TransientDirectory td_stateDB_tmp;
BlockHeader biGenesisBlock = constructBlock(o["genesisBlockHeader"].get_obj(), h256{});
State trueState(OverlayDB(State::openDB(td_stateDB_tmp.path(), h256{}, WithExisting::Kill)), BaseState::Empty, biGenesisBlock.coinbaseAddress());
//Imported blocks from the start
std::vector<blockSet> blockSets;
State trueState(OverlayDB(State::openDB(td_stateDB_tmp.path(), h256{}, WithExisting::Kill)), BaseState::Empty);
importer.importState(o["pre"].get_obj(), trueState);
o["pre"] = fillJsonWithState(trueState);
o["pre"] = fillJsonWithState(trueState); //convert all fields to hex
trueState.commit();
//Imported blocks from the start
std::vector<blockSet> blockSets; //Block(bytes) => UncleList(Blocks(bytes))
if (_fillin)
biGenesisBlock = constructBlock(o["genesisBlockHeader"].get_obj(), trueState.rootHash());
else
@ -98,7 +107,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
// construct true blockchain
TransientDirectory td;
FullBlockChain<Ethash> trueBc(rlpGenesisBlock.out(), StateDefinition(), td.path(), WithExisting::Kill);
FullBlockChain<Ethash> trueBc(rlpGenesisBlock.out(), AccountMap(), td.path(), WithExisting::Kill);
if (_fillin)
{
@ -126,13 +135,15 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
vBiBlocks.push_back(biGenesisBlock);
TransientDirectory td_stateDB, td_bc;
FullBlockChain<Ethash> bc(rlpGenesisBlock.out(), StateDefinition(), td_bc.path(), WithExisting::Kill);
State state(OverlayDB(State::openDB(td_stateDB.path(), h256{}, WithExisting::Kill)), BaseState::Empty);
state.setAddress(biGenesisBlock.coinbaseAddress());
importer.importState(o["pre"].get_obj(), state);
FullBlockChain<Ethash> bc(rlpGenesisBlock.out(), AccountMap(), td_bc.path(), WithExisting::Kill);
OverlayDB database (State::openDB(td_stateDB.path(), h256{}, WithExisting::Kill));
State state(database, BaseState::Empty);
Block block(database, BaseState::Empty, biGenesisBlock.beneficiary());
state = importer.m_statePre;
state.commit();
state.sync(bc);
//import previous blocks
for (size_t i = 1; i < importBlockNumber; i++) //0 block is genesis
{
BlockQueue uncleQueue;
@ -141,11 +152,11 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
for (size_t j = 0; j < uncles.size(); j++)
uncleQueue.import(&uncles.at(j), false);
const bytes block = blockSets.at(i).first;
const bytes blockFromSet = blockSets.at(i).first;
bc.sync(uncleQueue, state.db(), 4);
bc.attemptImport(block, state.db());
vBiBlocks.push_back(BlockHeader(block));
state.sync(bc);
bc.attemptImport(blockFromSet, state.db());
vBiBlocks.push_back(BlockHeader(blockFromSet));
//state.sync(bc);
}
// get txs
@ -182,148 +193,149 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
{
cnote << "error in importing uncle! This produces an invalid block (May be by purpose for testing).";
}
}
}
bc.sync(uncleBlockQueue, state.db(), 4);
state.commitToMine(bc);
block.commitToSeal(bc);
//mine a new block on top of previously imported
try
{
state.sync(bc);
state.sync(bc, txs, gp);
mine(state, bc);
block.sync(bc);
block.sync(bc, txs, gp);
mine(block, bc);
}
catch (Exception const& _e)
{
cnote << "state sync or mining did throw an exception: " << diagnostic_information(_e);
cnote << "block sync or mining did throw an exception: " << diagnostic_information(_e);
return;
}
catch (std::exception const& _e)
{
cnote << "state sync or mining did throw an exception: " << _e.what();
cnote << "block sync or mining did throw an exception: " << _e.what();
return;
}
blObj["rlp"] = toHex(state.blockData(), 2, HexPrefix::Add);
//get valid transactions
Transactions txList;
for (auto const& txi: txs.topTransactions(std::numeric_limits<unsigned>::max()))
txList.push_back(txi);
blObj["transactions"] = writeTransactionsToJson(txList);
BlockHeader current_BlockHeader(state.blockData());
RLPStream uncleStream;
uncleStream.appendList(vBiUncles.size());
for (unsigned i = 0; i < vBiUncles.size(); ++i)
{
RLPStream uncleRlp;
vBiUncles[i].streamRLP(uncleRlp);
uncleStream.appendRaw(uncleRlp.out());
}
if (blObj.count("blockHeader"))
overwriteBlockHeader(current_BlockHeader, blObj, vBiBlocks[vBiBlocks.size()-1]);
if (blObj.count("blockHeader") && blObj["blockHeader"].get_obj().count("bruncle"))
current_BlockHeader.populateFromParent(vBiBlocks[vBiBlocks.size() -1]);
if (vBiUncles.size())
{
// update unclehash in case of invalid uncles
current_BlockHeader.setSha3Uncles(sha3(uncleStream.out()));
updatePoW(current_BlockHeader);
}
// write block header
mObject oBlockHeader;
writeBlockHeaderToJson(oBlockHeader, current_BlockHeader);
blObj["blockHeader"] = oBlockHeader;
vBiBlocks.push_back(current_BlockHeader);
// compare blocks from state and from rlp
RLPStream txStream;
txStream.appendList(txList.size());
for (unsigned i = 0; i < txList.size(); ++i)
{
RLPStream txrlp;
txList[i].streamRLP(txrlp);
txStream.appendRaw(txrlp.out());
}
RLPStream block2 = createFullBlockFromHeader(current_BlockHeader, txStream.out(), uncleStream.out());
blObj["rlp"] = toHex(block2.out(), 2, HexPrefix::Add);
if (sha3(RLP(state.blockData())[0].data()) != sha3(RLP(block2.out())[0].data()))
{
cnote << "block header mismatch state.blockData() vs updated state.info()\n";
cerr << toHex(state.blockData()) << "vs" << toHex(block2.out());
}
if (sha3(RLP(state.blockData())[1].data()) != sha3(RLP(block2.out())[1].data()))
cnote << "txs mismatch\n";
if (sha3(RLP(state.blockData())[2].data()) != sha3(RLP(block2.out())[2].data()))
cnote << "uncle list mismatch\n" << RLP(state.blockData())[2].data() << "\n" << RLP(block2.out())[2].data();
try
{
state.sync(bc);
bc.import(block2.out(), state.db());
state.sync(bc);
state.commit();
//there we get new blockchain status in state which could have more difficulty than we have in trueState
//attempt to import new block to the true blockchain
trueBc.sync(uncleBlockQueue, trueState.db(), 4);
trueBc.attemptImport(block2.out(), trueState.db());
trueState.sync(trueBc);
blockSet newBlock;
newBlock.first = block2.out();
newBlock.second = uncleBlockQueueList;
if (importBlockNumber < blockSets.size())
{
//make new correct history of imported blocks
blockSets[importBlockNumber] = newBlock;
for (size_t i = importBlockNumber + 1; i < blockSets.size(); i++)
blockSets.pop_back();
}
else
blockSets.push_back(newBlock);
}
// if exception is thrown, RLP is invalid and no blockHeader, Transaction list, or Uncle list should be given
catch (...)
{
cnote << "block is invalid!\n";
blObj.erase(blObj.find("blockHeader"));
blObj.erase(blObj.find("uncleHeaders"));
blObj.erase(blObj.find("transactions"));
}
blArray.push_back(blObj);
this_thread::sleep_for(chrono::seconds(1));
// blObj["rlp"] = toHex(state.blockData(), 2, HexPrefix::Add);
// //get valid transactions
// Transactions txList;
// for (auto const& txi: txs.topTransactions(std::numeric_limits<unsigned>::max()))
// txList.push_back(txi);
// blObj["transactions"] = writeTransactionsToJson(txList);
// BlockHeader current_BlockHeader = state.info();
// RLPStream uncleStream;
// uncleStream.appendList(vBiUncles.size());
// for (unsigned i = 0; i < vBiUncles.size(); ++i)
// {
// RLPStream uncleRlp;
// vBiUncles[i].streamRLP(uncleRlp);
// uncleStream.appendRaw(uncleRlp.out());
// }
// if (blObj.count("blockHeader"))
// overwriteBlockHeader(current_BlockHeader, blObj);
// if (blObj.count("blockHeader") && blObj["blockHeader"].get_obj().count("bruncle"))
// current_BlockHeader.populateFromParent(vBiBlocks[vBiBlocks.size() -1]);
// if (vBiUncles.size())
// {
// // update unclehash in case of invalid uncles
// current_BlockHeader.setSha3Uncles(sha3(uncleStream.out()));
// updatePoW(current_BlockHeader);
// }
// // write block header
// mObject oBlockHeader;
// writeBlockHeaderToJson(oBlockHeader, current_BlockHeader);
// blObj["blockHeader"] = oBlockHeader;
// vBiBlocks.push_back(current_BlockHeader);
// // compare blocks from state and from rlp
// RLPStream txStream;
// txStream.appendList(txList.size());
// for (unsigned i = 0; i < txList.size(); ++i)
// {
// RLPStream txrlp;
// txList[i].streamRLP(txrlp);
// txStream.appendRaw(txrlp.out());
// }
// RLPStream block2 = createFullBlockFromHeader(current_BlockHeader, txStream.out(), uncleStream.out());
// blObj["rlp"] = toHex(block2.out(), 2, HexPrefix::Add);
// if (sha3(RLP(state.blockData())[0].data()) != sha3(RLP(block2.out())[0].data()))
// {
// cnote << "block header mismatch state.blockData() vs updated state.info()\n";
// cerr << toHex(state.blockData()) << "vs" << toHex(block2.out());
// }
// if (sha3(RLP(state.blockData())[1].data()) != sha3(RLP(block2.out())[1].data()))
// cnote << "txs mismatch\n";
// if (sha3(RLP(state.blockData())[2].data()) != sha3(RLP(block2.out())[2].data()))
// cnote << "uncle list mismatch\n" << RLP(state.blockData())[2].data() << "\n" << RLP(block2.out())[2].data();
// try
// {
// state.sync(bc);
// bc.import(block2.out(), state.db());
// state.sync(bc);
// state.commit();
// //there we get new blockchain status in state which could have more difficulty than we have in trueState
// //attempt to import new block to the true blockchain
// trueBc.sync(uncleBlockQueue, trueState.db(), 4);
// trueBc.attemptImport(block2.out(), trueState.db());
// trueState.sync(trueBc);
// blockSet newBlock;
// newBlock.first = block2.out();
// newBlock.second = uncleBlockQueueList;
// if (importBlockNumber < blockSets.size())
// {
// //make new correct history of imported blocks
// blockSets[importBlockNumber] = newBlock;
// for (size_t i = importBlockNumber + 1; i < blockSets.size(); i++)
// blockSets.pop_back();
// }
// else
// blockSets.push_back(newBlock);
// }
// // if exception is thrown, RLP is invalid and no blockHeader, Transaction list, or Uncle list should be given
// catch (...)
// {
// cnote << "block is invalid!\n";
// blObj.erase(blObj.find("blockHeader"));
// blObj.erase(blObj.find("uncleHeaders"));
// blObj.erase(blObj.find("transactions"));
// }
// blArray.push_back(blObj);
// this_thread::sleep_for(chrono::seconds(1));
} //for blocks
if (o.count("expect") > 0)
{
stateOptionsMap expectStateMap;
State stateExpect(OverlayDB(), BaseState::Empty, biGenesisBlock.coinbaseAddress());
importer.importState(o["expect"].get_obj(), stateExpect, expectStateMap);
ImportTest::checkExpectedState(stateExpect, trueState, expectStateMap, Options::get().checkState ? WhenError::Throw : WhenError::DontThrow);
o.erase(o.find("expect"));
}
o["blocks"] = blArray;
o["postState"] = fillJsonWithState(trueState);
o["lastblockhash"] = toString(trueBc.info().hash());
//make all values hex in pre section
State prestate(OverlayDB(), BaseState::Empty, biGenesisBlock.coinbaseAddress());
importer.importState(o["pre"].get_obj(), prestate);
o["pre"] = fillJsonWithState(prestate);
// if (o.count("expect") > 0)
// {
// AccountMaskMap expectStateMap;
// State stateExpect(OverlayDB(), BaseState::Empty, biGenesisBlock.beneficiary());
// ImportTest::importState(o["expect"].get_obj(), stateExpect, expectStateMap);
// ImportTest::checkExpectedState(stateExpect, trueState, expectStateMap, Options::get().checkState ? WhenError::Throw : WhenError::DontThrow);
// o.erase(o.find("expect"));
// }
// o["blocks"] = blArray;
// o["postState"] = fillJsonWithState(trueState);
// o["lastblockhash"] = toString(trueBc.info().hash());
// //make all values hex in pre section
// State prestate(OverlayDB(), BaseState::Empty);
// ImportTest::importState(o["pre"].get_obj(), prestate);
// o["pre"] = fillJsonWithState(prestate);
}//_fillin
else
{
for (auto const& bl: o["blocks"].get_array())
@ -334,11 +346,9 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
try
{
blockRLP = importByteArray(blObj["rlp"].get_str());
trueState.sync(trueBc);
trueBc.import(blockRLP, trueState.db());
if (trueBc.info() != BlockHeader(blockRLP))
importedAndBest = false;
trueState.sync(trueBc);
}
// if exception is thrown, RLP is invalid and no blockHeader, Transaction list, or Uncle list should be given
catch (Exception const& _e)
@ -382,7 +392,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
TBOOST_CHECK_MESSAGE((blockHeaderFromFields.headerHash(WithProof) == blockFromRlp.headerHash(WithProof)), "hash in given RLP not matching the block hash!");
TBOOST_CHECK_MESSAGE((blockHeaderFromFields.parentHash() == blockFromRlp.parentHash()), "parentHash in given RLP not matching the block parentHash!");
TBOOST_CHECK_MESSAGE((blockHeaderFromFields.sha3Uncles() == blockFromRlp.sha3Uncles()), "sha3Uncles in given RLP not matching the block sha3Uncles!");
TBOOST_CHECK_MESSAGE((blockHeaderFromFields.coinbaseAddress() == blockFromRlp.coinbaseAddress()),"coinbaseAddress in given RLP not matching the block coinbaseAddress!");
TBOOST_CHECK_MESSAGE((blockHeaderFromFields.beneficiary() == blockFromRlp.beneficiary()),"beneficiary in given RLP not matching the block beneficiary!");
TBOOST_CHECK_MESSAGE((blockHeaderFromFields.stateRoot() == blockFromRlp.stateRoot()), "stateRoot in given RLP not matching the block stateRoot!");
TBOOST_CHECK_MESSAGE((blockHeaderFromFields.transactionsRoot() == blockFromRlp.transactionsRoot()), "transactionsRoot in given RLP not matching the block transactionsRoot!");
TBOOST_CHECK_MESSAGE((blockHeaderFromFields.receiptsRoot() == blockFromRlp.receiptsRoot()), "receiptsRoot in given RLP not matching the block receiptsRoot!");
@ -502,7 +512,6 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
}
}
}
// helping functions
mArray importUncles(mObject const& _blObj, vector<BlockHeader>& _vBiUncles, vector<BlockHeader> const& _vBiBlocks, std::vector<blockSet> _blockSet)
@ -574,7 +583,7 @@ mArray importUncles(mObject const& _blObj, vector<BlockHeader>& _vBiUncles, vect
uncleBlockFromFields = constructHeader(
overwrite == "parentHash" ? h256(uncleHeaderObj["parentHash"].get_str()) : uncleBlockFromFields.parentHash(),
uncleBlockFromFields.sha3Uncles(),
uncleBlockFromFields.coinbaseAddress(),
uncleBlockFromFields.beneficiary(),
overwrite == "stateRoot" ? h256(uncleHeaderObj["stateRoot"].get_str()) : uncleBlockFromFields.stateRoot(),
uncleBlockFromFields.transactionsRoot(),
uncleBlockFromFields.receiptsRoot(),
@ -672,7 +681,7 @@ void overwriteBlockHeader(BlockHeader& _header, mObject& _blObj, BlockHeader con
BlockHeader tmp = constructHeader(
ho.count("parentHash") ? h256(ho["parentHash"].get_str()) : _header.parentHash(),
ho.count("uncleHash") ? h256(ho["uncleHash"].get_str()) : _header.sha3Uncles(),
ho.count("coinbase") ? Address(ho["coinbase"].get_str()) : _header.coinbaseAddress(),
ho.count("coinbase") ? Address(ho["coinbase"].get_str()) : _header.beneficiary(),
ho.count("stateRoot") ? h256(ho["stateRoot"].get_str()): _header.stateRoot(),
ho.count("transactionsTrie") ? h256(ho["transactionsTrie"].get_str()) : _header.transactionsRoot(),
ho.count("receiptTrie") ? h256(ho["receiptTrie"].get_str()) : _header.receiptsRoot(),
@ -759,7 +768,7 @@ mObject writeBlockHeaderToJson(mObject& _o, BlockHeader const& _bi)
{
_o["parentHash"] = toString(_bi.parentHash());
_o["uncleHash"] = toString(_bi.sha3Uncles());
_o["coinbase"] = toString(_bi.coinbaseAddress());
_o["coinbase"] = toString(_bi.beneficiary());
_o["stateRoot"] = toString(_bi.stateRoot());
_o["transactionsTrie"] = toString(_bi.transactionsRoot());
_o["receiptTrie"] = toString(_bi.receiptsRoot());

21
test/libethereum/gaspricer.cpp

@ -22,6 +22,7 @@
#include <libtestutils/BlockChainLoader.h>
#include <libethcore/Ethash.h>
#include <libethereum/BlockChain.h>
#include <libethereum/CanonBlockChain.h>
#include <libethereum/GasPricer.h>
#include <libethereum/BasicGasPricer.h>
@ -43,7 +44,7 @@ void executeGasPricerTest(string const& name, double _etherPrice, double _blockF
BlockChain const& bc = bcLoader.bc();
gp.update(bc);
BOOST_CHECK_EQUAL(gp.ask(State()), _expectedAsk);
BOOST_CHECK_EQUAL(gp.ask(Block()), _expectedAsk);
BOOST_CHECK_EQUAL(gp.bid(_txPrio), _expectedBid);
}
} }
@ -54,10 +55,12 @@ BOOST_AUTO_TEST_CASE(trivialGasPricer)
{
cnote << "trivialGasPricer";
std::shared_ptr<dev::eth::GasPricer> gp(new TrivialGasPricer);
BOOST_CHECK_EQUAL(gp->ask(State()), c_defaultGasPrice);
BOOST_CHECK_EQUAL(gp->ask(Block()), c_defaultGasPrice);
BOOST_CHECK_EQUAL(gp->bid(), c_defaultGasPrice);
gp->update(CanonBlockChain<Ethash>(TransientDirectory().path(), WithExisting::Kill));
BOOST_CHECK_EQUAL(gp->ask(State()), c_defaultGasPrice);
bytes bl = CanonBlockChain<Ethash>::createGenesisBlock();
gp->update(FullBlockChain<Ethash>(bl, AccountMap(), TransientDirectory().path(), WithExisting::Kill));
BOOST_CHECK_EQUAL(gp->ask(Block()), c_defaultGasPrice);
BOOST_CHECK_EQUAL(gp->bid(), c_defaultGasPrice);
}
@ -65,27 +68,27 @@ BOOST_AUTO_TEST_CASE(basicGasPricerNoUpdate)
{
cnote << "basicGasPricer";
BasicGasPricer gp(u256(double(ether / 1000) / 30.679), u256(15.0 * 1000));
BOOST_CHECK_EQUAL(gp.ask(State()), 155632494086);
BOOST_CHECK_EQUAL(gp.ask(Block()), 155632494086);
BOOST_CHECK_EQUAL(gp.bid(), 155632494086);
gp.setRefPrice(u256(0));
BOOST_CHECK_EQUAL(gp.ask(State()), 0);
BOOST_CHECK_EQUAL(gp.ask(Block()), 0);
BOOST_CHECK_EQUAL(gp.bid(), 0);
gp.setRefPrice(u256(1));
gp.setRefBlockFees(u256(0));
BOOST_CHECK_EQUAL(gp.ask(State()), 0);
BOOST_CHECK_EQUAL(gp.ask(Block()), 0);
BOOST_CHECK_EQUAL(gp.bid(), 0);
gp.setRefPrice(u256("0x100000000000000000000000000000000"));
BOOST_CHECK_THROW(gp.setRefBlockFees(u256("0x100000000000000000000000000000000")), Overflow);
BOOST_CHECK_EQUAL(gp.ask(State()), 0);
BOOST_CHECK_EQUAL(gp.ask(Block()), 0);
BOOST_CHECK_EQUAL(gp.bid(), 0);
gp.setRefPrice(1);
gp.setRefBlockFees(u256("0x100000000000000000000000000000000"));
BOOST_CHECK_THROW(gp.setRefPrice(u256("0x100000000000000000000000000000000")), Overflow);
BOOST_CHECK_EQUAL(gp.ask(State()), u256("108315264019305646138446560671076"));
BOOST_CHECK_EQUAL(gp.ask(Block()), u256("108315264019305646138446560671076"));
BOOST_CHECK_EQUAL(gp.bid(), u256("108315264019305646138446560671076"));
}

18
test/libethereum/state.cpp

@ -56,19 +56,18 @@ void doStateTests(json_spirit::mValue& v, bool _fillin)
TBOOST_REQUIRE((o.count("transaction") > 0));
ImportTest importer(o, _fillin);
State theState = importer.m_statePre;
const State importedStatePost = importer.m_statePost;
bytes output;
try
{
Listener::ExecTimeGuard guard{i.first};
output = theState.execute(lastHashes(importer.m_environment.currentBlock.number()), importer.m_transaction).output;
output = importer.executeTest();
}
catch (Exception const& _e)
{
cnote << "Exception: " << diagnostic_information(_e);
theState.commit();
//theState.commit();
}
catch (std::exception const& _e)
{
@ -78,7 +77,7 @@ void doStateTests(json_spirit::mValue& v, bool _fillin)
if (_fillin)
{
#if ETH_FATDB
importer.exportTest(output, theState);
importer.exportTest(output);
#else
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("You can not fill tests when FATDB is switched off"));
#endif
@ -92,16 +91,13 @@ void doStateTests(json_spirit::mValue& v, bool _fillin)
checkOutput(output, o);
// check logs
checkLog(theState.pending().size() ? theState.log(0) : LogEntries(), importer.m_environment.sub.logs);
checkLog(importer.m_logs, importer.m_logsExpected);
// check addresses
#if ETH_FATDB
ImportTest::checkExpectedState(importer.m_statePost, theState);
auto expectedAddrs = importer.m_statePost.addresses();
auto resultAddrs = theState.addresses();
checkAddresses(expectedAddrs, resultAddrs);
ImportTest::compareStates(importer.m_statePost, importedStatePost);
#endif
TBOOST_CHECK_MESSAGE((theState.rootHash() == h256(o["postStateRoot"].get_str())), "wrong post state root");
TBOOST_CHECK_MESSAGE((importer.m_statePost.rootHash() == h256(o["postStateRoot"].get_str())), "wrong post state root");
}
}
}

19
test/libethereum/stateOriginal.cpp

@ -23,7 +23,7 @@
#include <boost/test/unit_test.hpp>
#include <boost/filesystem/operations.hpp>
#include <libethereum/CanonBlockChain.h>
#include <libethereum/State.h>
#include <libethereum/Block.h>
#include <libethcore/Farm.h>
#include <libethcore/BasicAuthority.h>
#include <libethereum/Defaults.h>
@ -44,12 +44,15 @@ BOOST_AUTO_TEST_SUITE(StateIntegration)
BOOST_AUTO_TEST_CASE(Basic)
{
State s;
Block s;
}
BOOST_AUTO_TEST_CASE(Complex)
{
if (test::Options::get().nodag)
//Mining Changed on block branch
//Review this test
/*if (test::Options::get().nodag)
return;
cnote << "Testing State...";
@ -64,8 +67,8 @@ BOOST_AUTO_TEST_CASE(Complex)
CanonBlockChain<BasicAuthority> bc;
cout << bc;
State s = bc.genesisState(stateDB);
s.setAddress(myMiner.address());
Block s = bc.genesisBlock(stateDB);
s.setBeneficiary(myMiner.address());
cout << s;
// Sync up - this won't do much until we use the last state.
@ -92,8 +95,8 @@ BOOST_AUTO_TEST_CASE(Complex)
cout << s;
// Mine to get some ether and set in stone.
s.commitToMine(bc);
s.commitToMine(bc);
s.commitToSeal(bc);
s.commitToSeal(bc);
mine(s, bc);
bc.attemptImport(s.blockData(), stateDB);
@ -101,7 +104,7 @@ BOOST_AUTO_TEST_CASE(Complex)
s.sync(bc);
cout << s;
cout << s;*/
}
BOOST_AUTO_TEST_SUITE_END()

2
test/libethereum/transaction.cpp → test/libethereum/transactionTests.cpp

@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file transaction.cpp
/** @file transactionTests.cpp
* @author Dmitrii Khokhlov <winsvega@mail.ru>
* @date 2015
* Transaction test functions.

58
test/libevm/vm.cpp

@ -24,6 +24,7 @@
#include <libethereum/Executive.h>
#include <libevm/VMFactory.h>
#include <libevm/ExtVMFace.h>
#include "vm.h"
using namespace std;
@ -32,8 +33,9 @@ using namespace dev;
using namespace dev::eth;
using namespace dev::test;
FakeExtVM::FakeExtVM(eth::BlockInfo const& _previousBlock, eth::BlockInfo const& _currentBlock, unsigned _depth): /// TODO: XXX: remove the default argument & fix.
ExtVMFace(Address(), Address(), Address(), 0, 1, bytesConstRef(), bytes(), EmptySHA3, _previousBlock, _currentBlock, test::lastHashes(_currentBlock.number()), _depth) {}
FakeExtVM::FakeExtVM(EnvInfo const& _envInfo, unsigned _depth): /// TODO: XXX: remove the default argument & fix.
ExtVMFace(_envInfo, Address(), Address(), Address(), 0, 1, bytesConstRef(), bytes(), EmptySHA3, _depth)
{}
h160 FakeExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _init, OnOpFunc const&)
{
@ -83,45 +85,30 @@ void FakeExtVM::reset(u256 _myBalance, u256 _myNonce, map<u256, u256> const& _st
mObject FakeExtVM::exportEnv()
{
mObject ret;
ret["previousHash"] = toString(currentBlock.parentHash());
ret["currentDifficulty"] = toCompactHex(currentBlock.difficulty(), HexPrefix::Add, 1);
ret["currentTimestamp"] = toCompactHex(currentBlock.timestamp(), HexPrefix::Add, 1);
ret["currentCoinbase"] = toString(currentBlock.coinbaseAddress());
ret["currentNumber"] = toCompactHex(currentBlock.number(), HexPrefix::Add, 1);
ret["currentGasLimit"] = toCompactHex(currentBlock.gasLimit(), HexPrefix::Add, 1);
ret["currentDifficulty"] = toCompactHex(envInfo().difficulty(), HexPrefix::Add, 1);
ret["currentTimestamp"] = toCompactHex(envInfo().timestamp(), HexPrefix::Add, 1);
ret["currentCoinbase"] = toString(envInfo().beneficiary());
ret["currentNumber"] = toCompactHex(envInfo().number(), HexPrefix::Add, 1);
ret["currentGasLimit"] = toCompactHex(envInfo().gasLimit(), HexPrefix::Add, 1);
return ret;
}
void FakeExtVM::importEnv(mObject& _o)
{
// cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest)
assert(_o.count("previousHash") > 0);
assert(_o.count("currentGasLimit") > 0);
assert(_o.count("currentDifficulty") > 0);
assert(_o.count("currentTimestamp") > 0);
assert(_o.count("currentCoinbase") > 0);
assert(_o.count("currentNumber") > 0);
RLPStream rlpStream;
rlpStream.appendList(BlockInfo::BasicFields);
rlpStream << h256(_o["previousHash"].get_str());
rlpStream << EmptyListSHA3;
rlpStream << Address(_o["currentCoinbase"].get_str());
rlpStream << h256(); // stateRoot
rlpStream << EmptyTrie; // transactionTrie
rlpStream << EmptyTrie; // receiptTrie
rlpStream << LogBloom(); // bloom
rlpStream << toInt(_o["currentDifficulty"]);
rlpStream << toInt(_o["currentNumber"]);
rlpStream << toInt(_o["currentGasLimit"]);
rlpStream << 0; //gasUsed
rlpStream << toInt(_o["currentTimestamp"]);
rlpStream << std::string(); //extra data
currentBlock = BlockInfo(rlpStream.out(), CheckEverything, h256{}, HeaderData);
lastHashes = test::lastHashes(currentBlock.number());
EnvInfo& info = *(const_cast<EnvInfo*> (&envInfo())); //trick
info.setGasLimit(toInt(_o["currentGasLimit"]));
info.setDifficulty(toInt(_o["currentDifficulty"]));
info.setTimestamp(toInt(_o["currentTimestamp"]));
info.setBeneficiary(Address(_o["currentCoinbase"].get_str()));
info.setNumber(toInt(_o["currentNumber"]));
info.setLastHashes( lastHashes( info.number() ) );
}
mObject FakeExtVM::exportState()
@ -323,7 +310,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
TBOOST_REQUIRE((o.count("pre") > 0));
TBOOST_REQUIRE((o.count("exec") > 0));
FakeExtVM fev;
FakeExtVM fev(eth::EnvInfo{});
fev.importEnv(o["env"].get_obj());
fev.importState(o["pre"].get_obj());
@ -393,10 +380,10 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
{
State postState(OverlayDB(), eth::BaseState::Empty);
State expectState(OverlayDB(), eth::BaseState::Empty);
stateOptionsMap expectStateMap;
AccountMaskMap expectStateMap;
ImportTest::importState(o["post"].get_obj(), postState);
ImportTest::importState(o["expect"].get_obj(), expectState, expectStateMap);
ImportTest::checkExpectedState(expectState, postState, expectStateMap, Options::get().checkState ? WhenError::Throw : WhenError::DontThrow);
ImportTest::compareStates(expectState, postState, expectStateMap, Options::get().checkState ? WhenError::Throw : WhenError::DontThrow);
o.erase(o.find("expect"));
}
@ -431,11 +418,12 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
TBOOST_REQUIRE((o.count("gas") > 0));
TBOOST_REQUIRE((o.count("logs") > 0));
dev::test::FakeExtVM test;
dev::test::FakeExtVM test(eth::EnvInfo{});
test.importState(o["post"].get_obj());
test.importCallCreates(o["callcreates"].get_array());
test.sub.logs = importLog(o["logs"].get_array());
checkOutput(output, o);
TBOOST_CHECK_EQUAL(toInt(o["gas"]), fev.gas);
@ -444,9 +432,9 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
mObject mPostState = fev.exportState();
ImportTest::importState(mPostState, postState);
ImportTest::importState(o["post"].get_obj(), expectState);
ImportTest::checkExpectedState(expectState, postState);
ImportTest::compareStates(expectState, postState);
checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
//checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
checkCallCreates(fev.callcreates, test.callcreates);

4
test/libevm/vm.h

@ -47,8 +47,8 @@ struct FakeExtVMFailure : virtual Exception {};
class FakeExtVM: public eth::ExtVMFace
{
public:
FakeExtVM() = default;
FakeExtVM(eth::BlockInfo const& _previousBlock, eth::BlockInfo const& _currentBlock, unsigned _depth = 0);
FakeExtVM() = delete;
FakeExtVM(eth::EnvInfo const& _envInfo, unsigned _depth = 0);
virtual u256 store(u256 _n) override { return std::get<2>(addresses[myAddress])[_n]; }
virtual void setStore(u256 _n, u256 _v) override { std::get<2>(addresses[myAddress])[_n] = _v; }

3
test/libp2p/net.cpp

@ -339,9 +339,6 @@ BOOST_AUTO_TEST_SUITE(netTypes)
BOOST_AUTO_TEST_CASE(deadlineTimer)
{
// @subtly fixme
return;
if (test::Options::get().nonetwork)
return;

9
test/libp2p/peer.cpp

@ -152,8 +152,9 @@ BOOST_AUTO_TEST_CASE(requirePeer)
if (test::Options::get().nonetwork)
return;
VerbosityHolder reduceVerbosity(10);
VerbosityHolder temporaryLevel(10);
unsigned const step = 10;
const char* const localhost = "127.0.0.1";
NetworkPreferences prefs1(localhost, 30323, false);
NetworkPreferences prefs2(localhost, 30324, false);
@ -166,7 +167,8 @@ BOOST_AUTO_TEST_CASE(requirePeer)
auto node2 = host2.id();
host1.requirePeer(node2, NodeIPEndpoint(bi::address::from_string(localhost), prefs2.listenPort, prefs2.listenPort));
this_thread::sleep_for(chrono::seconds(3));
for (unsigned i = 0; i < 3000 && (!host1.peerCount() || !host2.peerCount()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
auto host1peerCount = host1.peerCount();
auto host2peerCount = host2.peerCount();
@ -190,7 +192,8 @@ BOOST_AUTO_TEST_CASE(requirePeer)
host1.relinquishPeer(node2);
this_thread::sleep_for(chrono::seconds(1));
for (unsigned i = 0; i < 2000 && (host1.peerCount() || host2.peerCount()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
host1peerCount = host1.peerCount();
host2peerCount = host2.peerCount();

12
test/libsolidity/SolidityEndToEndTest.cpp

@ -1151,8 +1151,10 @@ BOOST_AUTO_TEST_CASE(blockchain)
" blockNumber = block.number;\n"
" }\n"
"}\n";
m_envInfo.setBeneficiary(Address(0x123));
m_envInfo.setNumber(7);
compileAndRun(sourceCode, 27);
BOOST_CHECK(callContractFunctionWithValue("someInfo()", 28) == encodeArgs(28, 0, 1));
BOOST_CHECK(callContractFunctionWithValue("someInfo()", 28) == encodeArgs(28, 0x123, 7));
}
BOOST_AUTO_TEST_CASE(msg_sig)
@ -1187,12 +1189,14 @@ BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same)
BOOST_AUTO_TEST_CASE(now)
{
char const* sourceCode = "contract test {\n"
" function someInfo() returns (bool success) {\n"
" return block.timestamp == now && now > 0;\n"
" function someInfo() returns (bool equal, uint val) {\n"
" equal = block.timestamp == now;\n"
" val = now;\n"
" }\n"
"}\n";
m_envInfo.setTimestamp(9);
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("someInfo()") == encodeArgs(true));
BOOST_CHECK(callContractFunction("someInfo()") == encodeArgs(true, 9));
}
BOOST_AUTO_TEST_CASE(type_conversions_cleanup)

68
test/libsolidity/solidityExecutionFramework.h

@ -25,6 +25,7 @@
#include <string>
#include <tuple>
#include "../TestHelper.h"
#include <libethcore/ABI.h>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h>
@ -44,7 +45,6 @@ public:
ExecutionFramework()
{
g_logVerbosity = 0;
m_state.resetCurrent();
}
bytes const& compileAndRunWithoutCheck(
@ -166,6 +166,69 @@ public:
return encodeArgs(u256(0x20), u256(_arg.size()), _arg);
}
class ContractInterface
{
public:
ContractInterface(ExecutionFramework& _framework): m_framework(_framework) {}
protected:
template <class... Args>
bytes const& call(std::string const& _sig, Args const&... _arguments)
{
auto const& ret = m_framework.callContractFunctionWithValue(_sig, m_nextValue, _arguments...);
m_nextValue = 0;
return ret;
}
void callString(std::string const& _name, std::string const& _arg)
{
BOOST_CHECK(call(_name + "(string)", u256(0x20), _arg.length(), _arg).empty());
}
void callStringAddress(std::string const& _name, std::string const& _arg1, u160 const& _arg2)
{
BOOST_CHECK(call(_name + "(string,address)", u256(0x40), _arg2, _arg1.length(), _arg1).empty());
}
void callStringAddressBool(std::string const& _name, std::string const& _arg1, u160 const& _arg2, bool _arg3)
{
BOOST_CHECK(call(_name + "(string,address,bool)", u256(0x60), _arg2, _arg3, _arg1.length(), _arg1).empty());
}
void callStringBytes32(std::string const& _name, std::string const& _arg1, h256 const& _arg2)
{
BOOST_CHECK(call(_name + "(string,bytes32)", u256(0x40), _arg2, _arg1.length(), _arg1).empty());
}
u160 callStringReturnsAddress(std::string const& _name, std::string const& _arg)
{
bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg);
BOOST_REQUIRE(ret.size() == 0x20);
BOOST_CHECK(std::count(ret.begin(), ret.begin() + 12, 0) == 12);
return eth::abiOut<u160>(ret);
}
std::string callAddressReturnsString(std::string const& _name, u160 const& _arg)
{
bytes const& ret = call(_name + "(address)", _arg);
BOOST_REQUIRE(ret.size() >= 0x20);
u256 len = eth::abiOut<u256>(ret);
BOOST_REQUIRE(ret.size() == (0x20 + len) / 32 * 32);
return std::string(ret.begin() + 0x20, ret.begin() + 0x20 + size_t(len));
}
h256 callStringReturnsBytes32(std::string const& _name, std::string const& _arg)
{
bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg);
BOOST_REQUIRE(ret.size() == 0x20);
return eth::abiOut<h256>(ret);
}
private:
u256 m_nextValue;
ExecutionFramework& m_framework;
};
private:
template <class CppFunction, class... Args>
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
@ -185,7 +248,7 @@ protected:
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
{
m_state.addBalance(m_sender, _value); // just in case
eth::Executive executive(m_state, eth::LastHashes(), 0);
eth::Executive executive(m_state, m_envInfo, 0);
eth::ExecutionResult res;
executive.setResultRecipient(res);
eth::Transaction t =
@ -226,6 +289,7 @@ protected:
dev::solidity::CompilerStack m_compiler;
Address m_sender;
Address m_contractAddress;
eth::EnvInfo m_envInfo;
eth::State m_state;
u256 const m_gasPrice = 100 * eth::szabo;
u256 const m_gas = 100000000;

6
test/libweb3jsonrpc/jsonrpc.cpp

@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(jsonrpc_transact)
dev::KeyPair key = KeyPair::create();
auto address = key.address();
auto receiver = KeyPair::create();
web3->ethereum()->setAddress(address);
web3->ethereum()->setBeneficiary(address);
coinbase = jsonrpcClient->eth_coinbase();
BOOST_CHECK_EQUAL(jsToAddress(coinbase), web3->ethereum()->address());
@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(simple_contract)
{
cnote << "Testing jsonrpc contract...";
KeyPair kp = KeyPair::create();
web3->ethereum()->setAddress(kp.address());
web3->ethereum()->setBeneficiary(kp.address());
jsonrpcServer->setAccounts({kp});
dev::eth::mine(*(web3->ethereum()), 1);
@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(contract_storage)
{
cnote << "Testing jsonrpc contract storage...";
KeyPair kp = KeyPair::create();
web3->ethereum()->setAddress(kp.address());
web3->ethereum()->setBeneficiary(kp.address());
jsonrpcServer->setAccounts({kp});
dev::eth::mine(*(web3->ethereum()), 1);

264
test/libwhisper/shhrpc.cpp

@ -0,0 +1,264 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file shhrpc.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date July 2015
*/
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h>
#include <libethcore/CommonJS.h>
#include <libwebthree/WebThree.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#include <jsonrpccpp/client/connectors/httpclient.h>
#include <test/TestHelper.h>
#include <test/libweb3jsonrpc/webthreestubclient.h>
#include <libethcore/KeyManager.h>
#include <libp2p/Common.h>
#include <libwhisper/WhisperHost.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::p2p;
using namespace dev::shh;
namespace js = json_spirit;
WebThreeDirect* web3;
unique_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<WebThreeStubClient> jsonrpcClient;
static uint16_t const web3port = 30333;
struct Setup
{
Setup()
{
dev::p2p::NodeIPEndpoint::test_allowLocal = true;
static bool setup = false;
if (!setup)
{
setup = true;
NetworkPreferences nprefs(std::string(), web3port, false);
web3 = new WebThreeDirect("shhrpc-web3", "", WithExisting::Trust, {"eth", "shh"}, nprefs);
web3->setIdealPeerCount(1);
web3->ethereum()->setForceMining(false);
auto server = new jsonrpc::HttpServer(8080);
vector<KeyPair> v;
KeyManager keyMan;
TrivialGasPricer gp;
jsonrpcServer = unique_ptr<WebThreeStubServer>(new WebThreeStubServer(*server, *web3, nullptr, v, keyMan, gp));
jsonrpcServer->setIdentities({});
jsonrpcServer->StartListening();
auto client = new jsonrpc::HttpClient("http://localhost:8080");
jsonrpcClient = unique_ptr<WebThreeStubClient>(new WebThreeStubClient(*client));
}
}
~Setup()
{
dev::p2p::NodeIPEndpoint::test_allowLocal = false;
}
};
BOOST_FIXTURE_TEST_SUITE(shhrpc, Setup)
BOOST_AUTO_TEST_CASE(basic)
{
cnote << "Testing web3 basic functionality...";
web3->startNetwork();
unsigned const step = 10;
for (unsigned i = 0; i < 3000 && !web3->haveNetwork(); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(web3->haveNetwork());
uint16_t const port2 = 30334;
NetworkPreferences prefs2("127.0.0.1", port2, false);
string const version2 = "shhrpc-host2";
Host host2(version2, prefs2);
auto whost2 = host2.registerCapability(new WhisperHost());
host2.start();
for (unsigned i = 0; i < 3000 && !host2.haveNetwork(); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host2.haveNetwork());
web3->addNode(host2.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), port2, port2));
for (unsigned i = 0; i < 3000 && (!web3->peerCount() || !host2.peerCount()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE_EQUAL(host2.peerCount(), 1);
BOOST_REQUIRE_EQUAL(web3->peerCount(), 1);
vector<PeerSessionInfo> vpeers = web3->peers();
BOOST_REQUIRE(!vpeers.empty());
PeerSessionInfo const& peer = vpeers.back();
BOOST_REQUIRE_EQUAL(peer.id, host2.id());
BOOST_REQUIRE_EQUAL(peer.port, port2);
BOOST_REQUIRE_EQUAL(peer.clientVersion, version2);
web3->stopNetwork();
for (unsigned i = 0; i < 3000 && (web3->haveNetwork() || host2.haveNetwork()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(!web3->peerCount());
BOOST_REQUIRE(!host2.peerCount());
}
BOOST_AUTO_TEST_CASE(send)
{
cnote << "Testing web3 send...";
bool sent = false;
bool ready = false;
unsigned result = 0;
unsigned const messageCount = 10;
unsigned const step = 10;
uint16_t port2 = 30337;
Host host2("shhrpc-host2", NetworkPreferences("127.0.0.1", port2, false));
host2.setIdealPeerCount(1);
auto whost2 = host2.registerCapability(new WhisperHost());
host2.start();
web3->startNetwork();
std::thread listener([&]()
{
setThreadName("listener");
ready = true;
auto w = whost2->installWatch(BuildTopicMask("odd"));
set<unsigned> received;
for (unsigned x = 0; x < 9000 && !sent; x += step)
this_thread::sleep_for(chrono::milliseconds(step));
for (unsigned x = 0, last = 0; x < 100 && received.size() < messageCount; ++x)
{
this_thread::sleep_for(chrono::milliseconds(50));
for (auto i: whost2->checkWatch(w))
{
Message msg = whost2->envelope(i).open(whost2->fullTopics(w));
last = RLP(msg.payload()).toInt<unsigned>();
if (received.insert(last).second)
result += last;
}
}
});
for (unsigned i = 0; i < 2000 && (!host2.haveNetwork() || !web3->haveNetwork()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host2.haveNetwork());
BOOST_REQUIRE(web3->haveNetwork());
web3->requirePeer(host2.id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), port2, port2));
for (unsigned i = 0; i < 3000 && (!web3->peerCount() || !host2.peerCount()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE_EQUAL(host2.peerCount(), 1);
BOOST_REQUIRE_EQUAL(web3->peerCount(), 1);
KeyPair us = KeyPair::create();
for (unsigned i = 0; i < messageCount; ++i)
{
web3->whisper()->post(us.sec(), RLPStream().append(i * i).out(), BuildTopic(i)(i % 2 ? "odd" : "even"), 777000, 1);
this_thread::sleep_for(chrono::milliseconds(50));
}
sent = true;
auto messages = web3->whisper()->all();
BOOST_REQUIRE_EQUAL(messages.size(), messageCount);
listener.join();
BOOST_REQUIRE_EQUAL(result, 1 + 9 + 25 + 49 + 81);
}
BOOST_AUTO_TEST_CASE(receive)
{
cnote << "Testing web3 receive...";
bool sent = false;
bool ready = false;
unsigned result = 0;
unsigned const messageCount = 6;
unsigned const step = 10;
uint16_t port2 = 30338;
Host host2("shhrpc-host2", NetworkPreferences("127.0.0.1", port2, false));
host2.setIdealPeerCount(1);
auto whost2 = host2.registerCapability(new WhisperHost());
host2.start();
web3->startNetwork();
std::thread listener([&]()
{
setThreadName("listener");
ready = true;
auto w = web3->whisper()->installWatch(BuildTopicMask("odd"));
set<unsigned> received;
for (unsigned x = 0; x < 9000 && !sent; x += step)
this_thread::sleep_for(chrono::milliseconds(step));
for (unsigned x = 0, last = 0; x < 100 && received.size() < messageCount; ++x)
{
this_thread::sleep_for(chrono::milliseconds(50));
for (auto i: web3->whisper()->checkWatch(w))
{
Message msg = web3->whisper()->envelope(i).open(web3->whisper()->fullTopics(w));
last = RLP(msg.payload()).toInt<unsigned>();
if (received.insert(last).second)
result += last;
}
}
});
for (unsigned i = 0; i < 2000 && (!host2.haveNetwork() || !web3->haveNetwork()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host2.haveNetwork());
BOOST_REQUIRE(web3->haveNetwork());
host2.addNode(web3->id(), NodeIPEndpoint(bi::address::from_string("127.0.0.1"), web3port, web3port));
for (unsigned i = 0; i < 3000 && (!web3->peerCount() || !host2.peerCount()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE_EQUAL(host2.peerCount(), 1);
BOOST_REQUIRE_EQUAL(web3->peerCount(), 1);
KeyPair us = KeyPair::create();
for (unsigned i = 0; i < messageCount; ++i)
{
web3->whisper()->post(us.sec(), RLPStream().append(i * i * i).out(), BuildTopic(i)(i % 2 ? "odd" : "even"), 777000, 1);
this_thread::sleep_for(chrono::milliseconds(50));
}
sent = true;
listener.join();
BOOST_REQUIRE_EQUAL(result, 1 + 27 + 125);
}
BOOST_AUTO_TEST_SUITE_END()

1
test/libwhisper/whisperTopic.cpp

@ -161,7 +161,6 @@ BOOST_AUTO_TEST_CASE(forwarding)
}
});
// Host must be configured not to share peers.
uint16_t port2 = 30313;
Host host2("Forwarder", NetworkPreferences("127.0.0.1", port2, false));

4
third/MainWin.cpp

@ -415,7 +415,7 @@ void Main::readSettings(bool _skipGeometry)
m_myKeys.append(KeyPair(k));
}
}
ethereum()->setAddress(m_myKeys.back().address());
ethereum()->setBeneficiary(m_myKeys.back().address());
m_networkConfig = s.value("peers").toByteArray();
ui->urlEdit->setText(s.value("url", "about:blank").toString()); //http://gavwood.com/gavcoin.html
on_urlEdit_returnPressed();
@ -609,7 +609,7 @@ void Main::on_mine_triggered()
{
if (ui->mine->isChecked())
{
ethereum()->setAddress(m_myKeys.last().address());
ethereum()->setBeneficiary(m_myKeys.last().address());
ethereum()->startMining();
}
else

Loading…
Cancel
Save