From c186d8f4d80a9201d77f36250b41d7863f499308 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 18 Feb 2014 13:27:13 +0000 Subject: [PATCH] Move to canon ES-1. Minor fix for contracts. --- TODO | 5 ++ alethzero/MainWin.cpp | 6 +- libethereum/BlockChain.cpp | 35 +++++++-- libethereum/BlockChain.h | 2 +- libethereum/BlockInfo.cpp | 2 +- libethereum/Client.cpp | 39 +++++----- libethereum/Client.h | 14 ++-- libethereum/Exceptions.h | 10 +-- libethereum/Instruction.h | 120 +++++++++++++++---------------- libethereum/State.cpp | 141 +++++++++++++++++++++++++------------ libethereum/State.h | 42 ++++++++--- test/vm.cpp | 26 +++++-- 12 files changed, 281 insertions(+), 161 deletions(-) diff --git a/TODO b/TODO index ea09bbc3d..59cacdca9 100644 --- a/TODO +++ b/TODO @@ -18,6 +18,10 @@ Network: - Solid communications? - Strategy for peer suggestion? +Cleanups & caching +- All caches should flush unused data (I'm looking at you, BlockChain) to avoid memory overload. +- State DB should keep only last few N blocks worth of nodes (except for restore points - configurable, defaults to every 30000th block - all blocks that are restore points should be stored so their stateRoots are known good). + THREAD-SAFETY - BlockChain - TransactionQueue @@ -43,6 +47,7 @@ GUI - Make address/block chain list model-based, JIT populated. - Make everything else model-based - Qt/QML class. +- Turn on/off debug channels. For PoC3: - Shared contract acceptence tests. diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 0142f9e6f..deb6bf26c 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -65,7 +65,7 @@ Main::~Main() void Main::on_about_triggered() { - QMessageBox::about(this, "About AlethZero PoC-2", "AlethZero/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM) "\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Eric Lombrozo, Marko Simovic, Alex Leverington and several others."); + QMessageBox::about(this, "About AlethZero PoC-3", "AlethZero/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM) "\nBy Gav Wood, 2014.\nBased on a design by Vitalik Buterin.\n\nTeam Ethereum++ includes: Eric Lombrozo, Marko Simovic, Alex Leverington, Tim Hughes and several others."); } void Main::writeSettings() @@ -305,11 +305,11 @@ void Main::on_send_clicked() u256 totalReq = value() + fee(); m_client->lock(); for (auto i: m_myKeys) - if (m_client->state().balance(i.address()) >= totalReq && i.address() != Address(fromUserHex(ui->destination->text().toStdString()))) + if (m_client->state().balance(i.address()) >= totalReq/* && i.address() != Address(fromUserHex(ui->destination->text().toStdString()))*/) { m_client->unlock(); Secret s = i.secret(); - Address r = Address(fromUserHex(ui->destination->text().toStdString())); + Address r = ui->destination->text().size() ? Address(fromUserHex(ui->destination->text().toStdString())) : Address(); m_client->transact(s, r, value(), assemble(ui->data->toPlainText().toStdString())); refresh(); return; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 991839557..9dec365c4 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -116,20 +116,41 @@ bool contains(T const& _t, V const& _v) return false; } +bool BlockChain::attemptImport(bytes const& _block, Overlay const& _stateDB) +{ +#if ETH_CATCH + try +#endif + { + import(_block, _stateDB); + return true; + } +#if ETH_CATCH + catch (...) + { + return false; + } +#endif +} + + void BlockChain::import(bytes const& _block, Overlay const& _db) { // VERIFY: populates from the block and checks the block is internally coherent. BlockInfo bi(&_block); +#if ETH_CATCH try +#endif { bi.verifyInternals(&_block); } +#if ETH_CATCH catch (Exception const& _e) { clog(BlockChainNote) << " Malformed block (" << _e.description() << ")."; throw; } - +#endif auto newHash = eth::sha3(_block); // Check block doesn't already exist first! @@ -151,7 +172,9 @@ void BlockChain::import(bytes const& _block, Overlay const& _db) clog(BlockChainNote) << "Attempting import of " << newHash << "..."; u256 td; +#if ETH_CATCH try +#endif { // Check family: BlockInfo biParent(block(bi.parentHash)); @@ -168,7 +191,7 @@ void BlockChain::import(bytes const& _block, Overlay const& _db) auto tdIncrease = s.playback(&_block, bi, biParent, biGrandParent, true); td = pd.totalDifficulty + tdIncrease; -#if PARANOIA +#if ETH_PARANOIA checkConsistency(); #endif // All ok - insert into DB @@ -182,15 +205,17 @@ void BlockChain::import(bytes const& _block, Overlay const& _db) m_detailsDB->Put(m_writeOptions, ldb::Slice((char const*)&bi.parentHash, 32), (ldb::Slice)eth::ref(m_details[bi.parentHash].rlp())); m_db->Put(m_writeOptions, ldb::Slice((char const*)&newHash, 32), (ldb::Slice)ref(_block)); -#if PARANOIA +#if ETH_PARANOIA checkConsistency(); #endif } - catch (...) +#if ETH_CATCH + catch (Exception const& _e) { - clog(BlockChainNote) << " Malformed block."; + clog(BlockChainNote) << " Malformed block (" << _e.description() << ")."; throw; } +#endif // cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children."; diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 8ad0cb8b8..9576b3259 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -74,7 +74,7 @@ public: void process(); /// Attempt to import the given block. - bool attemptImport(bytes const& _block, Overlay const& _stateDB) { try { import(_block, _stateDB); return true; } catch (...) { return false; } } + bool attemptImport(bytes const& _block, Overlay const& _stateDB); /// Import block into disk-backed DB void import(bytes const& _block, Overlay const& _stateDB); diff --git a/libethereum/BlockInfo.cpp b/libethereum/BlockInfo.cpp index c5f00eab4..cd2323cfa 100644 --- a/libethereum/BlockInfo.cpp +++ b/libethereum/BlockInfo.cpp @@ -152,6 +152,6 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const throw InvalidDifficulty(); // Check timestamp is after previous timestamp. - if (parentHash && _parent.timestamp >= timestamp) + if (parentHash && _parent.timestamp > timestamp) throw InvalidTimestamp(); } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index dc0f78803..fbd2c3e2f 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -32,17 +32,15 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const m_clientVersion(_clientVersion), m_bc(_dbPath), m_stateDB(State::openDB(_dbPath)), - m_s(_us, m_stateDB), - m_mined(_us, m_stateDB) + m_preMine(_us, m_stateDB), + m_postMine(_us, m_stateDB) { Defaults::setDBPath(_dbPath); - // Synchronise the state according to the block chain - i.e. replay all transactions in block chain, in order. - // In practise this won't need to be done since the State DB will contain the keys for the tries for most recent (and many old) blocks. + // Synchronise the state according to the head of the block chain. // TODO: currently it contains keys for *all* blocks. Make it remove old ones. - m_s.sync(m_bc); - m_s.sync(m_tq); - m_mined = m_s; + m_preMine.sync(m_bc); + m_postMine = m_preMine; m_changed = true; static const char* c_threadName = "eth"; @@ -88,7 +86,7 @@ void Client::stopNetwork() void Client::startMining() { m_doMine = true; - m_miningStarted = true; + m_restartMining = true; } void Client::stopMining() @@ -100,7 +98,7 @@ void Client::transact(Secret _secret, Address _dest, u256 _amount, u256s _data) { lock_guard l(m_lock); Transaction t; - t.nonce = m_mined.transactionsFrom(toAddress(_secret)); + t.nonce = m_postMine.transactionsFrom(toAddress(_secret)); t.receiveAddress = _dest; t.value = _amount; t.data = _data; @@ -135,35 +133,35 @@ void Client::work() // Resynchronise state with block chain & trans { lock_guard l(m_lock); - if (m_s.sync(m_bc)) + if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { if (m_doMine) - cnote << "Externally mined block: Restarting mining operation."; + cnote << "New block on chain: Restarting mining operation."; changed = true; - m_miningStarted = true; // need to re-commit to mine. - m_mined = m_s; + m_restartMining = true; // need to re-commit to mine. + m_postMine = m_preMine; } - if (m_mined.sync(m_tq)) + if (m_postMine.sync(m_tq)) { if (m_doMine) cnote << "Additional transaction ready: Restarting mining operation."; changed = true; - m_miningStarted = true; + m_restartMining = true; } } if (m_doMine) { - if (m_miningStarted) + if (m_restartMining) { lock_guard l(m_lock); - m_mined.commitToMine(m_bc); + m_postMine.commitToMine(m_bc); } - m_miningStarted = false; + m_restartMining = false; // Mine for a while. - MineInfo mineInfo = m_mined.mine(100); + MineInfo mineInfo = m_postMine.mine(100); m_mineProgress.best = max(m_mineProgress.best, mineInfo.best); m_mineProgress.current = mineInfo.best; m_mineProgress.requirement = mineInfo.requirement; @@ -172,10 +170,9 @@ void Client::work() { // Import block. lock_guard l(m_lock); - m_bc.attemptImport(m_mined.blockData(), m_stateDB); + m_bc.attemptImport(m_postMine.blockData(), m_stateDB); m_mineProgress.best = 0; m_changed = true; - m_miningStarted = true; // need to re-commit to mine. } } else diff --git a/libethereum/Client.h b/libethereum/Client.h index 2ed0e4a50..2ec04029c 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -90,11 +90,11 @@ public: bool changed() const { auto ret = m_changed; m_changed = false; return ret; } /// Get the object representing the current state of Ethereum. - State const& state() const { return m_s; } + State const& state() const { return m_preMine; } /// Get the object representing the current canonical blockchain. BlockChain const& blockChain() const { return m_bc; } /// Get a map containing each of the pending transactions. - std::map const& pending() const { return m_mined.pending(); } + std::map const& pending() const { return m_postMine.pending(); } void setClientVersion(std::string const& _name) { m_clientVersion = _name; } @@ -117,9 +117,9 @@ public: // Mining stuff: /// Set the coinbase address. - void setAddress(Address _us) { m_s.setAddress(_us); } + void setAddress(Address _us) { m_preMine.setAddress(_us); } /// Get the coinbase address. - Address address() const { return m_s.address(); } + Address address() const { return m_preMine.address(); } /// Start mining. void startMining(); /// Stop mining. @@ -134,8 +134,8 @@ private: BlockChain m_bc; ///< Maintains block database. TransactionQueue m_tq; ///< Maintains list of incoming transactions not yet on the block chain. Overlay m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. - State m_s; ///< The present state of the client. - State m_mined; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). + State m_preMine; ///< The present state of the client. + State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). PeerServer* m_net = nullptr; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. std::thread* m_work; ///< The work thread. @@ -144,7 +144,7 @@ private: enum { Active = 0, Deleting, Deleted } m_workState = Active; bool m_doMine = false; ///< Are we supposed to be mining? MineProgress m_mineProgress; - mutable bool m_miningStarted = false; + mutable bool m_restartMining = false; mutable bool m_changed; }; diff --git a/libethereum/Exceptions.h b/libethereum/Exceptions.h index 023362554..3aa73fcac 100644 --- a/libethereum/Exceptions.h +++ b/libethereum/Exceptions.h @@ -15,10 +15,12 @@ public: class BadHexCharacter: public Exception {}; class NotEnoughCash: public Exception {}; -class BadInstruction: public Exception {}; -class StackTooSmall: public Exception { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; -class OperandOutOfRange: public Exception { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; -class ExecutionException: public Exception {}; + +class VMException: public Exception {}; +class BadInstruction: public VMException {}; +class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; +class OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; + class NoSuchContract: public Exception {}; class ContractAddressCollision: public Exception {}; class FeeTooSmall: public Exception {}; diff --git a/libethereum/Instruction.h b/libethereum/Instruction.h index 71c5d547a..fe5e37edc 100644 --- a/libethereum/Instruction.h +++ b/libethereum/Instruction.h @@ -47,10 +47,8 @@ enum class Instruction: uint8_t GE, EQ, NOT, - MYADDRESS = 0x10, TXSENDER, ///< pushes the transaction sender TXVALUE , ///< pushes the transaction value - TXFEE, ///< pushes the transaction fee TXDATAN, ///< pushes the number of data items TXDATA, ///< pops one item and pushes data item S[-1], or zero if index out of range BLK_PREVHASH, ///< pushes the hash of the previous block (NOT the current one since that's impossible!) @@ -58,6 +56,8 @@ enum class Instruction: uint8_t BLK_TIMESTAMP, ///< pushes the timestamp of the current block BLK_NUMBER, ///< pushes the current block number BLK_DIFFICULTY, ///< pushes the difficulty of the current block + BLK_NONCE, + BASEFEE, SHA256 = 0x20, RIPEMD160, ECMUL, @@ -69,72 +69,72 @@ enum class Instruction: uint8_t PUSH = 0x30, POP, DUP, - DUPN, SWAP, - SWAPN, - LOAD, - STORE, - JMP = 0x40, + MLOAD, + MSTORE, + SLOAD, + SSTORE, + JMP, JMPI, IND, - EXTRO = 0x50, + EXTRO, BALANCE, - MKTX = 0x60, - SUICIDE = 0xff + MKTX, + SUICIDE = 0x3f }; static const std::map c_instructions = { - { "STOP", (Instruction)0x00 }, - { "ADD", (Instruction)0x01 }, - { "SUB", (Instruction)0x02 }, - { "MUL", (Instruction)0x03 }, - { "DIV", (Instruction)0x04 }, - { "SDIV", (Instruction)0x05 }, - { "MOD", (Instruction)0x06 }, - { "SMOD", (Instruction)0x07 }, - { "EXP", (Instruction)0x08 }, - { "NEG", (Instruction)0x09 }, - { "LT", (Instruction)0x0a }, - { "LE", (Instruction)0x0b }, - { "GT", (Instruction)0x0c }, - { "GE", (Instruction)0x0d }, - { "EQ", (Instruction)0x0e }, - { "NOT", (Instruction)0x0f }, - { "MYADDRESS", (Instruction)0x10 }, - { "TXSENDER", (Instruction)0x11 }, - { "TXVALUE", (Instruction)0x12 }, - { "TXFEE", (Instruction)0x13 }, - { "TXDATAN", (Instruction)0x14 }, - { "TXDATA", (Instruction)0x15 }, - { "BLK_PREVHASH", (Instruction)0x16 }, - { "BLK_COINBASE", (Instruction)0x17 }, - { "BLK_TIMESTAMP", (Instruction)0x18 }, - { "BLK_NUMBER", (Instruction)0x19 }, - { "BLK_DIFFICULTY", (Instruction)0x1a }, - { "SHA256", (Instruction)0x20 }, - { "RIPEMD160", (Instruction)0x21 }, - { "ECMUL", (Instruction)0x22 }, - { "ECADD", (Instruction)0x23 }, - { "ECSIGN", (Instruction)0x24 }, - { "ECRECOVER", (Instruction)0x25 }, - { "ECVALID", (Instruction)0x26 }, - { "SHA3", (Instruction)0x27 }, - { "PUSH", (Instruction)0x30 }, - { "POP", (Instruction)0x31 }, - { "DUP", (Instruction)0x32 }, - { "DUPN", (Instruction)0x33 }, - { "SWAP", (Instruction)0x34 }, - { "SWAPN", (Instruction)0x35 }, - { "LOAD", (Instruction)0x36 }, - { "STORE", (Instruction)0x37 }, - { "JMP", (Instruction)0x40 }, - { "JMPI", (Instruction)0x41 }, - { "IND", (Instruction)0x42 }, - { "EXTRO", (Instruction)0x50 }, - { "BALANCE", (Instruction)0x51 }, - { "MKTX", (Instruction)0x60 }, - { "SUICIDE", (Instruction)0xff } + { "STOP", Instruction::STOP }, + { "ADD", Instruction::ADD }, + { "SUB", Instruction::SUB }, + { "MUL", Instruction::MUL }, + { "DIV", Instruction::DIV }, + { "SDIV", Instruction::SDIV }, + { "MOD", Instruction::MOD }, + { "SMOD", Instruction::SMOD }, + { "EXP", Instruction::EXP }, + { "NEG", Instruction::NEG }, + { "LT", Instruction::LT }, + { "LE", Instruction::LE }, + { "GT", Instruction::GT }, + { "GE", Instruction::GE }, + { "EQ", Instruction::EQ }, + { "NOT", Instruction::NOT }, + { "TXSENDER", Instruction::TXSENDER }, + { "TXVALUE", Instruction::TXVALUE }, + { "TXDATAN", Instruction::TXDATAN }, + { "TXDATA", Instruction::TXDATA }, + { "BLK_PREVHASH", Instruction::BLK_PREVHASH }, + { "BLK_COINBASE", Instruction::BLK_COINBASE }, + { "BLK_TIMESTAMP", Instruction::BLK_TIMESTAMP }, + { "BLK_NUMBER", Instruction::BLK_NUMBER }, + { "BLK_DIFFICULTY", Instruction::BLK_DIFFICULTY }, + { "BLK_NONCE", Instruction::BLK_NONCE }, + { "BASFEEE", Instruction::BASEFEE }, + { "SHA256", Instruction::SHA256 }, + { "RIPEMD160", Instruction::RIPEMD160 }, + { "ECMUL", Instruction::ECMUL }, + { "ECADD", Instruction::ECADD }, + { "ECSIGN", Instruction::ECSIGN }, + { "ECRECOVER", Instruction::ECRECOVER }, + { "ECVALID", Instruction::ECVALID }, + { "SHA3", Instruction::SHA3 }, + { "PUSH", Instruction::PUSH }, + { "POP", Instruction::POP }, + { "DUP", Instruction::DUP }, + { "SWAP", Instruction::SWAP }, + { "MLOAD", Instruction::MLOAD }, + { "MSTORE", Instruction::MSTORE }, + { "SLOAD", Instruction::SLOAD }, + { "SSTORE", Instruction::SSTORE }, + { "JMP", Instruction::JMP }, + { "JMPI", Instruction::JMPI }, + { "IND", Instruction::IND }, + { "EXTRO", Instruction::EXTRO }, + { "BALANCE", Instruction::BALANCE }, + { "MKTX", Instruction::MKTX }, + { "SUICIDE", Instruction::SUICIDE } }; u256s assemble(std::string const& _code); diff --git a/libethereum/State.cpp b/libethereum/State.cpp index accb04bcc..93d9fc4d6 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -48,13 +48,13 @@ using namespace eth; u256 const c_stepFee = 1; u256 const c_dataFee = 20; -u256 const c_memoryFee = 5; +u256 const c_memoryFee = 0;//5; memoryFee is 0 for PoC-3 u256 const c_extroFee = 40; u256 const c_cryptoFee = 20; u256 const c_newContractFee = 100; u256 const c_txFee = 100; -u256 const eth::c_genesisDifficulty = (u256)1 << 22; +u256 eth::c_genesisDifficulty = (u256)1 << 22; std::map const& eth::genesisState() { @@ -63,7 +63,7 @@ std::map const& eth::genesisState() { // Initialise. s_ret[Address(fromUserHex("8a40bfaa73256b60764c1bf40675a99083efb075"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); - s_ret[Address(fromUserHex("93658b04240e4bd4046fd2d6d417d20f146f4b43"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); + s_ret[Address(fromUserHex("e6716f9544a56c530d868e4bfbacb172315bdead"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); s_ret[Address(fromUserHex("1e12515ce3e0f817a4ddef9ca55788a1d66bd2df"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); s_ret[Address(fromUserHex("1a26338f0d905e295fccb71fa9ea849ffa12aaf4"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); } @@ -90,8 +90,8 @@ State::State(Address _coinbaseAddress, Overlay const& _db): m_state(&m_db), m_ourAddress(_coinbaseAddress) { - m_blockReward = u256(15000000000) * 100000000; - m_fees.setMultiplier(u256(100000) * 1000000000); + m_blockReward = 1500 * finney; + m_fees.setMultiplier(100 * szabo); secp256k1_start(); @@ -147,6 +147,11 @@ void FeeStructure::setMultiplier(u256 _x) m_txFee = c_txFee * _x; } +u256 FeeStructure::multiplier() const +{ + return m_stepFee / c_stepFee; +} + void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const { auto it = m_cache.find(_a); @@ -171,7 +176,7 @@ void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) con { // Populate memory. assert(it->second.type() == AddressType::Contract); - TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't alter the overlay! :) + TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't alter the overlay! :) map& mem = it->second.setHaveMemory(); for (auto const& i: memdb) if (mem.find(i.first) == mem.end()) @@ -271,6 +276,8 @@ void State::resetCurrent() m_currentBlock.coinbaseAddress = m_ourAddress; m_currentBlock.stateRoot = m_previousBlock.stateRoot; m_currentBlock.parentHash = m_previousBlock.hash; + m_currentBlock.sha3Transactions = h256(); + m_currentBlock.sha3Uncles = h256(); m_state.setRoot(m_currentBlock.stateRoot); } @@ -356,6 +363,9 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _ if (m_currentBlock.parentHash != m_previousBlock.hash) throw InvalidParentHash(); +// cnote << "playback begins:" << m_state.root(); +// cnote << m_state; + // All ok with the block generally. Play back the transactions now... for (auto const& i: RLP(_block)[1]) execute(i.data()); @@ -385,9 +395,10 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _ { cwarn << "Bad state root!"; cnote << "Given to be:" << m_currentBlock.stateRoot; + cnote << TrieDB(&m_db, m_currentBlock.stateRoot); cnote << "Calculated to be:" << rootHash(); cnote << m_state; - cnote << TrieDB(&m_db, m_currentBlock.stateRoot); + cnote << *this; // Rollback the trie. m_db.rollback(); throw InvalidStateRoot(); @@ -414,14 +425,15 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _ // (i.e. all the transactions we executed). void State::commitToMine(BlockChain const& _bc) { - if (m_previousBlock.hash != m_committedPreviousHash) + if (m_currentBlock.sha3Transactions != h256() || m_currentBlock.sha3Uncles != h256()) { - m_committedPreviousHash = m_previousBlock.hash; - cnote << "Commiting to mine on" << m_previousBlock.hash; + Addresses uncleAddresses; + for (auto i: RLP(m_currentUncles)) + uncleAddresses.push_back(i[2].toHash
()); + unapplyRewards(uncleAddresses); } - if (m_currentBlock.sha3Transactions != h256() || m_currentBlock.sha3Uncles != h256()) - return; + cnote << "Commiting to mine on" << m_previousBlock.hash; RLPStream uncles; Addresses uncleAddresses; @@ -459,6 +471,10 @@ void State::commitToMine(BlockChain const& _bc) // Commit any and all changes to the trie that are in the cache, then update the state root accordingly. commit(); + cnote << "stateRoot:" << m_state.root(); +// cnote << m_state; +// cnote << *this; + m_currentBlock.stateRoot = m_state.root(); m_currentBlock.parentHash = m_previousBlock.hash; } @@ -577,7 +593,7 @@ u256 State::contractMemory(Address _id, u256 _memory) const return mit->second; } // Memory not cached - just grab one item from the DB rather than cache the lot. - TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't change the overlay! :) + TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't change the overlay! :) return RLP(memdb.at(_memory)).toInt(); // TODO: CHECK: check if this is actually an RLP decode } @@ -605,6 +621,17 @@ void State::applyRewards(Addresses const& _uncleAddresses) addBalance(m_currentBlock.coinbaseAddress, r); } +void State::unapplyRewards(Addresses const& _uncleAddresses) +{ + u256 r = m_blockReward; + for (auto const& i: _uncleAddresses) + { + subBalance(i, m_blockReward * 3 / 4); + r += m_blockReward / 8; + } + subBalance(m_currentBlock.coinbaseAddress, r); +} + void State::executeBare(Transaction const& _t, Address _sender) { // Entry point for a contract-originated transaction. @@ -634,8 +661,16 @@ void State::executeBare(Transaction const& _t, Address _sender) if (isContractAddress(_t.receiveAddress)) { - MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction. - execute(_t.receiveAddress, _sender, _t.value, _t.data, &feeAdder.fee); + try + { + MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction. + execute(_t.receiveAddress, _sender, _t.value, _t.data, &feeAdder.fee); + } + catch (VMException const& _e) + { + cnote << "VM Exception: " << _e.description(); + throw; + } } } else @@ -690,27 +725,29 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s throw StackTooSmall(_n, stack.size()); }; ensureCached(_myAddress, true, true); - auto& myMemory = m_cache[_myAddress].memory(); + auto& myStore = m_cache[_myAddress].memory(); - auto mem = [&](u256 _n) -> u256 + auto store = [&](u256 _n) -> u256 { - auto i = myMemory.find(_n); - return i == myMemory.end() ? 0 : i->second; + auto i = myStore.find(_n); + return i == myStore.end() ? 0 : i->second; }; - auto setMem = [&](u256 _n, u256 _v) + auto setStore = [&](u256 _n, u256 _v) { if (_v) { - auto it = myMemory.find(_n); - if (it == myMemory.end()) - myMemory.insert(make_pair(_n, _v)); + auto it = myStore.find(_n); + if (it == myStore.end()) + myStore.insert(make_pair(_n, _v)); else - myMemory.at(_n) = _v; + myStore.at(_n) = _v; } else - myMemory.erase(_n); + myStore.erase(_n); }; + map tempMem; + u256 curPC = 0; u256 nextPC = 1; u256 stepCount = 0; @@ -721,21 +758,21 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s bigint minerFee = stepCount > 16 ? m_fees.m_stepFee : 0; bigint voidFee = 0; - auto rawInst = mem(curPC); + auto rawInst = store(curPC); if (rawInst > 0xff) throw BadInstruction(); Instruction inst = (Instruction)(uint8_t)rawInst; switch (inst) { - case Instruction::STORE: + case Instruction::SSTORE: require(2); - if (!mem(stack.back()) && stack[stack.size() - 2]) + if (!store(stack.back()) && stack[stack.size() - 2]) voidFee += m_fees.m_memoryFee; - if (mem(stack.back()) && !stack[stack.size() - 2]) + if (store(stack.back()) && !stack[stack.size() - 2]) voidFee -= m_fees.m_memoryFee; // continue on to... - case Instruction::LOAD: + case Instruction::SLOAD: minerFee += m_fees.m_dataFee; break; @@ -851,9 +888,9 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s stack.back() = stack.back() ? 0 : 1; stack.pop_back(); break; - case Instruction::MYADDRESS: + /*case Instruction::MYADDRESS: stack.push_back((u160)_myAddress); - break; + break;*/ case Instruction::TXSENDER: stack.push_back((u160)_txSender); break; @@ -882,6 +919,12 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s case Instruction::BLK_DIFFICULTY: stack.push_back(m_currentBlock.difficulty); break; + case Instruction::BLK_NONCE: + stack.push_back(m_currentBlock.nonce); + break; + case Instruction::BASEFEE: + stack.push_back(m_fees.multiplier()); + break; case Instruction::SHA256: { uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); @@ -1057,7 +1100,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s } case Instruction::PUSH: { - stack.push_back(mem(curPC + 1)); + stack.push_back(store(curPC + 1)); nextPC = curPC + 2; break; } @@ -1069,15 +1112,15 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s require(1); stack.push_back(stack.back()); break; - case Instruction::DUPN: + /*case Instruction::DUPN: { - auto s = mem(curPC + 1); + auto s = store(curPC + 1); if (s == 0 || s > stack.size()) throw OperandOutOfRange(1, stack.size(), s); stack.push_back(stack[stack.size() - (uint)s]); nextPC = curPC + 2; break; - } + }*/ case Instruction::SWAP: { require(2); @@ -1086,25 +1129,35 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s stack[stack.size() - 2] = d; break; } - case Instruction::SWAPN: + /*case Instruction::SWAPN: { require(1); auto d = stack.back(); - auto s = mem(curPC + 1); + auto s = store(curPC + 1); if (s == 0 || s > stack.size()) throw OperandOutOfRange(1, stack.size(), s); stack.back() = stack[stack.size() - (uint)s]; stack[stack.size() - (uint)s] = d; nextPC = curPC + 2; break; - } - case Instruction::LOAD: + }*/ + case Instruction::MLOAD: + require(1); + stack.back() = tempMem[stack.back()]; + break; + case Instruction::MSTORE: + require(2); + tempMem[stack.back()] = stack[stack.size() - 2]; + stack.pop_back(); + stack.pop_back(); + break; + case Instruction::SLOAD: require(1); - stack.back() = mem(stack.back()); + stack.back() = store(stack.back()); break; - case Instruction::STORE: + case Instruction::SSTORE: require(2); - setMem(stack.back(), stack[stack.size() - 2]); + setStore(stack.back(), stack[stack.size() - 2]); stack.pop_back(); stack.pop_back(); break; @@ -1168,7 +1221,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s { require(1); Address dest = asAddress(stack.back()); - u256 minusVoidFee = myMemory.size() * m_fees.m_memoryFee; + u256 minusVoidFee = myStore.size() * m_fees.m_memoryFee; addBalance(dest, balance(_myAddress) + minusVoidFee); m_cache[_myAddress].kill(); // ...follow through to... diff --git a/libethereum/State.h b/libethereum/State.h index 2713188ae..63520da70 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -39,7 +39,7 @@ namespace eth class BlockChain; -extern const u256 c_genesisDifficulty; +extern u256 c_genesisDifficulty; std::map const& genesisState(); #define ETH_SENDER_PAYS_SETUP 1 @@ -48,6 +48,7 @@ struct FeeStructure { /// The fee structure. Values yet to be agreed on... void setMultiplier(u256 _x); ///< The current block multiplier. + u256 multiplier() const; u256 m_stepFee; u256 m_dataFee; u256 m_memoryFee; @@ -166,9 +167,6 @@ public: /// Get the list of pending transactions. std::map const& pending() const { return m_transactions; } - /// Finalise the block, applying the earned rewards. - void applyRewards(Addresses const& _uncleAddresses); - /// Execute all transactions within a given block. /// @returns the additional total difficulty. /// If the _grandParent is passed, it will check the validity of each of the uncles. @@ -217,6 +215,12 @@ private: /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); + /// Finalise the block, applying the earned rewards. + void applyRewards(Addresses const& _uncleAddresses); + + /// Unfinalise the block, unapplying the earned rewards. + void unapplyRewards(Addresses const& _uncleAddresses); + Overlay m_db; ///< Our overlay for the state tree. TrieDB m_state; ///< Our state tree, as an Overlay DB. std::map m_transactions; ///< The current list of transactions that we've included in the state. @@ -227,7 +231,6 @@ private: BlockInfo m_currentBlock; ///< The current block's information. bytes m_currentBytes; ///< The current block. uint m_currentNumber; - h256 m_committedPreviousHash; ///< Hash of previous block that we are committing to mine. bytes m_currentTxs; bytes m_currentUncles; @@ -256,7 +259,17 @@ inline std::ostream& operator<<(std::ostream& _out, State const& _s) RLP r(i.second); _out << "[ " << (r.itemCount() == 3 ? "CONTRACT] " : " NORMAL] ") << i.first << ": " << std::dec << r[1].toInt() << "@" << r[0].toInt(); if (r.itemCount() == 3) - _out << " &" << r[2].toHash(); + { + _out << " *" << r[2].toHash(); + TrieDB memdb(const_cast(&_s.m_db), r[2].toHash()); // promise we won't alter the overlay! :) + std::map mem; + for (auto const& j: memdb) + { + _out << std::endl << " [" << j.first << ":" << asHex(j.second) << "]"; + mem[j.first] = RLP(j.second).toInt(); + } + _out << std::endl << mem; + } _out << std::endl; } else @@ -272,11 +285,20 @@ inline std::ostream& operator<<(std::ostream& _out, State const& _s) { if (i.second.haveMemory()) { - _out << std::endl; - _out << i.second.memory(); + _out << std::endl << i.second.memory(); } else - _out << " &" << i.second.oldRoot(); + { + _out << " *" << i.second.oldRoot(); + TrieDB memdb(const_cast(&_s.m_db), i.second.oldRoot()); // promise we won't alter the overlay! :) + std::map mem; + for (auto const& j: memdb) + { + _out << std::endl << " [" << j.first << ":" << asHex(j.second) << "]"; + mem[j.first] = RLP(j.second).toInt(); + } + _out << std::endl << mem; + } } _out << std::endl; } @@ -297,7 +319,7 @@ void commit(std::map const& _cache, DB& _db, TrieDB memdb(&_db); + TrieDB memdb(&_db); memdb.init(); for (auto const& j: i.second.memory()) if (j.second) diff --git a/test/vm.cpp b/test/vm.cpp index fafb035f6..55b8d79a2 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -36,13 +36,19 @@ template <> class UnitTest<1> public: int operator()() { + c_genesisDifficulty = (u256)1; + KeyPair p = KeyPair::create(); - Overlay o; + Overlay o(State::openDB("/tmp/vmTest", true)); State s(p.address(), o); + BlockChain bc("/tmp/vmTest", true); cout << s; - s.addBalance(p.address(), Uether); + s.commitToMine(bc); + s.mine(1000000); + bc.attemptImport(s.blockData(), o); + s.sync(bc); cout << s; @@ -50,7 +56,7 @@ public: c.receiveAddress = Address(); c.nonce = 0; - c.data = assemble("txsender load txvalue add txsender store stop"); + c.data = assemble("txsender sload txvalue add txsender sstore stop"); c.value = ether; c.sign(p.secret()); s.execute(c.rlp()); @@ -58,11 +64,14 @@ public: cout << s; - s.commit(); + s.commitToMine(bc); + s.mine(1000000); + bc.attemptImport(s.blockData(), o); + s.sync(bc); cout << s; - cout << s.m_db; +// cout << s.m_db; c.receiveAddress = ca; c.nonce = 1; @@ -73,6 +82,13 @@ public: cout << s; + s.commitToMine(bc); + s.mine(); + bc.attemptImport(s.blockData(), o); + s.sync(bc); + + cout << s; + return 0; } };