diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e585fc42..27bf9d5e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,4 +25,5 @@ endif () add_subdirectory(libethereum) add_subdirectory(test) +#add_subdirectory(eth) diff --git a/eth/CMakeLists.txt b/eth/CMakeLists.txt new file mode 100644 index 000000000..2bb2d56e1 --- /dev/null +++ b/eth/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_policy(SET CMP0015 NEW) + +aux_source_directory(. SRC_LIST) +include_directories(../../secp256k1/include) +include_directories(../libethereum) +link_directories(../libethereum) +link_directories(../../secp256k1) + +add_executable(eth ${SRC_LIST}) + +target_link_libraries(eth secp256k1) +target_link_libraries(eth ethereum) +target_link_libraries(eth gmp) diff --git a/eth/main.cpp b/eth/main.cpp new file mode 100644 index 000000000..e31ab05ea --- /dev/null +++ b/eth/main.cpp @@ -0,0 +1,76 @@ +/* + 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. + + Foobar 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 Foobar. If not, see . +*/ +/** @file main.cpp + * @author Gav Wood + * @date 2014 + * Ethereum client. + */ + +#include "PeerNetwork.h" +#include "BlockChain.h" +#include "State.h" +using namespace std; +using namespace eth; + +int main() +{ + // Our address. + Address us; // TODO: should be loaded from config file/set at command-line. + + BlockChain bc; // TODO: Implement - should look for block database. + + State s(us); +// s.restore(); // TODO: Implement - key optimisation. + + TransactionQueue tq; // TODO: Implement. + + // Synchronise the state according to the block chain - i.e. replay all transactions, in order. Will take a while if the state isn't restored. + s.sync(bc, tq); + + PeerNetwork net; // TODO: Implement - should run in background and send us events when blocks found and allow us to send blocks as required. + while (true) + { + // Process network events. + net.process(); + + // Synchronise block chain with network. + net.sync(bc, tq); + + // Synchronise state to block chain. + // This should remove any transactions on our queue that are included in the block chain. + s.sync(bc, tq); // Resynchronise state with block chain & trans + + if (s.mine(100)) + { + // Mined block + bytes b = s.blockData(); + bc.import(b); + } + } + + return 0; +} + +/* +while (net.incomingTransaction()) +{ + bytes const& tx = net.incomingTransaction(); + s.execute(tx); + net.popIncomingTransaction(); +} + +*/ diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp new file mode 100644 index 000000000..0e1fc3d6d --- /dev/null +++ b/libethereum/BlockChain.cpp @@ -0,0 +1,14 @@ +#include "Common.h" +#include "BlockChain.h" +using namespace std; +using namespace eth; + +BlockChain::BlockChain() +{ +} + +BlockChain::~BlockChain() +{ +} + + diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h new file mode 100644 index 000000000..bb568a1e8 --- /dev/null +++ b/libethereum/BlockChain.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Common.h" + +namespace eth +{ + +/** + * @brief Models the blockchain database. + */ +class BlockChain +{ +public: + BlockChain(); + ~BlockChain(); + + void import(bytes const& _block) {} + +private: +}; + +} + + diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp new file mode 100644 index 000000000..3d714a2da --- /dev/null +++ b/libethereum/PeerNetwork.cpp @@ -0,0 +1,31 @@ +#include "Common.h" +#include "PeerNetwork.h" +using namespace std; +using namespace eth; + +PeerNetwork::PeerNetwork() +{ +} + +PeerNetwork::~PeerNetwork() +{ +} + +void PeerNetwork::process() +{ +} + +void PeerNetwork::sync(BlockChain& _bc, TransactionQueue const&) +{ +/* while (incomingBlock()) + { + // import new block + bytes const& block = net.incomingBlock(); + bc.import(block); + net.popIncomingBlock(); + + // check block chain and make longest given all available blocks. + bc.rejig(); + }*/ +} + diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h new file mode 100644 index 000000000..894cc6db5 --- /dev/null +++ b/libethereum/PeerNetwork.h @@ -0,0 +1,37 @@ +#pragma once + +#include "Common.h" + +namespace eth +{ + +class BlockChain; +class TransactionQueue; + +static const bytes NullBytes; + +class PeerNetwork +{ +public: + PeerNetwork(); + ~PeerNetwork(); + + /// Conduct I/O, polling, syncing, whatever. + /// Ideally all time-consuming I/O is done in a background thread, but you get this call every 100ms or so anyway. + void process(); + + /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. + void sync(BlockChain& _bc, TransactionQueue const&); + + /// Get an incoming transaction from the queue. @returns bytes() if nothing waiting. + bytes const& incomingTransaction() { return NullBytes; } + + /// Remove incoming transaction from the queue. Make sure you've finished with the data from any previous incomingTransaction() calls. + void popIncomingTransaction() {} + +private: +}; + +} + + diff --git a/libethereum/State.cpp b/libethereum/State.cpp index a3da5d066..693c45b9b 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -118,7 +118,7 @@ void State::execute(Transaction const& _t, Address _sender) if (_t.receiveAddress) { - assert(subBalance(_sender, _t.value + _t.fee)); + subBalance(_sender, _t.value + _t.fee); addBalance(_t.receiveAddress, _t.value); addBalance(m_minerAddress, _t.fee); @@ -141,7 +141,7 @@ void State::execute(Transaction const& _t, Address _sender) auto& mem = m_current[newAddress].memory(); for (uint i = 0; i < _t.data.size(); ++i) mem[i] = _t.data[i]; - assert(subBalance(_sender, _t.value + _t.fee)); + subBalance(_sender, _t.value + _t.fee); addBalance(newAddress, _t.value); addBalance(m_minerAddress, _t.fee); } diff --git a/libethereum/State.h b/libethereum/State.h index c577e9bd6..f6d8778d2 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -34,6 +34,21 @@ namespace eth { +class BlockChain; + +// TODO: Repot. +/** + * @brief A queue of Transactions, each stored as RLP. + */ +class TransactionQueue +{ +public: + void sync(BlockChain const& _bc) {} + +private: + std::vector m_data; +}; + /** * @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). @@ -49,30 +64,47 @@ public: /// Verify a given block. bool verify(bytes const& _block, uint _number = 0); + /// Attempt to find valid nonce for block that this state represents. + /// @param _msTimeout Timeout before return in milliseconds. + /// @returns true if it got lucky. + bool mine(uint _msTimeout = 1000) const { (void)_msTimeout; return false; } + + /// Get the complete current block, including valid nonce. + bytes blockData() const { return bytes(); } + + /// Sync our state with the block chain. + /// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue. + void sync(BlockChain const& _bc, TransactionQueue const& _tq) {} + /// Execute a given transaction. bool execute(bytes const& _rlp) { try { Transaction t(_rlp); execute(t, t.sender()); } catch (...) { return false; } } /// Check if the address is a valid normal (non-contract) account address. bool isNormalAddress(Address _address) const; + /// Check if the address is a valid contract's address. bool isContractAddress(Address _address) const; /// Get an account's balance. + /// @returns 0 if the address has never been used. u256 balance(Address _id) const; /// Add some amount to balance. + /// Will initialise the address if it has never been used. void addBalance(Address _id, u256 _amount); /** Subtract some amount from balance. - * @throws NotEnoughCash if balance of @a _id is less than @a _value. + * @throws NotEnoughCash if balance of @a _id is less than @a _value (or has never been used). * @note We use bigint here as we don't want any accidental problems with negative numbers. */ void subBalance(Address _id, bigint _value); /// Get the value of a memory position of a contract. + /// @returns 0 if no contract exists at that address. u256 contractMemory(Address _contract, u256 _memory) const; /// 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 _address) const; private: @@ -93,6 +125,8 @@ private: // TODO: std::hash
and then move to unordered_map. // Will need to sort on hash construction. std::map m_current; ///< The current state. We work with a C++ hash map rather than a Trie. + std::vector m_transactions; ///< The current list of transactions that we've included in the state. + BlockInfo m_previousBlock; ///< The previous block's information. BlockInfo m_currentBlock; ///< The current block's information.