From 08d88f1a4aa6151ac626d80b1cf8650d805b73b9 Mon Sep 17 00:00:00 2001 From: arkpar Date: Wed, 4 Feb 2015 01:00:19 +0100 Subject: [PATCH] mix: Mining and transactions on a real blockchain --- mix/ClientModel.cpp | 39 +++++-- mix/MixClient.cpp | 219 ++++++++++++++++++++++++---------------- mix/MixClient.h | 30 +++--- mix/qml/MainContent.qml | 3 +- 4 files changed, 175 insertions(+), 116 deletions(-) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 365dce9a9..ebfd885f1 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -83,7 +84,7 @@ ClientModel::ClientModel(AppContext* _context): qRegisterMetaType("TransactionLogEntry"); connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); - m_client.reset(new MixClient()); + m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), std::vector { m_client->userAccount() }, m_client.get())); connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection); @@ -93,6 +94,7 @@ ClientModel::ClientModel(AppContext* _context): ClientModel::~ClientModel() { + } QString ClientModel::apiCall(QString const& _message) @@ -111,8 +113,25 @@ QString ClientModel::apiCall(QString const& _message) void ClientModel::mine() { - m_client->mine(); - newBlock(); + if (m_running) + BOOST_THROW_EXCEPTION(ExecutionStateException()); + m_running = true; + emit runStarted(); + emit runStateChanged(); + QtConcurrent::run([=]() + { + try + { + m_client->mine(); + newBlock(); + } + catch (...) + { + std::cerr << boost::current_exception_diagnostic_information(); + emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); + } + m_running = false; + }); } QString ClientModel::contractAddress() const @@ -145,7 +164,7 @@ void ClientModel::setupState(QVariantMap _state) TransactionSettings transactionSettings(functionId, transaction.value("url").toString()); transactionSettings.gasPrice = 10000000000000; transactionSettings.gas = 125000; - transactionSettings.value = 100; + transactionSettings.value = 0; transactionSequence.push_back(transactionSettings); } else @@ -242,11 +261,13 @@ void ClientModel::executeSequence(std::vector const& _seque } catch(boost::exception const&) { + std::cerr << boost::current_exception_diagnostic_information(); emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); } catch(std::exception const& e) { + std::cerr << boost::current_exception_diagnostic_information(); emit runFailed(e.what()); } m_running = false; @@ -256,7 +277,7 @@ void ClientModel::executeSequence(std::vector const& _seque void ClientModel::showDebugger() { - ExecutionResult const& last = m_client->record().back().transactions.back(); + ExecutionResult const& last = m_client->lastExecution(); showDebuggerForTransaction(last); } @@ -289,7 +310,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) void ClientModel::debugTransaction(unsigned _block, unsigned _index) { - auto const& t = m_client->record().at(_block).transactions.at(_index); + auto const& t = m_client->execution(_block, _index); showDebuggerForTransaction(t); } @@ -320,9 +341,9 @@ void ClientModel::onStateReset() void ClientModel::onNewTransaction() { - unsigned block = m_client->number(); - unsigned index = m_client->record().back().transactions.size() - 1; - ExecutionResult const& tr = m_client->record().back().transactions.back(); + unsigned block = m_client->number() + 1; + unsigned index = m_client->pendingExecutions().size() - 1; + ExecutionResult const& tr = m_client->lastExecution(); QString address = QString::fromStdString(toJS(tr.address)); QString value = QString::fromStdString(dev::toString(tr.value)); QString contract = address; diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index a6c833532..7cbb8424b 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -36,35 +36,49 @@ using namespace dev; using namespace dev::eth; using namespace dev::mix; -const Secret c_stdSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); +const Secret c_userAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); -MixClient::MixClient(): - m_userAccount(c_stdSecret) +MixClient::MixClient(std::string const& _dbPath): + m_userAccount(c_userAccountSecret), m_bc(_dbPath, true), m_dbPath(_dbPath), m_minigThreads(0) +{ + //resetState(10000000 * ether); +} + +MixClient::~MixClient() { - resetState(10000000 * ether); } void MixClient::resetState(u256 _balance) { - WriteGuard l(x_state); - Guard fl(m_filterLock); - m_filters.clear(); - m_watches.clear(); - m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Genesis); - m_state.addBalance(m_userAccount.address(), _balance); - Block genesis; - genesis.state = m_state; - Block open; - m_blocks = Blocks { genesis, open }; //last block contains a list of pending transactions to be finalized -// m_lastHashes.clear(); -// m_lastHashes.resize(256); -// m_lastHashes[0] = genesis.hash; + (void) _balance; + { + WriteGuard l(x_state); + Guard fl(m_filterLock); + m_filters.clear(); + m_watches.clear(); + m_bc.reopen(m_dbPath, true); + m_state = eth::State(); + m_stateDB = OverlayDB(); + m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Genesis); + m_state.sync(m_bc); + m_startState = m_state; + m_pendingExecutions.clear(); + } + mine(); } void MixClient::executeTransaction(Transaction const& _t, State& _state) { bytes rlp = _t.rlp(); - Executive execution(_state, LastHashes(), 0); + + // do debugging run first + LastHashes lastHashes(256); + lastHashes[0] = m_bc.numberHash(m_bc.number()); + for (unsigned i = 1; i < 256; ++i) + lastHashes[i] = lastHashes[i - 1] ? m_bc.details(lastHashes[i - 1]).parent : h256(); + + State execState = _state; + Executive execution(execState, lastHashes, 0); execution.setup(&rlp); std::vector machineStates; std::vector levels; @@ -130,21 +144,25 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state) d.value = _t.value(); if (_t.isCreation()) d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); - d.receipt = TransactionReceipt(m_state.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage - m_blocks.back().transactions.emplace_back(d); + d.receipt = TransactionReceipt(execState.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage + m_pendingExecutions.emplace_back(std::move(d)); + // execute on a state + _state.execute(lastHashes, rlp, nullptr, true); + + // collect watches h256Set changed; Guard l(m_filterLock); for (std::pair& i: m_filters) - if ((unsigned)i.second.filter.latest() > m_blocks.size() - 1) + if ((unsigned)i.second.filter.latest() > m_bc.number()) { // acceptable number. - auto m = i.second.filter.matches(d.receipt); + auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); if (m.size()) { // filter catches them for (LogEntry const& l: m) - i.second.changes.push_back(LocalisedLogEntry(l, m_blocks.size())); + i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1)); changed.insert(i.first); } } @@ -152,44 +170,56 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state) noteChanged(changed); } -void MixClient::validateBlock(int _block) const -{ - if (_block != -1 && _block != 0 && (unsigned)_block >= m_blocks.size() - 1) - BOOST_THROW_EXCEPTION(InvalidBlockException() << BlockIndex(_block)); -} - void MixClient::mine() { WriteGuard l(x_state); - Block& block = m_blocks.back(); - m_state.mine(0, true); + m_state.commitToMine(m_bc); + while (!m_state.mine(100, true).completed) {} m_state.completeMine(); - m_state.commitToMine(BlockChain()); - m_state.cleanup(true); - block.state = m_state; - block.info = m_state.info(); - block.hash = block.info.hash; - m_blocks.push_back(Block()); - + m_bc.import(m_state.blockData(), m_stateDB); + m_state.sync(m_bc); + //m_state.cleanup(true); + m_startState = m_state; + m_executions.emplace_back(std::move(m_pendingExecutions)); h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; noteChanged(changed); } -State const& MixClient::asOf(int _block) const +ExecutionResult const& MixClient::execution(unsigned _block, unsigned _transaction) const +{ + if (_block == m_bc.number() + 1) + return m_pendingExecutions.at(_transaction); + return m_executions.at(_block).at(_transaction); +} + +ExecutionResult const& MixClient::lastExecution() const +{ + if (m_pendingExecutions.size() > 0) + return m_pendingExecutions.back(); + return m_executions.back().back(); +} + +ExecutionResults const& MixClient::pendingExecutions() const { - validateBlock(_block); + return m_pendingExecutions; +} + +State MixClient::asOf(int _block) const +{ + ReadGuard l(x_state); if (_block == 0) - return m_blocks[m_blocks.size() - 2].state; - else if (_block == -1) return m_state; + else if (_block == -1) + return m_startState; else - return m_blocks[_block].state; + return State(m_stateDB, m_bc, m_bc.numberHash(_block)); } void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { WriteGuard l(x_state); u256 n = m_state.transactionsFrom(toAddress(_secret)); + _gasPrice = 0; //TODO: remove after fixing setBalance Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); executeTransaction(t, m_state); } @@ -198,6 +228,7 @@ Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, { WriteGuard l(x_state); u256 n = m_state.transactionsFrom(toAddress(_secret)); + _gasPrice = 0; //TODO: remove after fixing setBalance eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); executeTransaction(t, m_state); Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); @@ -228,36 +259,31 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _ bytes rlp = t.rlp(); WriteGuard lw(x_state); //TODO: lock is required only for last execution state executeTransaction(t, temp); - return m_blocks.back().transactions.back().returnValue; + return m_pendingExecutions.back().returnValue; } u256 MixClient::balanceAt(Address _a, int _block) const { - ReadGuard l(x_state); return asOf(_block).balance(_a); } u256 MixClient::countAt(Address _a, int _block) const { - ReadGuard l(x_state); return asOf(_block).transactionsFrom(_a); } u256 MixClient::stateAt(Address _a, u256 _l, int _block) const { - ReadGuard l(x_state); return asOf(_block).storage(_a, _l); } bytes MixClient::codeAt(Address _a, int _block) const { - ReadGuard l(x_state); return asOf(_block).code(_a); } std::map MixClient::storageAt(Address _a, int _block) const { - ReadGuard l(x_state); return asOf(_block).storage(_a); } @@ -274,23 +300,40 @@ eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const { LocalisedLogEntries ret; - unsigned lastBlock = m_blocks.size() - 1; //last block contains pending transactions + unsigned lastBlock = m_bc.number(); unsigned block = std::min(lastBlock, (unsigned)_f.latest()); unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest())); - for (; ret.size() != _f.max() && block != end; block--) + unsigned skip = _f.skip(); + // Pending transactions + if (block > m_bc.number()) { - bool pendingBlock = (block == lastBlock); - if (pendingBlock || _f.matches(m_blocks[block].info.logBloom)) - for (ExecutionResult const& t: m_blocks[block].transactions) - if (pendingBlock || _f.matches(t.receipt.bloom())) + ReadGuard l(x_state); + for (unsigned i = 0; i < m_state.pending().size(); ++i) + { + // Might have a transaction that contains a matching log. + TransactionReceipt const& tr = m_state.receipt(i); + LogEntries logEntries = _f.matches(tr); + for (unsigned entry = 0; entry < logEntries.size() && ret.size() != _f.max(); ++entry) + ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); + skip -= std::min(skip, static_cast(logEntries.size())); + } + block = m_bc.number(); + } + + // The rest + auto h = m_bc.numberHash(block); + for (; ret.size() != block && block != end; block--) + { + if (_f.matches(m_bc.info(h).logBloom)) + for (TransactionReceipt receipt: m_bc.receipts(h).receipts) + if (_f.matches(receipt.bloom())) { - LogEntries logEntries = _f.matches(t.receipt); - if (logEntries.size()) - { - for (unsigned entry = _f.skip(); entry < logEntries.size() && ret.size() != _f.max(); ++entry) - ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); - } + LogEntries logEntries = _f.matches(receipt); + for (unsigned entry = skip; entry < logEntries.size() && ret.size() != _f.max(); ++entry) + ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); + skip -= std::min(skip, static_cast(logEntries.size())); } + h = m_bc.details(h).parent; } return ret; } @@ -372,66 +415,65 @@ LocalisedLogEntries MixClient::checkWatch(unsigned _watchId) h256 MixClient::hashFromNumber(unsigned _number) const { - validateBlock(_number); - return m_blocks[_number].hash; + return m_bc.numberHash(_number); } eth::BlockInfo MixClient::blockInfo(h256 _hash) const { - (void)_hash; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockInfo")); + return BlockInfo(m_bc.block(_hash)); } eth::BlockDetails MixClient::blockDetails(h256 _hash) const { - (void)_hash; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::blockDetails")); + return m_bc.details(_hash); } eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const { - (void)_blockHash; - (void)_i; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::transaction")); + auto bl = m_bc.block(_blockHash); + RLP b(bl); + if (_i < b[1].itemCount()) + return Transaction(b[1][_i].data(), CheckSignature::Range); + else + return Transaction(); } eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const { - (void)_blockHash; - (void)_i; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::uncle")); + auto bl = m_bc.block(_blockHash); + RLP b(bl); + if (_i < b[2].itemCount()) + return BlockInfo::fromHeader(b[2][_i].data()); + else + return BlockInfo(); } unsigned MixClient::number() const { - return m_blocks.size() - 1; + return m_bc.number(); } eth::Transactions MixClient::pending() const { - return eth::Transactions(); + return m_state.pending(); } eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const { - (void)_txi; - (void)_block; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff")); + State st(m_stateDB, m_bc, _block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); } eth::StateDiff MixClient::diff(unsigned _txi, int _block) const { - (void)_txi; - (void)_block; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::diff")); + State st = asOf(_block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); } Addresses MixClient::addresses(int _block) const { - validateBlock(_block); - ReadGuard l(x_state); Addresses ret; - for (auto const& i: m_state.addresses()) + for (auto const& i: asOf(_block).addresses()) ret.push_back(i.first); return ret; } @@ -456,23 +498,22 @@ Address MixClient::address() const void MixClient::setMiningThreads(unsigned _threads) { - (void)_threads; - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::setMiningThreads")); + m_minigThreads = _threads; } unsigned MixClient::miningThreads() const { - return 0; + return m_minigThreads; } void MixClient::startMining() { - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::startMining")); + //no-op } void MixClient::stopMining() { - BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::Interface::stopMining")); + //no-op } bool MixClient::isMining() diff --git a/mix/MixClient.h b/mix/MixClient.h index c34fff60b..ca0101680 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -24,8 +24,10 @@ #pragma once #include +#include #include #include +#include #include "MachineStates.h" namespace dev @@ -33,26 +35,18 @@ namespace dev namespace mix { -struct Block -{ - ExecutionResults transactions; - h256 hash; - dev::eth::State state; - dev::eth::BlockInfo info; -}; - -using Blocks = std::vector; - - class MixClient: public dev::eth::Interface { public: - MixClient(); + MixClient(std::string const& _dbPath); + virtual ~MixClient(); /// Reset state to the empty state with given balance. void resetState(u256 _balance); KeyPair const& userAccount() const { return m_userAccount; } void mine(); - Blocks const& record() const { return m_blocks; } + ExecutionResult const& execution(unsigned _block, unsigned _transaction) const; + ExecutionResult const& lastExecution() const; + ExecutionResults const& pendingExecutions() const; //dev::eth::Interface void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; @@ -94,18 +88,22 @@ public: private: void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state); - void validateBlock(int _block) const; void noteChanged(h256Set const& _filters); - dev::eth::State const& asOf(int _block) const; + dev::eth::State asOf(int _block) const; KeyPair m_userAccount; eth::State m_state; + eth::State m_startState; OverlayDB m_stateDB; + eth::BlockChain m_bc; mutable boost::shared_mutex x_state; mutable std::mutex m_filterLock; std::map m_filters; std::map m_watches; - Blocks m_blocks; + std::vector m_executions; + ExecutionResults m_pendingExecutions; + std::string m_dbPath; + unsigned m_minigThreads; }; } diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 247de143f..e6c636514 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -59,8 +59,7 @@ Rectangle { } function hideRightView() { - if (rightView.visible) - rightView.hide(); + rightView.visible = false; } function toggleWebPreview() {