From af65ddc4894ad05d5d7106aa1497a0929bc0ef83 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 1 Mar 2014 16:32:33 +0000 Subject: [PATCH 01/28] TODO --- TODO | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index cabece70e..01d9c9e6e 100644 --- a/TODO +++ b/TODO @@ -44,12 +44,17 @@ Robustness - Better handling of corrupt blocks. - Kill DB & restart. +GUI: +- Turn on/off debug channels. + +For PoC-4: + GUI - Make address/block chain list model-based, JIT populated. - Make everything else model-based - Qt/QML class. -- Turn on/off debug channels. +Kill old DBs when protocol different. ### Marko From a5dba4d35713c1db20b75a5c3e3f76209d80f6e4 Mon Sep 17 00:00:00 2001 From: Tim Hughes Date: Sat, 1 Mar 2014 16:54:35 +0000 Subject: [PATCH 02/28] Fixed implicit type conversions. --- test/hexPrefix.cpp | 2 +- test/trie.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hexPrefix.cpp b/test/hexPrefix.cpp index 10b839a75..b8bc1f48c 100644 --- a/test/hexPrefix.cpp +++ b/test/hexPrefix.cpp @@ -46,7 +46,7 @@ public: cnote << i.first; bytes v; for (auto& i: o["seq"].get_array()) - v.push_back(i.get_int()); + v.push_back((byte)i.get_int()); auto e = hexPrefixEncode(v, o["term"].get_bool()); if (!o["out"].is_null() && o["out"].get_str() != asHex(e)) { diff --git a/test/trie.cpp b/test/trie.cpp index 26a072600..3092238ce 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -53,7 +53,7 @@ public: vector> ss; for (auto& i: o["in"].get_obj()) ss.push_back(make_pair(i.first, i.second.get_str())); - for (unsigned j = 0; j < fac(ss.size()); ++j) + for (unsigned j = 0; j < fac((unsigned)ss.size()); ++j) { next_permutation(ss.begin(), ss.end()); BasicMap m; From 3c15f44bc0bfaa18ddd62063de5d645035adfef1 Mon Sep 17 00:00:00 2001 From: Tim Hughes Date: Sat, 1 Mar 2014 17:48:33 +0000 Subject: [PATCH 03/28] Generate BuildInfo.h on VS2013 (using a short lua script to avoid cmake for now). --- windows/Alethzero.vcxproj | 33 +++++++++++++++++++++++++++---- windows/Alethzero.vcxproj.filters | 3 +++ windows/BuildInfo.lua | 25 +++++++++++++++++++++++ windows/Ethereum.vcxproj | 25 +++++++++++++++++++++++ windows/Ethereum.vcxproj.filters | 16 +++++++++++++++ windows/LibEthereum.props | 6 +++++- windows/UseQt.props | 6 +----- 7 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 windows/BuildInfo.lua create mode 100644 windows/Ethereum.vcxproj.filters diff --git a/windows/Alethzero.vcxproj b/windows/Alethzero.vcxproj index 334a5147e..69f65fab1 100644 --- a/windows/Alethzero.vcxproj +++ b/windows/Alethzero.vcxproj @@ -170,17 +170,17 @@ - "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" $(IntDir)moc_%(FileName).cpp - "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" - "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" - "$(Lua)" moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" + $(Lua) moc.lua "$(QtBin)/moc" "$(IntDir)moc_%(FileName).cpp" "@(ClCompile->'%(AdditionalIncludeDirectories)');$(IncludePath)" "@(ClCompile->'%(PreprocessorDefinitions)');_MSC_VER=1800" "%(FullPath)" $(IntDir)moc_%(FileName).cpp @@ -206,6 +206,31 @@ $(IntDir)ui_%(FileName).h + + + Document + $(Lua) "%(FullPath)" "$(IntDir)%(FileName).h" + + + $(Lua) "%(FullPath)" "$(IntDir)%(FileName).h" + + + $(Lua) "%(FullPath)" "$(IntDir)%(FileName).h" + + + $(Lua) "%(FullPath)" "$(IntDir)%(FileName).h" + + + $(IntDir)%(FileName).h + $(IntDir)%(FileName).h + $(IntDir)%(FileName).h + $(IntDir)%(FileName).h + ../.git/index + ../.git/index + ../.git/index + ../.git/index + + diff --git a/windows/Alethzero.vcxproj.filters b/windows/Alethzero.vcxproj.filters index ecf9afee0..6a1228b36 100644 --- a/windows/Alethzero.vcxproj.filters +++ b/windows/Alethzero.vcxproj.filters @@ -24,5 +24,8 @@ + + Windows + \ No newline at end of file diff --git a/windows/BuildInfo.lua b/windows/BuildInfo.lua new file mode 100644 index 000000000..47b23a795 --- /dev/null +++ b/windows/BuildInfo.lua @@ -0,0 +1,25 @@ + +function os.capture(cmd) + local f = io.popen(cmd, 'r') + if (f) then + local s = f:read('*a') + if (f:close()) then + return s + end + end + return nil +end + +hash = (os.capture("git rev-parse HEAD") or "UnknownRevision"):gsub("\n$", "") +clean = ((os.capture("git diff --name-only") or "0"):gsub("\n$", "") == "") and "1" or "0" + +local output = io.open(arg[1], "w") +if (output) then + output:write("// This file was automatically generated by buildinfo.lua\n#pragma once\n\n") + output:write("#define ETH_COMMIT_HASH "..hash.."\n") + output:write("#define ETH_CLEAN_REPO "..clean.."\n") + output:close() +end + + + diff --git a/windows/Ethereum.vcxproj b/windows/Ethereum.vcxproj index 023424879..7b36e9d53 100644 --- a/windows/Ethereum.vcxproj +++ b/windows/Ethereum.vcxproj @@ -160,6 +160,31 @@ + + + Document + $(Lua) "%(FullPath)" "$(IntDir)%(FileName).h" + + + $(IntDir)%(FileName).h + $(Lua) "%(FullPath)" "$(IntDir)%(FileName).h" + + + $(IntDir)%(FileName).h + $(Lua) "%(FullPath)" "$(IntDir)%(FileName).h" + + + $(IntDir)%(FileName).h + $(Lua) "%(FullPath)" "$(IntDir)%(FileName).h" + + + $(IntDir)%(FileName).h + ../.git/index + ../.git/index + ../.git/index + ../.git/index + + diff --git a/windows/Ethereum.vcxproj.filters b/windows/Ethereum.vcxproj.filters new file mode 100644 index 000000000..0bee64924 --- /dev/null +++ b/windows/Ethereum.vcxproj.filters @@ -0,0 +1,16 @@ + + + + + + + + {ed0eafbf-bbfb-4700-b7c0-9b58049cc681} + + + + + Windows + + + \ No newline at end of file diff --git a/windows/LibEthereum.props b/windows/LibEthereum.props index 3de930166..38c353828 100644 --- a/windows/LibEthereum.props +++ b/windows/LibEthereum.props @@ -3,6 +3,7 @@ ../../boost + "../../lua/lua" ..\..\_build\$(ProjectName)\$(Platform)_$(Configuration)\ @@ -14,7 +15,7 @@ Level4 true false - include/$(ProjectName);../libethereum;$(BoostDir);../../leveldb/include;../../cryptopp;../secp256k1;../../miniupnp + include/$(ProjectName);$(IntDir);../libethereum;$(BoostDir);../../leveldb/include;../../cryptopp;../secp256k1;../../miniupnp ETH_BUILD_PLATFORM=Windows/VS2013;ETH_BUILD_TYPE=$(Configuration)-$(Platform);STATICLIB;LEVELDB_PLATFORM_WINDOWS;USE_NUM_BOOST;USE_FIELD_10X26;USE_FIELD_INV_BUILTIN;_WIN32_WINNT=0x0501;WIN32;%(PreprocessorDefinitions) true true @@ -27,5 +28,8 @@ $(BoostDir) + + $(Lua) + \ No newline at end of file diff --git a/windows/UseQt.props b/windows/UseQt.props index 9ced7563f..b3a177623 100644 --- a/windows/UseQt.props +++ b/windows/UseQt.props @@ -6,13 +6,12 @@ $(QtDir)/qtbase/bin $(QtDir)/qtbase/include;../../Qt/Src/qtbase/include $(QtDir)/qtbase/lib;$(QtDir)/qtbase/plugins/platforms - ../../lua/lua - ..;$(IntDir);$(QtInclude);%(AdditionalIncludeDirectories) + ..;$(QtInclude);%(AdditionalIncludeDirectories) $(QtLib);%(AdditionalLibraryDirectories) @@ -31,8 +30,5 @@ $(QtLib) - - $(Lua) - \ No newline at end of file From 7e452b55b7e261a1435386b83dd77fe6b4927fca Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 1 Mar 2014 20:09:18 +0000 Subject: [PATCH 04/28] Better handling of git versioning stuff. --- CMakeLists.txt | 28 ++++++++++++++++++---------- release.sh | 9 +++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 090fdc576..b1c31b9a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,19 +141,27 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Generate header file containing useful build information -execute_process(COMMAND git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git --work-tree=${CMAKE_CURRENT_SOURCE_DIR} rev-parse HEAD OUTPUT_VARIABLE ETH_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git --work-tree=${CMAKE_CURRENT_SOURCE_DIR} diff --shortstat OUTPUT_VARIABLE ETH_LOCAL_CHANGES OUTPUT_STRIP_TRAILING_WHITESPACE) -if("x${ETH_LOCAL_CHANGES}" STREQUAL "x") - set(ETH_CLEAN_REPO 1) +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.h) else() - set(ETH_CLEAN_REPO 0) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) + execute_process(COMMAND git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git --work-tree=${CMAKE_CURRENT_SOURCE_DIR} rev-parse HEAD OUTPUT_VARIABLE ETH_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git --work-tree=${CMAKE_CURRENT_SOURCE_DIR} diff --shortstat OUTPUT_VARIABLE ETH_LOCAL_CHANGES OUTPUT_STRIP_TRAILING_WHITESPACE) + if("x${ETH_LOCAL_CHANGES}" STREQUAL "x") + set(ETH_CLEAN_REPO 1) + else() + set(ETH_CLEAN_REPO 0) + endif() + message("Commit Hash: ${ETH_COMMIT_HASH} (Clean: ${ETH_CLEAN_REPO} - ${ETH_LOCAL_CHANGES})") + else() + message("Unknown repo.") + set(ETH_COMMIT_HASH 0) + set(ETH_CLEAN_REPO 1) + endif() + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/BuildInfo.h "// This file was automatically generated by cmake\n#pragma once\n\n") + file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/BuildInfo.h "#define ETH_COMMIT_HASH ${ETH_COMMIT_HASH}\n#define ETH_CLEAN_REPO ${ETH_CLEAN_REPO}\n#define ETH_BUILD_TYPE ${CMAKE_BUILD_TYPE}\n#define ETH_BUILD_PLATFORM ${ETH_BUILD_PLATFORM}\n") + include_directories(${CMAKE_CURRENT_BINARY_DIR}) endif() -message("Commit Hash: ${ETH_COMMIT_HASH} (Clean: ${ETH_CLEAN_REPO} - ${ETH_LOCAL_CHANGES})") -file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/BuildInfo.h "// This file was automatically generated by cmake\n#pragma once\n\n") -file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/BuildInfo.h "#define ETH_COMMIT_HASH ${ETH_COMMIT_HASH}\n#define ETH_CLEAN_REPO ${ETH_CLEAN_REPO}\n#define ETH_BUILD_TYPE ${CMAKE_BUILD_TYPE}\n#define ETH_BUILD_PLATFORM ${ETH_BUILD_PLATFORM}\n") -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - add_subdirectory(secp256k1) add_subdirectory(libethereum) add_subdirectory(test) diff --git a/release.sh b/release.sh index 8cf59acf5..712e2540a 100755 --- a/release.sh +++ b/release.sh @@ -35,6 +35,14 @@ git checkout "$branch" archdir="cpp-ethereum-$version" archfile="$archdir.tar.bz2" +echo Making BuildInfo... +mkdir build +cd build +cmake .. +cd .. +cp build/BuildInfo.h . +rm -rf build + echo Cleaning backup files... find . | grep \~ | xargs rm -f @@ -51,6 +59,7 @@ mv cpp-ethereum $archdir echo Creating archive... tar c $archdir | bzip2 -- > $archfile +shasum $archfile [[ ! "$version" == "" ]] && ln -sf $archfile "cpp-ethereum_$version.orig.tar.bz2" From f4f6230e5d7d33ea1c1ec6df40512c6bb913a4c5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 2 Mar 2014 10:45:10 +0000 Subject: [PATCH 05/28] TODO --- TODO | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/TODO b/TODO index 01d9c9e6e..80cc4a4b7 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,20 @@ ### UP FOR GRABS -Tests -- Use standard tests. +For PoC-4: + +GUI +- Make address/block chain list model-based, JIT populated. +- Make everything else model-based +- Qt/QML class. + +Robustness +- Store version alongside BC DB. Kill old DBs when protocol different. Remove block chain on protocol change (i.e. store protocol with block chain). + +Cleanups & caching +- All caches should flush unused data (I'm looking at you, BlockChain) to avoid memory overload. + + +Generally: Crypto stuff: - kFromMessage @@ -21,7 +34,6 @@ Network: - Ignore transactions with future nonces until address's nonce changes. 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 @@ -32,29 +44,19 @@ THREAD-SAFETY General: - Better logging. - Colours. -- Move over to new system. -- Remove block chain on protocol change (i.e. store protocol with block chain). + - Move over to new system. Robustness - Remove aborts - Recover from all exceptions. - Especially RLP & other I/O. - RLP should never assert; only throw. -- Store version alongside BC DB. - Better handling of corrupt blocks. - Kill DB & restart. GUI: - Turn on/off debug channels. -For PoC-4: - -GUI -- Make address/block chain list model-based, JIT populated. -- Make everything else model-based -- Qt/QML class. - -Kill old DBs when protocol different. ### Marko From 7e38de27108f231639a80cfa069c3d99e992f023 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 3 Mar 2014 12:21:23 +0000 Subject: [PATCH 06/28] Avoid reprocessing future transactions until it's possible they're valid. --- libethereum/PeerNetwork.cpp | 5 ++-- libethereum/State.cpp | 44 ++++++++++++++++++++------------ libethereum/TransactionQueue.cpp | 17 ++++++++++++ libethereum/TransactionQueue.h | 6 ++++- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index c77af38eb..c7d699461 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -830,9 +830,8 @@ void PeerServer::ensureAccepting() { clog(NetWarn) << "ERROR: " << _e.what(); } - m_accepting = false; - if (m_mode == NodeMode::PeerServer || m_peers.size() < m_idealPeerCount * 2) + if (ec.value() != 1 && m_mode == NodeMode::PeerServer || m_peers.size() < m_idealPeerCount * 2) ensureAccepting(); }); } @@ -926,7 +925,7 @@ bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) { for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it) if (_tq.import(*it)) - ret = true; + {}//ret = true; // just putting a transaction in the queue isn't enough to change the state - it might have an invalid nonce... else m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on. m_incomingTransactions.clear(); diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 74db7aeb4..f14c8c18c 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -288,31 +288,41 @@ bool State::sync(TransactionQueue& _tq) // TRANSACTIONS bool ret = false; auto ts = _tq.transactions(); - for (auto const& i: ts) + vector> futures; + + for (int goodTxs = 1; goodTxs;) { - if (!m_transactionSet.count(i.first)) + goodTxs = 0; + for (auto const& i: ts) { - // don't have it yet! Execute it now. - try + if (!m_transactionSet.count(i.first)) { - execute(i.second); - ret = true; - } - catch (InvalidNonce const& in) - { - if (in.required > in.candidate) + // don't have it yet! Execute it now. + try + { + execute(i.second); + ret = true; + _tq.noteGood(i); + ++goodTxs; + } + catch (InvalidNonce const& in) + { + if (in.required > in.candidate) + { + // too old + _tq.drop(i.first); + ret = true; + } + else + _tq.setFuture(i); + } + catch (std::exception const&) { - // too old + // Something else went wrong - drop it. _tq.drop(i.first); ret = true; } } - catch (std::exception const&) - { - // Something else went wrong - drop it. - _tq.drop(i.first); - ret = true; - } } } return ret; diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 2b908d8b2..1c6144eab 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -51,3 +51,20 @@ bool TransactionQueue::import(bytes const& _block) return true; } + +void TransactionQueue::setFuture(std::pair const& _t) +{ + if (m_data.count(_t.first)) + { + m_data.erase(_t.first); + m_future.insert(make_pair(Transaction(_t.second).sender(), _t)); + } +} + +void TransactionQueue::noteGood(std::pair const& _t) +{ + auto r = m_future.equal_range(Transaction(_t.second).sender()); + for (auto it = r.first; it != r.second; ++it) + m_data.insert(_t); + m_future.erase(r.first); +} diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index 8f2ccaef1..89e889cc1 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -40,14 +40,18 @@ public: void drop(h256 _txHash) { m_data.erase(_txHash); } std::map const& transactions() const { return m_data; } + void setFuture(std::pair const& _t); + void noteGood(std::pair const& _t); + Transactions interestQueue() { Transactions ret; swap(ret, m_interestQueue); return ret; } void pushInterest(Address _a) { m_interest[_a]++; } void popInterest(Address _a) { if (m_interest[_a] > 1) m_interest[_a]--; else if (m_interest[_a]) m_interest.erase(_a); } private: - std::map m_data; ///< the queue. + std::map m_data; ///< Map of SHA3(tx) to tx. Transactions m_interestQueue; std::map m_interest; + std::multimap> m_future; ///< For transactions that have a future nonce; we map their sender address to the tx stuff, and insert once the sender has a valid TX. }; } From 245f711c310b0a6cb3ae91552e1f8ad0558f8b75 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 3 Mar 2014 13:25:19 +0000 Subject: [PATCH 07/28] TODO. --- TODO | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/TODO b/TODO index 80cc4a4b7..9dac30894 100644 --- a/TODO +++ b/TODO @@ -7,6 +7,14 @@ GUI - Make everything else model-based - Qt/QML class. +Core +- Signalling for changes to State. + +THREAD-SAFETY +- BlockChain +- TransactionQueue +- State + Robustness - Store version alongside BC DB. Kill old DBs when protocol different. Remove block chain on protocol change (i.e. store protocol with block chain). @@ -36,11 +44,6 @@ Network: Cleanups & caching - 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 -- State - General: - Better logging. - Colours. From da4144bbb628cc9208c4585a5dfc240e62e55402 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 3 Mar 2014 15:17:49 +0000 Subject: [PATCH 08/28] Better handling of bad transactions. --- TODO | 99 -------------------------------- libethereum/Exceptions.h | 2 +- libethereum/PeerNetwork.cpp | 5 +- libethereum/Transaction.cpp | 22 ++++--- libethereum/TransactionQueue.cpp | 7 ++- 5 files changed, 26 insertions(+), 109 deletions(-) delete mode 100644 TODO diff --git a/TODO b/TODO deleted file mode 100644 index 9dac30894..000000000 --- a/TODO +++ /dev/null @@ -1,99 +0,0 @@ -### UP FOR GRABS - -For PoC-4: - -GUI -- Make address/block chain list model-based, JIT populated. -- Make everything else model-based -- Qt/QML class. - -Core -- Signalling for changes to State. - -THREAD-SAFETY -- BlockChain -- TransactionQueue -- State - -Robustness -- Store version alongside BC DB. Kill old DBs when protocol different. Remove block chain on protocol change (i.e. store protocol with block chain). - -Cleanups & caching -- All caches should flush unused data (I'm looking at you, BlockChain) to avoid memory overload. - - -Generally: - -Crypto stuff: -- kFromMessage -- Check all the tweak instructions. - -Network: -- *** Exponential backoff on bad connection. -- *** Handle exception when no network. -- NotInChain will be very bad for new peers - it'll run through until the genesis. - - Check how many it has first. -- Crypto on network - use id as public key? -- Make work with IPv6 -- Peers rated. - - Useful/useless - new blocks/transactions or useful peers? - - Solid communications? -- Strategy for peer suggestion? -- Ignore transactions with future nonces until address's nonce changes. - -Cleanups & caching -- 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). - -General: -- Better logging. - - Colours. - - Move over to new system. - -Robustness -- Remove aborts -- Recover from all exceptions. - - Especially RLP & other I/O. -- RLP should never assert; only throw. -- Better handling of corrupt blocks. - - Kill DB & restart. - -GUI: -- Turn on/off debug channels. - - -### Marko - -Ubuntu builds -- Raring (branch, local, x64 only :-( ) -- Quantal (branch) (Launchpad) -- Saucy (master) (Launchpad) - -### Alex - -Mac build. -Mac build instructions. - -### Eric - -Windows XC build. -Windows XC build instructions. - -### Tim/Harv - -Windows MSVC build. -Windows MSVC build instructions. - -LATER: - -Trie on DB. -- Move the restore point stuff into block restore points - - i.e. keep all nodes from last 127 blocks with counter, at 128, kill but keep every (60*24*7)th or so i.e. one per week as a restore point. - - maybe allow this to be configured. - - -### TIM - -Stateful Miner class. - -Better Mod-Exp. - diff --git a/libethereum/Exceptions.h b/libethereum/Exceptions.h index 2857102c9..e55530dfe 100644 --- a/libethereum/Exceptions.h +++ b/libethereum/Exceptions.h @@ -31,7 +31,7 @@ class NoSuchContract: public Exception {}; class ContractAddressCollision: public Exception {}; class FeeTooSmall: public Exception {}; class InvalidSignature: public Exception {}; -class InvalidTransactionFormat: public Exception {}; +class InvalidTransactionFormat: public Exception { public: InvalidTransactionFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid transaction format: Bad field " + toString(m_f) + " (" + asHex(m_d) + ")"; } }; class InvalidBlockFormat: public Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block format: Bad field " + toString(m_f) + " (" + asHex(m_d) + ")"; } }; class InvalidBlockHeaderFormat: public Exception { public: InvalidBlockHeaderFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block header format: Bad field " + toString(m_f) + " (" + asHex(m_d) + ")"; } }; class InvalidUnclesHash: public Exception {}; diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index c7d699461..3bc9cdcc0 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -591,6 +591,8 @@ void PeerSession::doRead() { if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) { + disconnect(BadProtocol); + return; clogS(NetWarn) << "Out of alignment. Skipping: " << hex << showbase << (int)m_incoming[0] << dec; memmove(m_incoming.data(), m_incoming.data() + 1, m_incoming.size() - 1); m_incoming.resize(m_incoming.size() - 1); @@ -610,6 +612,7 @@ void PeerSession::doRead() cerr << "Received " << len << ": " << asHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; cwarn << "INVALID MESSAGE RECEIVED"; disconnect(BadProtocol); + return; } else { @@ -831,7 +834,7 @@ void PeerServer::ensureAccepting() clog(NetWarn) << "ERROR: " << _e.what(); } m_accepting = false; - if (ec.value() != 1 && m_mode == NodeMode::PeerServer || m_peers.size() < m_idealPeerCount * 2) + if (ec.value() != 1 && (m_mode == NodeMode::PeerServer || m_peers.size() < m_idealPeerCount * 2)) ensureAccepting(); }); } diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index cf8ff3140..e751815af 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -30,14 +30,22 @@ using namespace eth; Transaction::Transaction(bytesConstRef _rlpData) { + int field = 0; RLP rlp(_rlpData); - nonce = rlp[0].toInt(); - receiveAddress = rlp[1].toHash
(); - value = rlp[2].toInt(); - data.reserve(rlp[3].itemCountStrict()); - for (auto const& i: rlp[3]) - data.push_back(i.toInt()); - vrs = Signature{ rlp[4].toInt(), rlp[5].toInt(), rlp[6].toInt() }; + try + { + nonce = rlp[field = 0].toInt(); + receiveAddress = rlp[field = 1].toHash
(); + value = rlp[field = 2].toInt(); + data.reserve(rlp[field = 3].itemCountStrict()); + for (auto const& i: rlp[3]) + data.push_back(i.toInt()); + vrs = Signature{ rlp[field = 4].toInt(), rlp[field = 5].toInt(), rlp[field = 6].toInt() }; + } + catch (RLPException const&) + { + throw InvalidTransactionFormat(field, rlp[field].data()); + } } Address Transaction::safeSender() const noexcept diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 1c6144eab..1b7c57ee2 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -43,9 +43,14 @@ bool TransactionQueue::import(bytes const& _block) // If valid, append to blocks. m_data[h] = _block; } + catch (InvalidTransactionFormat const& _e) + { + cwarn << "Ignoring invalid transaction: " << _e.description(); + return false; + } catch (std::exception const& _e) { - cout << "*** Ignoring invalid transaction: " << _e.what(); + cwarn << "Ignoring invalid transaction: " << _e.what(); return false; } From 491bb2ed9547cdd69c99ccfc5087b35384972811 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 3 Mar 2014 18:10:18 +0000 Subject: [PATCH 09/28] Preparation for 0.3.11 --- libethereum/Common.h | 2 +- libethereum/PeerNetwork.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libethereum/Common.h b/libethereum/Common.h index 98f7707f5..df1d8569e 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -24,7 +24,7 @@ #pragma once // define version -#define ETH_VERSION 0.4.0 +#define ETH_VERSION 0.3.11 // way to many uint to size_t warnings in 32 bit build #ifdef _M_IX86 diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index 3bc9cdcc0..713441de4 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -43,7 +43,7 @@ using namespace eth; #define clogS(X) eth::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -static const int c_protocolVersion = 7; +static const int c_protocolVersion = 8; static const eth::uint c_maxHashes = 32; ///< Maximum number of hashes GetChain will ever send. static const eth::uint c_maxBlocks = 32; ///< Maximum number of blocks Blocks will ever send. BUG: if this gets too big (e.g. 2048) stuff starts going wrong. From b63b6251507196889c7fe23dca4d55345d1d29ba Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Mon, 3 Mar 2014 10:49:46 -0800 Subject: [PATCH 10/28] Generate BuildInfo.h --- .gitignore | 3 +++ BuildInfo.sh | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 22 ++------------------ 3 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 BuildInfo.sh diff --git a/.gitignore b/.gitignore index 90c2da58c..bcdbfa12d 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ ipch *.opensdf *.suo +# Generated headers +*.h + *.user *.user.* *~ diff --git a/BuildInfo.sh b/BuildInfo.sh new file mode 100644 index 000000000..0370297df --- /dev/null +++ b/BuildInfo.sh @@ -0,0 +1,55 @@ +CURRENT_SOURCE_DIR=$1 +CURRENT_BINARY_DIR=$2 +BUILD_TYPE=$3 +BUILD_PLATFORM=$4 + +echo "Current source dir: $CURRENT_SOURCE_DIR" +echo "Current binary dir: $CURRENT_BINARY_DIR" +echo "Build type: $BUILD_TYPE" +echo "Build platform: $BUILD_PLATFORM" + +if [[ -e "$CURRENT_SOURCE_DIR/BuildInfo.h" ]] +then + echo "Using existing BuildInfo.h" + cp $CURRENT_SOURCE_DIR/BuildInfo.h $CURRENT_BINARY_DIR/BuildInfo.h.tmp +else + if [[ -e "$CURRENT_SOURCE_DIR/.git" ]] + then + ETH_COMMIT_HASH=$(git --git-dir=$CURRENT_SOURCE_DIR/.git --work-tree=$CURRENT_SOURCE_DIR rev-parse HEAD) + ETH_LOCAL_CHANGES=$(git --git-dir=$CURRENT_SOURCE_DIR/.git --work-tree=$CURRENT_SOURCE_DIR diff --shortstat) + if [[ -z "$ETH_LOCAL_CHANGES" ]] + then + ETH_CLEAN_REPO=1 + else + ETH_CLEAN_REPO=0 + fi + + echo "Commit hash: ${ETH_COMMIT_HASH} (Clean: ${ETH_CLEAN_REPO} - ${ETH_LOCAL_CHANGES})" + else + echo "Unknown repo." + ETH_COMMIT_HASH=0 + ETH_CLEAN_REPO=1 + fi + + echo "// This file was automatically generated by cmake" > $CURRENT_BINARY_DIR/BuildInfo.h.tmp + echo "" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp + echo "#pragma once" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp + echo "" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp + echo "#define ETH_COMMIT_HASH $ETH_COMMIT_HASH" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp + echo "#define ETH_CLEAN_REPO $ETH_CLEAN_REPO" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp + echo "#define ETH_BUILD_TYPE $BUILD_TYPE" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp + echo "#define ETH_BUILD_PLATFORM $BUILD_PLATFORM" >> $CURRENT_BINARY_DIR/BuildInfo.h.tmp +fi + +if [[ -e "$CURRENT_BINARY_DIR/BuildInfo.h" ]] +then + DIFF=$(diff $CURRENT_BINARY_DIR/BuildInfo.h $CURRENT_BINARY_DIR/BuildInfo.h.tmp) + if [[ -z "$DIFF" ]] + then + rm $CURRENT_BINARY_DIR/BuildInfo.h.tmp + else + mv $CURRENT_BINARY_DIR/BuildInfo.h.tmp $CURRENT_BINARY_DIR/BuildInfo.h + fi +else + mv $CURRENT_BINARY_DIR/BuildInfo.h.tmp $CURRENT_BINARY_DIR/BuildInfo.h +fi diff --git a/CMakeLists.txt b/CMakeLists.txt index b1c31b9a4..c715f1344 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,26 +141,8 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Generate header file containing useful build information -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.h) -else() - if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) - execute_process(COMMAND git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git --work-tree=${CMAKE_CURRENT_SOURCE_DIR} rev-parse HEAD OUTPUT_VARIABLE ETH_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git --work-tree=${CMAKE_CURRENT_SOURCE_DIR} diff --shortstat OUTPUT_VARIABLE ETH_LOCAL_CHANGES OUTPUT_STRIP_TRAILING_WHITESPACE) - if("x${ETH_LOCAL_CHANGES}" STREQUAL "x") - set(ETH_CLEAN_REPO 1) - else() - set(ETH_CLEAN_REPO 0) - endif() - message("Commit Hash: ${ETH_COMMIT_HASH} (Clean: ${ETH_CLEAN_REPO} - ${ETH_LOCAL_CHANGES})") - else() - message("Unknown repo.") - set(ETH_COMMIT_HASH 0) - set(ETH_CLEAN_REPO 1) - endif() - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/BuildInfo.h "// This file was automatically generated by cmake\n#pragma once\n\n") - file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/BuildInfo.h "#define ETH_COMMIT_HASH ${ETH_COMMIT_HASH}\n#define ETH_CLEAN_REPO ${ETH_CLEAN_REPO}\n#define ETH_BUILD_TYPE ${CMAKE_BUILD_TYPE}\n#define ETH_BUILD_PLATFORM ${ETH_BUILD_PLATFORM}\n") - include_directories(${CMAKE_CURRENT_BINARY_DIR}) -endif() +add_custom_target(BuildInfo.h ALL COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.sh ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BUILD_TYPE} ${ETH_BUILD_PLATFORM}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(secp256k1) add_subdirectory(libethereum) From 7f5a6cd9e93fa230acbf8cc7289cc344af1d27ba Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Mon, 3 Mar 2014 10:50:15 -0800 Subject: [PATCH 11/28] Add OpenGL and OpenSSL libraries to alethzero windows build. --- alethzero/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index e36ad52ff..33d2b7b16 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -87,10 +87,12 @@ elseif (${TARGET_PLATFORM} STREQUAL "w64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-keep-inline-dllexport -static-libgcc -static-libstdc++ -static") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-s -Wl,-subsystem,windows -mthreads -L/usr/x86_64-w64-mingw32/plugins/platforms") target_link_libraries(${EXECUTEABLE} gcc) - target_link_libraries(${EXECUTEABLE} mingw32 qtmain mswsock iphlpapi qwindows shlwapi Qt5PlatformSupport gdi32 comdlg32 oleaut32 imm32 winmm ole32 uuid ws2_32) + target_link_libraries(${EXECUTEABLE} mingw32 qtmain mswsock iphlpapi qwindows shlwapi Qt5PlatformSupport opengl32 gdi32 comdlg32 oleaut32 imm32 winmm ole32 uuid ws2_32) target_link_libraries(${EXECUTEABLE} boost_system-mt-s) target_link_libraries(${EXECUTEABLE} boost_filesystem-mt-s) target_link_libraries(${EXECUTEABLE} boost_thread_win32-mt-s) +# target_link_libraries(${EXECUTEABLE} ssl) + target_link_libraries(${EXECUTEABLE} crypt32) target_link_libraries(${EXECUTEABLE} Qt5PlatformSupport) set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) else () From c1c02c461d3912da502d05c338b8f8b6502dfc6f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 3 Mar 2014 18:54:31 +0000 Subject: [PATCH 12/28] Remove Database when protocol version changes. --- libethereum/Client.cpp | 30 +++++++++++++++++++++++++++--- libethereum/Client.h | 15 +++++++++++++++ libethereum/Defaults.h | 1 + libethereum/PeerNetwork.cpp | 10 ++++++++++ libethereum/PeerNetwork.h | 3 +++ libethereum/State.cpp | 2 -- 6 files changed, 56 insertions(+), 5 deletions(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index c30de604f..1752c2c0e 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -23,20 +23,44 @@ #include #include +#include #include "Common.h" #include "Defaults.h" using namespace std; using namespace eth; +VersionChecker::VersionChecker(string const& _dbPath, unsigned _protocolVersion): + m_path(_dbPath.size() ? _dbPath : Defaults::dbPath()), + m_protocolVersion(_protocolVersion) +{ + m_ok = RLP(contents(m_path + "/protocol")).toInt(RLP::LaisezFaire) == _protocolVersion; +} + +void VersionChecker::setOk() +{ + if (!m_ok) + { + try + { + boost::filesystem::create_directory(m_path); + } + catch (...) {} + writeFile(m_path + "/protocol", rlp(m_protocolVersion)); + } +} + Client::Client(std::string const& _clientVersion, Address _us, std::string const& _dbPath): m_clientVersion(_clientVersion), - m_bc(_dbPath), - m_stateDB(State::openDB(_dbPath)), + m_vc(_dbPath, PeerSession::protocolVersion()), + m_bc(_dbPath, !m_vc.ok()), + m_stateDB(State::openDB(_dbPath, !m_vc.ok())), m_preMine(_us, m_stateDB), m_postMine(_us, m_stateDB), m_workState(Active) { - Defaults::setDBPath(_dbPath); + if (_dbPath.size()) + Defaults::setDBPath(_dbPath); + m_vc.setOk(); // Synchronise the state according to the head of the block chain. // TODO: currently it contains keys for *all* blocks. Make it remove old ones. diff --git a/libethereum/Client.h b/libethereum/Client.h index bf1964fe0..5bcfe10fa 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -60,6 +60,20 @@ enum ClientWorkState Deleted }; +class VersionChecker +{ +public: + VersionChecker(std::string const& _dbPath, unsigned _protocolVersion); + + void setOk(); + bool ok() const { return m_ok; } + +private: + bool m_ok; + std::string m_path; + unsigned m_protocolVersion; +}; + class Client { public: @@ -141,6 +155,7 @@ private: void work(); std::string m_clientVersion; ///< Our end-application client's name/version. + VersionChecker m_vc; ///< Dummy object to check & update the protocol version. 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. diff --git a/libethereum/Defaults.h b/libethereum/Defaults.h index ce0c27a42..0d8e84515 100644 --- a/libethereum/Defaults.h +++ b/libethereum/Defaults.h @@ -36,6 +36,7 @@ public: static Defaults* get() { if (!s_this) s_this = new Defaults; return s_this; } static void setDBPath(std::string const& _dbPath) { get()->m_dbPath = _dbPath; } + static std::string const& dbPath() { return get()->m_dbPath; } private: std::string m_dbPath; diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index 713441de4..a4e3c6317 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -97,6 +97,16 @@ PeerSession::~PeerSession() m_socket.close(); } +int PeerSession::protocolVersion() +{ + return c_protocolVersion; +} + +int PeerSession::networkId() +{ + return 0; +} + bi::tcp::endpoint PeerSession::endpoint() const { if (m_socket.is_open()) diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index 80c6b054c..aa5f42da7 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -101,6 +101,9 @@ public: bool isOpen() const { return m_socket.is_open(); } + static int protocolVersion(); + static int networkId(); + bi::tcp::endpoint endpoint() const; ///< for other peers to connect to. private: diff --git a/libethereum/State.cpp b/libethereum/State.cpp index f14c8c18c..c06453c6f 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -55,8 +55,6 @@ Overlay State::openDB(std::string _path, bool _killExisting) if (_path.empty()) _path = Defaults::get()->m_dbPath; boost::filesystem::create_directory(_path); - if (_killExisting) - boost::filesystem::remove_all(_path + "/state"); ldb::Options o; o.create_if_missing = true; From c2d298c6f44f50a64e3c21ca3259237c341018f5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 3 Mar 2014 19:29:46 +0000 Subject: [PATCH 13/28] Hardcoded peer server for PoC-3. --- alethzero/MainWin.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 83ceab115..c195ee4ff 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -116,14 +116,20 @@ Main::Main(QWidget *parent) : #if ETH_DEBUG m_servers.append("192.168.0.10:30301"); #else - connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r) + int pocnumber = QString(ETH_QUOTED(ETH_VERSION)).section('.', 1, 1).toInt(); + if (pocnumber == 3) + m_servers.push_back("54.201.28.117:30303"); + else { - m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); - }); - QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc" + QString(ETH_QUOTED(ETH_VERSION)).section('.', 1, 1) + ".txt")); - r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); - m_webCtrl.get(r); - srand(time(0)); + connect(&m_webCtrl, &QNetworkAccessManager::finished, [&](QNetworkReply* _r) + { + m_servers = QString::fromUtf8(_r->readAll()).split("\n", QString::SkipEmptyParts); + }); + QNetworkRequest r(QUrl("http://www.ethereum.org/servers.poc" + QString::number(pocnumber) + ".txt")); + r.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1712.0 Safari/537.36"); + m_webCtrl.get(r); + srand(time(0)); + } #endif on_verbosity_sliderMoved(); From 6e348fb224b9af7e61969c775e5ae1353e259705 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 3 Mar 2014 19:51:40 +0000 Subject: [PATCH 14/28] New namereg. Bug fix for transactions. --- alethzero/MainWin.cpp | 5 ++++- libethereum/TransactionQueue.cpp | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index c195ee4ff..831b0dc79 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -246,7 +246,10 @@ void Main::readSettings() ui->clientName->setText(s.value("clientName", "").toString()); ui->idealPeers->setValue(s.value("idealPeers", ui->idealPeers->value()).toInt()); ui->port->setValue(s.value("port", ui->port->value()).toInt()); - ui->nameReg->setText(s.value("nameReg", "11f62328e131dbb05ce4c73a3de3c7ab1c84a163").toString()); + if (s.value("nameReg").toString() == "11f62328e131dbb05ce4c73a3de3c7ab1c84a163") + s.remove("nameReg"); + ui->nameReg->setText(s.value("nameReg", "8ff91e5b145a23ab1afef34f12587c18bd42aec0").toString()); + } void Main::on_nameReg_textChanged() diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 1b7c57ee2..cbf87038e 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -71,5 +71,7 @@ void TransactionQueue::noteGood(std::pair const& _t) auto r = m_future.equal_range(Transaction(_t.second).sender()); for (auto it = r.first; it != r.second; ++it) m_data.insert(_t); - m_future.erase(r.first); + cdebug << m_future; + m_future.erase(r.first, r.second); + cdebug << m_future; } From 2c98c1191f3a9a6248a56856df57e4a219b120d1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 3 Mar 2014 20:30:41 +0000 Subject: [PATCH 15/28] Remove debug code. --- libethereum/TransactionQueue.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index cbf87038e..55d225273 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -71,7 +71,5 @@ void TransactionQueue::noteGood(std::pair const& _t) auto r = m_future.equal_range(Transaction(_t.second).sender()); for (auto it = r.first; it != r.second; ++it) m_data.insert(_t); - cdebug << m_future; m_future.erase(r.first, r.second); - cdebug << m_future; } From 7ef7bd2d148a8c7d47d115db57b93fe5d375b362 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 3 Mar 2014 20:51:56 +0000 Subject: [PATCH 16/28] Merge debian changes form master. --- debian/control | 8 ++++---- debian/rules | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/debian/control b/debian/control index a54315c18..b6c99a864 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: cpp-ethereum Section: science Priority: extra Maintainer: Ethereum (Ethereum Project) -Build-Depends: debhelper (>= 8.0.0), cmake, libgmp-dev, libcryptoppeth-dev, libboost-filesystem1.53-dev, libboost-mpi1.53-dev, libboost1.53-dev, libleveldb-dev, libminiupnpc-dev, qtbase5-dev, qt5-default +Build-Depends: debhelper (>= 8.0.0), cmake, libgmp-dev, libboost-thread-dev, libcryptoppeth-dev, libboost-filesystem-dev, libboost-mpi-dev, libboost-dev, libleveldb-dev, libminiupnpc-dev, qtbase5-dev, qt5-default Standards-Version: 3.9.4 Homepage: http://ethereum.org #Vcs-Git: git://git.debian.org/collab-maint/cpp-ethereum.git @@ -11,19 +11,19 @@ Homepage: http://ethereum.org Package: libethereum Section: libdevel Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libgmp, libcryptoppeth, libboost-filesystem1.53.0, libboost-mpi1.53.0, libleveldb, libminiupnpc, secp256k1eth +Depends: ${shlibs:Depends}, ${misc:Depends} Description: The future of computational social contracts. A long description of libethereum. Package: libethereum-dev Section: libdevel Architecture: any -Depends: libethereum (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, libgmp, libcryptoppeth, libboost-filesystem1.53.0, libboost-mpi1.53.0, libleveldb, libminiupnpc +Depends: libethereum (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} Description: The future of computational social contracts. Dev Files. A long description of libethereum. Dev Files. Package: alethzero Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libethereum (= ${binary:Version}), qtbase5 +Depends: ${shlibs:Depends}, ${misc:Depends}, libethereum (= ${binary:Version}) Description: The Qt-based Ethereum GUI. A long description of alethzero. diff --git a/debian/rules b/debian/rules index 5ffe813ee..83f79d114 100755 --- a/debian/rules +++ b/debian/rules @@ -31,6 +31,6 @@ override_dh_auto_install: # Move the libs over to the non-dev package. mkdir -p $(CURDIR)/debian/libethereum/usr mv $(CURDIR)/debian/libethereum-dev/usr/lib $(CURDIR)/debian/libethereum/usr/lib - mkdir -p $(CURDIR)/debian/alephzero/usr/bin - mv $(CURDIR)/debian/libethereum-dev/usr/bin/alethzero $(CURDIR)/debian/alethzero/usr/lib + mkdir -p $(CURDIR)/debian/alethzero/usr/bin + mv $(CURDIR)/debian/libethereum-dev/usr/bin/alethzero $(CURDIR)/debian/alethzero/usr/bin From f237105430a10b81527770aa10a1bacd33ece26e Mon Sep 17 00:00:00 2001 From: Vincent Gariepy Date: Mon, 3 Mar 2014 16:07:35 -0500 Subject: [PATCH 17/28] Add 'block' command to see current block number --- eth/main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/eth/main.cpp b/eth/main.cpp index b8e26aefb..d3e50b7f7 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -237,6 +237,13 @@ int main(int argc, char** argv) cout << "Current secret: " + asHex(us.secret().asArray()) << endl; cout << "===" << endl; } + else if (cmd == "block") + { + eth::uint n = c.blockChain().details().number; + cout << endl; + cout << "Current block # " << n << endl; + cout << "===" << endl; + } else if (cmd == "balance") { u256 balance = c.state().balance(us.address()); From 92200adeb303d7badca16e3d3ff95e6af77124a2 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Mon, 3 Mar 2014 15:26:47 -0800 Subject: [PATCH 18/28] Get rid of operator precedence warning. --- libethereum/Common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/Common.h b/libethereum/Common.h index df1d8569e..ad7a1ef78 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -155,7 +155,7 @@ public: { size_t h = 0; for (auto i: value.m_data) - h = (h << 5 - h) + i; + h = (h << (5 - h)) + i; return h; } }; From 8c1a0959417dc5ca169ee284837baae6589ccd67 Mon Sep 17 00:00:00 2001 From: Marko Simovic Date: Tue, 4 Mar 2014 02:58:58 +0000 Subject: [PATCH 19/28] Removed obsolete debian packaging files --- debian/README.Debian | 6 ---- debian/README.source | 9 ------ debian/changelog | 23 -------------- debian/compat | 1 - debian/control | 29 ----------------- debian/copyright | 24 -------------- debian/docs | 3 -- debian/rules | 36 --------------------- package.sh | 8 ----- release.sh | 74 -------------------------------------------- 10 files changed, 213 deletions(-) delete mode 100644 debian/README.Debian delete mode 100644 debian/README.source delete mode 100644 debian/changelog delete mode 100644 debian/compat delete mode 100644 debian/control delete mode 100644 debian/copyright delete mode 100644 debian/docs delete mode 100755 debian/rules delete mode 100755 package.sh delete mode 100755 release.sh diff --git a/debian/README.Debian b/debian/README.Debian deleted file mode 100644 index d9ffd1e35..000000000 --- a/debian/README.Debian +++ /dev/null @@ -1,6 +0,0 @@ -cpp-ethereum for Debian ------------------------ - - - - -- Gav > Mon, 03 Feb 2014 14:50:20 +0000 diff --git a/debian/README.source b/debian/README.source deleted file mode 100644 index 5d002af70..000000000 --- a/debian/README.source +++ /dev/null @@ -1,9 +0,0 @@ -cpp-ethereum for Debian ------------------------ - - - - - - diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index d29dd1850..000000000 --- a/debian/changelog +++ /dev/null @@ -1,23 +0,0 @@ -cpp-ethereum (0.1.0-1) saucy; urgency=low - - * Various improvements moving towards PoC-2. - - -- Ethereum (Ethereum Project) Fri, 07 Feb 2014 00:48:26 +0000 - -cpp-ethereum (0.1-3) saucy; urgency=low - - * Packaging stuff. - - -- Ethereum (Ethereum Project) Thu, 06 Feb 2014 14:13:00 +0000 - -cpp-ethereum (0.1-2) saucy; urgency=low - - * Fix Qt dep. - - -- Gav Wood Mon, 06 Feb 2014 11:35:20 +0000 - -cpp-ethereum (0.1-1) saucy; urgency=low - - * Initial release. - - -- Gav Wood Mon, 03 Feb 2014 14:50:20 +0000 diff --git a/debian/compat b/debian/compat deleted file mode 100644 index 45a4fb75d..000000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/debian/control b/debian/control deleted file mode 100644 index b6c99a864..000000000 --- a/debian/control +++ /dev/null @@ -1,29 +0,0 @@ -Source: cpp-ethereum -Section: science -Priority: extra -Maintainer: Ethereum (Ethereum Project) -Build-Depends: debhelper (>= 8.0.0), cmake, libgmp-dev, libboost-thread-dev, libcryptoppeth-dev, libboost-filesystem-dev, libboost-mpi-dev, libboost-dev, libleveldb-dev, libminiupnpc-dev, qtbase5-dev, qt5-default -Standards-Version: 3.9.4 -Homepage: http://ethereum.org -#Vcs-Git: git://git.debian.org/collab-maint/cpp-ethereum.git -#Vcs-Browser: http://git.debian.org/?p=collab-maint/cpp-ethereum.git;a=summary - -Package: libethereum -Section: libdevel -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: The future of computational social contracts. - A long description of libethereum. - -Package: libethereum-dev -Section: libdevel -Architecture: any -Depends: libethereum (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} -Description: The future of computational social contracts. Dev Files. - A long description of libethereum. Dev Files. - -Package: alethzero -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libethereum (= ${binary:Version}) -Description: The Qt-based Ethereum GUI. - A long description of alethzero. diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index fbbdb321d..000000000 --- a/debian/copyright +++ /dev/null @@ -1,24 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: cpp-ethereum -Source: github.com/ethereum/cpp-ethereum - -Files: * -Copyright: 2014 Gav Wood -License: MIT - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. diff --git a/debian/docs b/debian/docs deleted file mode 100644 index f31b08fec..000000000 --- a/debian/docs +++ /dev/null @@ -1,3 +0,0 @@ -CodingStandards.txt -README.md -TODO diff --git a/debian/rules b/debian/rules deleted file mode 100755 index 83f79d114..000000000 --- a/debian/rules +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- -# Sample debian/rules that uses debhelper. -# -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. -# -# Modified to make a template file for a multi-binary package with separated -# build-arch and build-indep targets by Bill Allombert 2001 - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -# This has to be exported to make some magic below work. -export DH_OPTIONS - - -%: - dh $@ - -override_dh_auto_configure: - cmake -DCMAKE_INSTALL_PREFIX=$(CURDIR)/debian/libethereum-dev/usr . - -override_dh_auto_build: - $(MAKE) -j8 - -override_dh_auto_install: - $(MAKE) install - # Move the libs over to the non-dev package. - mkdir -p $(CURDIR)/debian/libethereum/usr - mv $(CURDIR)/debian/libethereum-dev/usr/lib $(CURDIR)/debian/libethereum/usr/lib - mkdir -p $(CURDIR)/debian/alethzero/usr/bin - mv $(CURDIR)/debian/libethereum-dev/usr/bin/alethzero $(CURDIR)/debian/alethzero/usr/bin - diff --git a/package.sh b/package.sh deleted file mode 100755 index fb778a4d4..000000000 --- a/package.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -set -e -rm -f ../cpp-ethereum_*_source.changes -debuild -S -sa -cd .. -dput -f ppa:ethereum/ethereum cpp-ethereum_*_source.changes - diff --git a/release.sh b/release.sh deleted file mode 100755 index 712e2540a..000000000 --- a/release.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -dist="saucy" -version=$(grep "define ETH_VERSION" libethereum/Common.h | cut -d ' ' -f 3) -branch="$(git branch | grep \* | cut -c 3-)" - -if [[ ! "$1" == "" ]]; then - version=$1 -fi - -if [[ ! "$3" == "" ]]; then - if [[ ! "$4" == "" ]]; then - dist=$4 - fi - if [[ "$2" == "-i" ]]; then - # increment current debian release only - # new version ./release VERSION -i MESSAGE DIST - debchange -i -p "$3" -D "$dist" - git commit -a -m "$3" - else - # new version ./release VERSION DEB-VERSION MESSAGE DIST - debchange -v $version-$2 -p "$3" -D "$dist" - git commit -a -m "$3" - fi -fi - -opwd=`pwd` -cd /tmp - -echo Checking out... -git clone $opwd -cd cpp-ethereum -git checkout "$branch" - -archdir="cpp-ethereum-$version" -archfile="$archdir.tar.bz2" - -echo Making BuildInfo... -mkdir build -cd build -cmake .. -cd .. -cp build/BuildInfo.h . -rm -rf build - -echo Cleaning backup files... -find . | grep \~ | xargs rm -f - -echo Cleaning others... -rm release.sh - -echo Cleaning versioning... -rm -rf .git .gitignore - -echo Renaming directory... -cd .. -rm -rf $archdir -mv cpp-ethereum $archdir - -echo Creating archive... -tar c $archdir | bzip2 -- > $archfile -shasum $archfile - -[[ ! "$version" == "" ]] && ln -sf $archfile "cpp-ethereum_$version.orig.tar.bz2" - -echo Packaging... -cd "$archdir" -./package.sh - -echo Cleaning up... -rm -rf /tmp/$archdir -mv /tmp/$archfile ~ - -echo Done. From fb49ca64f6a89c17378dcb978351c9703ec4a708 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 4 Mar 2014 13:40:42 +0000 Subject: [PATCH 20/28] Repotted network stuff. --- alethzero/MainWin.cpp | 1 + libethereum/Client.cpp | 13 +- libethereum/Client.h | 4 +- libethereum/PeerNetwork.cpp | 1078 +---------------------------------- libethereum/PeerNetwork.h | 168 +----- libethereum/PeerServer.cpp | 542 ++++++++++++++++++ libethereum/PeerSession.cpp | 585 +++++++++++++++++++ test/peer.cpp | 2 +- 8 files changed, 1155 insertions(+), 1238 deletions(-) create mode 100644 libethereum/PeerServer.cpp create mode 100644 libethereum/PeerSession.cpp diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 831b0dc79..5fd9b5509 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "BuildInfo.h" #include "MainWin.h" #include "ui_Main.h" diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 1752c2c0e..472c0555e 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -26,6 +26,7 @@ #include #include "Common.h" #include "Defaults.h" +#include "PeerServer.h" using namespace std; using namespace eth; @@ -51,7 +52,7 @@ void VersionChecker::setOk() Client::Client(std::string const& _clientVersion, Address _us, std::string const& _dbPath): m_clientVersion(_clientVersion), - m_vc(_dbPath, PeerSession::protocolVersion()), + m_vc(_dbPath, PeerServer::protocolVersion()), m_bc(_dbPath, !m_vc.ok()), m_stateDB(State::openDB(_dbPath, !m_vc.ok())), m_preMine(_us, m_stateDB), @@ -97,6 +98,16 @@ void Client::startNetwork(unsigned short _listenPort, std::string const& _seedHo connect(_seedHost, _port); } +std::vector Client::peers() +{ + return m_net ? m_net->peers() : std::vector(); +} + +size_t Client::peerCount() const +{ + return m_net ? m_net->peerCount() : 0; +} + void Client::connect(std::string const& _seedHost, unsigned short _port) { if (!m_net.get()) diff --git a/libethereum/Client.h b/libethereum/Client.h index 5bcfe10fa..0844fc207 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -125,9 +125,9 @@ public: // Network stuff: /// Get information on the current peer set. - std::vector peers() { return m_net ? m_net->peers() : std::vector(); } + std::vector peers(); /// Same as peers().size(), but more efficient. - size_t peerCount() const { return m_net ? m_net->peerCount() : 0; } + size_t peerCount() const; /// Start the network subsystem. void startNetwork(unsigned short _listenPort = 30303, std::string const& _remoteHost = std::string(), unsigned short _remotePort = 30303, NodeMode _mode = NodeMode::Full, unsigned _peers = 5, std::string const& _publicIP = std::string(), bool _upnp = true); diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index a4e3c6317..2f93ee74c 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -15,51 +15,14 @@ along with cpp-ethereum. If not, see . */ /** @file PeerNetwork.cpp - * @authors: - * Gav Wood - * Eric Lombrozo + * @author Gav Wood * @date 2014 */ -#include -#ifdef _WIN32 -// winsock is already included -// #include -#else -#include -#endif - -#include -#include -#include "Exceptions.h" -#include "Common.h" -#include "BlockChain.h" -#include "BlockInfo.h" -#include "TransactionQueue.h" -#include "UPnP.h" #include "PeerNetwork.h" using namespace std; using namespace eth; -#define clogS(X) eth::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " - -static const int c_protocolVersion = 8; - -static const eth::uint c_maxHashes = 32; ///< Maximum number of hashes GetChain will ever send. -static const eth::uint c_maxBlocks = 32; ///< Maximum number of blocks Blocks will ever send. BUG: if this gets too big (e.g. 2048) stuff starts going wrong. -static const eth::uint c_maxBlocksAsk = 256; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). - -// Addresses we will skip during network interface discovery -// Use a vector as the list is small -// Why this and not names? -// Under MacOSX loopback (127.0.0.1) can be named lo0 and br0 are bridges (0.0.0.0) -static const vector c_rejectAddresses = { - {bi::address_v4::from_string("127.0.0.1")}, - {bi::address_v6::from_string("::1")}, - {bi::address_v4::from_string("0.0.0.0")}, - {bi::address_v6::from_string("::")} -}; - // Helper function to determine if an address falls within one of the reserved ranges // For V4: // Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*" @@ -80,44 +43,7 @@ bool eth::isPrivateAddress(bi::address _addressToCheck) return false; } -PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, unsigned short _peerPort): - m_server(_s), - m_socket(std::move(_socket)), - m_reqNetworkId(_rNId), - m_listenPort(_peerPort), - m_rating(0) -{ - m_disconnect = std::chrono::steady_clock::time_point::max(); - m_connect = std::chrono::steady_clock::now(); - m_info = PeerInfo({"?", _peerAddress.to_string(), m_listenPort, std::chrono::steady_clock::duration(0)}); -} - -PeerSession::~PeerSession() -{ - m_socket.close(); -} - -int PeerSession::protocolVersion() -{ - return c_protocolVersion; -} - -int PeerSession::networkId() -{ - return 0; -} - -bi::tcp::endpoint PeerSession::endpoint() const -{ - if (m_socket.is_open()) - try { - return bi::tcp::endpoint(m_socket.remote_endpoint().address(), m_listenPort); - } catch (...){} - - return bi::tcp::endpoint(); -} - -static std::string reasonOf(int _r) +std::string eth::reasonOf(DisconnectReason _r) { switch (_r) { @@ -134,1003 +60,3 @@ static std::string reasonOf(int _r) } } -// TODO: BUG! 256 -> work out why things start to break with big packet sizes -> g.t. ~370 blocks. - -bool PeerSession::interpret(RLP const& _r) -{ - clogS(NetRight) << _r; - switch (_r[0].toInt()) - { - case HelloPacket: - { - m_protocolVersion = _r[1].toInt(); - m_networkId = _r[2].toInt(); - auto clientVersion = _r[3].toString(); - m_caps = _r[4].toInt(); - m_listenPort = _r[5].toInt(); - m_id = _r[6].toHash(); - - clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << m_id.abridged() << showbase << hex << m_caps << dec << m_listenPort; - - if (m_server->m_peers.count(m_id)) - if (auto l = m_server->m_peers[m_id].lock()) - if (l.get() != this && l->isOpen()) - { - // Already connected. - cwarn << "Already have peer id" << m_id.abridged() << "at" << l->endpoint() << "rather than" << endpoint(); - disconnect(DuplicatePeer); - return false; - } - - if (m_protocolVersion != c_protocolVersion || m_networkId != m_reqNetworkId || !m_id) - { - disconnect(IncompatibleProtocol); - return false; - } - try - { m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), m_listenPort, std::chrono::steady_clock::duration()}); } - catch (...) - { - disconnect(BadProtocol); - return false; - } - - m_server->m_peers[m_id] = shared_from_this(); - - // Grab their block chain off them. - { - clogS(NetAllDetail) << "Want chain. Latest:" << m_server->m_latestBlockSent << ", number:" << m_server->m_chain->details(m_server->m_latestBlockSent).number; - uint count = std::min(c_maxHashes, m_server->m_chain->details(m_server->m_latestBlockSent).number + 1); - RLPStream s; - prep(s).appendList(2 + count); - s << GetChainPacket; - auto h = m_server->m_latestBlockSent; - for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) - { - clogS(NetAllDetail) << " " << i << ":" << h; - s << h; - } - - s << c_maxBlocksAsk; - sealAndSend(s); - s.clear(); - prep(s).appendList(1); - s << GetTransactionsPacket; - sealAndSend(s); - } - break; - } - case DisconnectPacket: - { - string reason = "Unspecified"; - if (_r[1].isInt()) - reason = reasonOf(_r[1].toInt()); - - clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")"; - if (m_socket.is_open()) - clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); - else - clogS(NetNote) << "Remote closed."; - m_socket.close(); - return false; - } - case PingPacket: - { -// clogS(NetMessageSummary) << "Ping"; - RLPStream s; - sealAndSend(prep(s).appendList(1) << PongPacket); - break; - } - case PongPacket: - m_info.lastPing = std::chrono::steady_clock::now() - m_ping; -// clogS(NetMessageSummary) << "Latency: " << chrono::duration_cast(m_lastPing).count() << " ms"; - break; - case GetPeersPacket: - { - clogS(NetMessageSummary) << "GetPeers"; - auto peers = m_server->potentialPeers(); - RLPStream s; - prep(s).appendList(peers.size() + 1); - s << PeersPacket; - for (auto i: peers) - { - clogS(NetMessageDetail) << "Sending peer " << asHex(i.first.ref().cropped(0, 4)) << i.second; - s.appendList(3) << i.second.address().to_v4().to_bytes() << i.second.port() << i.first; - } - sealAndSend(s); - break; - } - case PeersPacket: - clogS(NetMessageSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; - for (unsigned i = 1; i < _r.itemCount(); ++i) - { - bi::address_v4 peerAddress(_r[i][0].toArray()); - auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt()); - Public id = _r[i][2].toHash(); - if (isPrivateAddress(peerAddress)) - goto CONTINUE; - - clogS(NetAllDetail) << "Checking: " << ep << "(" << asHex(id.ref().cropped(0, 4)) << ")"; - - // check that it's not us or one we already know: - if (id && (m_server->m_key.pub() == id || m_server->m_peers.count(id) || m_server->m_incomingPeers.count(id))) - goto CONTINUE; - - // check that we're not already connected to addr: - if (!ep.port()) - goto CONTINUE; - for (auto i: m_server->m_addresses) - if (ep.address() == i && ep.port() == m_server->listenPort()) - goto CONTINUE; - for (auto i: m_server->m_peers) - if (shared_ptr p = i.second.lock()) - { - clogS(NetAllDetail) << " ...against " << p->endpoint(); - if (p->m_socket.is_open() && p->endpoint() == ep) - goto CONTINUE; - } - for (auto i: m_server->m_incomingPeers) - if (i.second.first == ep) - goto CONTINUE; - m_server->m_incomingPeers[id] = make_pair(ep, 0); - m_server->m_freePeers.push_back(id); - clogS(NetMessageDetail) << "New peer: " << ep << "(" << id << ")"; - CONTINUE:; - } - break; - case TransactionsPacket: - if (m_server->m_mode == NodeMode::PeerServer) - break; - clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << " entries)"; - m_rating += _r.itemCount() - 1; - for (unsigned i = 1; i < _r.itemCount(); ++i) - { - m_server->m_incomingTransactions.push_back(_r[i].data().toBytes()); - m_knownTransactions.insert(sha3(_r[i].data())); - } - break; - case BlocksPacket: - { - if (m_server->m_mode == NodeMode::PeerServer) - break; - clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << " entries)"; - unsigned used = 0; - for (unsigned i = 1; i < _r.itemCount(); ++i) - { - auto h = sha3(_r[i].data()); - if (!m_server->m_chain->details(h)) - { - m_server->m_incomingBlocks.push_back(_r[i].data().toBytes()); - m_knownBlocks.insert(h); - used++; - } - } - m_rating += used; - if (g_logVerbosity >= 3) - for (unsigned i = 1; i < _r.itemCount(); ++i) - { - auto h = sha3(_r[i].data()); - BlockInfo bi(_r[i].data()); - if (!m_server->m_chain->details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) - clogS(NetMessageDetail) << "Unknown parent " << bi.parentHash << " of block " << h; - else - clogS(NetMessageDetail) << "Known parent " << bi.parentHash << " of block " << h; - } - if (used) // we received some - check if there's any more - { - RLPStream s; - prep(s).appendList(3); - s << GetChainPacket; - s << sha3(_r[1].data()); - s << c_maxBlocksAsk; - sealAndSend(s); - } - break; - } - case GetChainPacket: - { - if (m_server->m_mode == NodeMode::PeerServer) - break; - clogS(NetMessageSummary) << "GetChain (" << (_r.itemCount() - 2) << " hashes, " << (_r[_r.itemCount() - 1].toInt()) << ")"; - // ******************************************************************** - // NEEDS FULL REWRITE! - h256s parents; - parents.reserve(_r.itemCount() - 2); - for (unsigned i = 1; i < _r.itemCount() - 1; ++i) - parents.push_back(_r[i].toHash()); - if (_r.itemCount() == 2) - break; - // return 2048 block max. - uint baseCount = (uint)min(_r[_r.itemCount() - 1].toInt(), c_maxBlocks); - clogS(NetMessageSummary) << "GetChain (" << baseCount << " max, from " << parents.front() << " to " << parents.back() << ")"; - for (auto parent: parents) - { - auto h = m_server->m_chain->currentHash(); - h256 latest = m_server->m_chain->currentHash(); - uint latestNumber = 0; - uint parentNumber = 0; - RLPStream s; - - if (m_server->m_chain->details(parent)) - { - latestNumber = m_server->m_chain->details(latest).number; - parentNumber = m_server->m_chain->details(parent).number; - uint count = min(latestNumber - parentNumber, baseCount); - clogS(NetAllDetail) << "Requires " << dec << (latestNumber - parentNumber) << " blocks from " << latestNumber << " to " << parentNumber; - clogS(NetAllDetail) << latest << " - " << parent; - - prep(s); - s.appendList(1 + count) << BlocksPacket; - uint endNumber = m_server->m_chain->details(parent).number; - uint startNumber = endNumber + count; - clogS(NetAllDetail) << "Sending " << dec << count << " blocks from " << startNumber << " to " << endNumber; - - uint n = latestNumber; - for (; n > startNumber; n--, h = m_server->m_chain->details(h).parent) {} - for (uint i = 0; i < count; ++i, --n, h = m_server->m_chain->details(h).parent) - { - if (h == parent || n == endNumber) - { - cwarn << "BUG! Couldn't create the reply for GetChain!"; - return true; - } - clogS(NetAllDetail) << " " << dec << i << " " << h; - s.appendRaw(m_server->m_chain->block(h)); - } - clogS(NetAllDetail) << "Parent: " << h; - } - else if (parent != parents.back()) - continue; - - if (h != parent) - { - // not in the blockchain; - if (parent == parents.back()) - { - // out of parents... - clogS(NetAllDetail) << "GetChain failed; not in chain"; - // No good - must have been on a different branch. - s.clear(); - prep(s).appendList(2) << NotInChainPacket << parents.back(); - } - else - // still some parents left - try them. - continue; - } - // send the packet (either Blocks or NotInChain) & exit. - sealAndSend(s); - break; - // ******************************************************************** - } - break; - } - case NotInChainPacket: - { - if (m_server->m_mode == NodeMode::PeerServer) - break; - h256 noGood = _r[1].toHash(); - clogS(NetMessageSummary) << "NotInChain (" << noGood << ")"; - if (noGood == m_server->m_chain->genesisHash()) - { - clogS(NetWarn) << "Discordance over genesis block! Disconnect."; - disconnect(WrongGenesis); - } - else - { - uint count = std::min(c_maxHashes, m_server->m_chain->details(noGood).number); - RLPStream s; - prep(s).appendList(2 + count); - s << GetChainPacket; - auto h = m_server->m_chain->details(noGood).parent; - for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) - s << h; - s << c_maxBlocksAsk; - sealAndSend(s); - } - break; - } - case GetTransactionsPacket: - { - if (m_server->m_mode == NodeMode::PeerServer) - break; - m_requireTransactions = true; - break; - } - default: - break; - } - return true; -} - -void PeerSession::ping() -{ - RLPStream s; - sealAndSend(prep(s).appendList(1) << PingPacket); - m_ping = std::chrono::steady_clock::now(); -} - -RLPStream& PeerSession::prep(RLPStream& _s) -{ - return _s.appendRaw(bytes(8, 0)); -} - -void PeerServer::seal(bytes& _b) -{ - _b[0] = 0x22; - _b[1] = 0x40; - _b[2] = 0x08; - _b[3] = 0x91; - uint32_t len = (uint32_t)_b.size() - 8; - _b[4] = (len >> 24) & 0xff; - _b[5] = (len >> 16) & 0xff; - _b[6] = (len >> 8) & 0xff; - _b[7] = len & 0xff; -} - -void PeerSession::sealAndSend(RLPStream& _s) -{ - bytes b; - _s.swapOut(b); - m_server->seal(b); - sendDestroy(b); -} - -bool PeerSession::checkPacket(bytesConstRef _msg) -{ - if (_msg.size() < 8) - return false; - if (!(_msg[0] == 0x22 && _msg[1] == 0x40 && _msg[2] == 0x08 && _msg[3] == 0x91)) - return false; - uint32_t len = ((_msg[4] * 256 + _msg[5]) * 256 + _msg[6]) * 256 + _msg[7]; - if (_msg.size() != len + 8) - return false; - RLP r(_msg.cropped(8)); - if (r.actualSize() != len) - return false; - return true; -} - -void PeerSession::sendDestroy(bytes& _msg) -{ - clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); - - if (!checkPacket(bytesConstRef(&_msg))) - { - cwarn << "INVALID PACKET CONSTRUCTED!"; - } - - auto self(shared_from_this()); - bytes* buffer = new bytes(std::move(_msg)); - ba::async_write(m_socket, ba::buffer(*buffer), [self,buffer](boost::system::error_code ec, std::size_t length) - { - delete buffer; - if (ec) - { - cwarn << "Error sending: " << ec.message(); - self->dropped(); - } -// cbug << length << " bytes written (EC: " << ec << ")"; - }); -} - -void PeerSession::send(bytesConstRef _msg) -{ - clogS(NetLeft) << RLP(_msg.cropped(8)); - - if (!checkPacket(_msg)) - { - cwarn << "INVALID PACKET CONSTRUCTED!"; - } - - auto self(shared_from_this()); - bytes* buffer = new bytes(_msg.toBytes()); - ba::async_write(m_socket, ba::buffer(*buffer), [self,buffer](boost::system::error_code ec, std::size_t length) - { - delete buffer; - if (ec) - { - cwarn << "Error sending: " << ec.message(); - self->dropped(); - } -// cbug << length << " bytes written (EC: " << ec << ")"; - }); -} - -void PeerSession::dropped() -{ - if (m_socket.is_open()) - try { - clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); - m_socket.close(); - }catch (...){} - for (auto i = m_server->m_peers.begin(); i != m_server->m_peers.end(); ++i) - if (i->second.lock().get() == this) - { - m_server->m_peers.erase(i); - break; - } -} - -void PeerSession::disconnect(int _reason) -{ - clogS(NetNote) << "Disconnecting (reason:" << reasonOf(_reason) << ")"; - if (m_socket.is_open()) - { - if (m_disconnect == chrono::steady_clock::time_point::max()) - { - RLPStream s; - prep(s); - s.appendList(2) << DisconnectPacket << _reason; - sealAndSend(s); - m_disconnect = chrono::steady_clock::now(); - } - else - dropped(); - } -} - -void PeerSession::start() -{ - RLPStream s; - prep(s); - s.appendList(7) << HelloPacket << (uint)c_protocolVersion << (uint)m_server->m_requiredNetworkId << m_server->m_clientVersion << (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0) << m_server->m_public.port() << m_server->m_key.pub(); - sealAndSend(s); - - ping(); - - doRead(); -} - -void PeerSession::doRead() -{ - auto self(shared_from_this()); - m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) - { - if (ec) - { - cwarn << "Error reading: " << ec.message(); - dropped(); - } - else - { - try - { - m_incoming.resize(m_incoming.size() + length); - memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); - while (m_incoming.size() > 8) - { - if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) - { - disconnect(BadProtocol); - return; - clogS(NetWarn) << "Out of alignment. Skipping: " << hex << showbase << (int)m_incoming[0] << dec; - memmove(m_incoming.data(), m_incoming.data() + 1, m_incoming.size() - 1); - m_incoming.resize(m_incoming.size() - 1); - } - else - { - uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data() + 4, 4)); - uint32_t tlen = len + 8; - if (m_incoming.size() < tlen) - break; - - // enough has come in. -// cerr << "Received " << len << ": " << asHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; - auto data = bytesConstRef(m_incoming.data(), tlen); - if (!checkPacket(data)) - { - cerr << "Received " << len << ": " << asHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; - cwarn << "INVALID MESSAGE RECEIVED"; - disconnect(BadProtocol); - return; - } - else - { - RLP r(data.cropped(8)); - if (!interpret(r)) - { - // error - dropped(); - return; - } - } - memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); - m_incoming.resize(m_incoming.size() - tlen); - } - } - doRead(); - } - catch (Exception const& _e) - { - clogS(NetWarn) << "ERROR: " << _e.description(); - dropped(); - } - catch (std::exception const& _e) - { - clogS(NetWarn) << "ERROR: " << _e.what(); - dropped(); - } - } - }); -} - -PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, unsigned short _port, NodeMode _m, string const& _publicAddress, bool _upnp): - m_clientVersion(_clientVersion), - m_mode(_m), - m_listenPort(_port), - m_chain(&_ch), - m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)), - m_socket(m_ioService), - m_key(KeyPair::create()), - m_requiredNetworkId(_networkId) -{ - populateAddresses(); - determinePublic(_publicAddress, _upnp); - ensureAccepting(); - clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); -} - -PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m): - m_clientVersion(_clientVersion), - m_mode(_m), - m_listenPort(0), - m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), - m_socket(m_ioService), - m_key(KeyPair::create()), - m_requiredNetworkId(_networkId) -{ - // populate addresses. - populateAddresses(); - clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); -} - -PeerServer::~PeerServer() -{ - for (auto const& i: m_peers) - if (auto p = i.second.lock()) - p->disconnect(ClientQuit); - delete m_upnp; -} - -void PeerServer::determinePublic(string const& _publicAddress, bool _upnp) -{ - if (_upnp) - try - { - m_upnp = new UPnP; - } - catch (NoUPnPDevice) {} // let m_upnp continue as null - we handle it properly. - - bi::tcp::resolver r(m_ioService); - if (m_upnp && m_upnp->isValid() && m_peerAddresses.size()) - { - clog(NetNote) << "External addr: " << m_upnp->externalIP(); - int p = m_upnp->addRedirect(m_peerAddresses[0].to_string().c_str(), m_listenPort); - if (p) - clog(NetNote) << "Punched through NAT and mapped local port" << m_listenPort << "onto external port" << p << "."; - else - { - // couldn't map - clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place). Assuming " << m_listenPort << " is local & external port."; - p = m_listenPort; - } - - auto eip = m_upnp->externalIP(); - if (eip == string("0.0.0.0") && _publicAddress.empty()) - m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p); - else - { - m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p); - m_addresses.push_back(m_public.address().to_v4()); - } - } - else - { - // No UPnP - fallback on given public address or, if empty, the assumed peer address. - m_public = bi::tcp::endpoint(_publicAddress.size() ? bi::address::from_string(_publicAddress) - : m_peerAddresses.size() ? m_peerAddresses[0] - : bi::address(), m_listenPort); - m_addresses.push_back(m_public.address().to_v4()); - } -} - -void PeerServer::populateAddresses() -{ -#ifdef _WIN32 - WSAData wsaData; - if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) - throw NoNetworking(); - - char ac[80]; - if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) - { - clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; - WSACleanup(); - throw NoNetworking(); - } - - struct hostent* phe = gethostbyname(ac); - if (phe == 0) - { - clog(NetWarn) << "Bad host lookup."; - WSACleanup(); - throw NoNetworking(); - } - - for (int i = 0; phe->h_addr_list[i] != 0; ++i) - { - struct in_addr addr; - memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); - char *addrStr = inet_ntoa(addr); - bi::address ad(bi::address::from_string(addrStr)); - m_addresses.push_back(ad.to_v4()); - bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); - if (!isLocal) - m_peerAddresses.push_back(ad.to_v4()); - clog(NetNote) << "Address: " << ac << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); - } - - WSACleanup(); -#else - ifaddrs* ifaddr; - if (getifaddrs(&ifaddr) == -1) - throw NoNetworking(); - - bi::tcp::resolver r(m_ioService); - - for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) - { - if (!ifa->ifa_addr) - continue; - if (ifa->ifa_addr->sa_family == AF_INET) - { - char host[NI_MAXHOST]; - if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) - continue; - // TODO: Make exception safe when no internet. - auto it = r.resolve({host, "30303"}); - bi::tcp::endpoint ep = it->endpoint(); - bi::address ad = ep.address(); - m_addresses.push_back(ad.to_v4()); - bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); - if (!isLocal) - m_peerAddresses.push_back(ad.to_v4()); - clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); - } - } - - freeifaddrs(ifaddr); -#endif -} - -std::map PeerServer::potentialPeers() -{ - std::map ret; - if (!m_public.address().is_unspecified()) - ret.insert(make_pair(m_key.pub(), m_public)); - for (auto i: m_peers) - if (auto j = i.second.lock()) - { - auto ep = j->endpoint(); - // Skip peers with a listen port of zero or are on a private network - bool peerOnNet = (j->m_listenPort != 0 && !isPrivateAddress(ep.address())); - if (peerOnNet && ep.port() && j->m_id) - ret.insert(make_pair(i.first, ep)); - } - return ret; -} - -void PeerServer::ensureAccepting() -{ - if (m_accepting == false) - { - clog(NetNote) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")"; - m_accepting = true; - m_acceptor.async_accept(m_socket, [=](boost::system::error_code ec) - { - if (!ec) - try - { - try { - clog(NetNote) << "Accepted connection from " << m_socket.remote_endpoint(); - } catch (...){} - bi::address remoteAddress = m_socket.remote_endpoint().address(); - // Port defaults to 0 - we let the hello tell us which port the peer listens to - auto p = std::make_shared(this, std::move(m_socket), m_requiredNetworkId, remoteAddress); - p->start(); - } - catch (std::exception const& _e) - { - clog(NetWarn) << "ERROR: " << _e.what(); - } - m_accepting = false; - if (ec.value() != 1 && (m_mode == NodeMode::PeerServer || m_peers.size() < m_idealPeerCount * 2)) - ensureAccepting(); - }); - } -} - -void PeerServer::connect(std::string const& _addr, unsigned short _port) noexcept -{ - try - { - connect(bi::tcp::endpoint(bi::address::from_string(_addr), _port)); - } - catch (exception const& e) - { - // Couldn't connect - clog(NetNote) << "Bad host " << _addr << " (" << e.what() << ")"; - } -} - -void PeerServer::connect(bi::tcp::endpoint const& _ep) -{ - clog(NetNote) << "Attempting connection to " << _ep; - bi::tcp::socket* s = new bi::tcp::socket(m_ioService); - s->async_connect(_ep, [=](boost::system::error_code const& ec) - { - if (ec) - { - clog(NetNote) << "Connection refused to " << _ep << " (" << ec.message() << ")"; - for (auto i = m_incomingPeers.begin(); i != m_incomingPeers.end(); ++i) - if (i->second.first == _ep && i->second.second < 3) - { - m_freePeers.push_back(i->first); - goto OK; - } - // for-else - clog(NetNote) << "Giving up."; - OK:; - } - else - { - auto p = make_shared(this, std::move(*s), m_requiredNetworkId, _ep.address(), _ep.port()); - clog(NetNote) << "Connected to " << _ep; - p->start(); - } - delete s; - }); -} - -bool PeerServer::sync() -{ - bool ret = false; - if (isInitialised()) - for (auto i = m_peers.begin(); i != m_peers.end();) - { - auto p = i->second.lock(); - if (p && p->m_socket.is_open() && - (p->m_disconnect == chrono::steady_clock::time_point::max() || chrono::steady_clock::now() - p->m_disconnect < chrono::seconds(1))) // kill old peers that should be disconnected. - ++i; - else - { - i = m_peers.erase(i); - ret = true; - } - } - return ret; -} - -bool PeerServer::ensureInitialised(BlockChain& _bc, TransactionQueue& _tq) -{ - if (m_latestBlockSent == h256()) - { - // First time - just initialise. - m_latestBlockSent = _bc.currentHash(); - clog(NetNote) << "Initialising: latest=" << m_latestBlockSent; - - for (auto const& i: _tq.transactions()) - m_transactionsSent.insert(i.first); - m_lastPeersRequest = chrono::steady_clock::time_point::min(); - return true; - } - return false; -} - -bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) -{ - bool ret = ensureInitialised(_bc, _tq); - - if (sync()) - ret = true; - - if (m_mode == NodeMode::Full) - { - for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it) - if (_tq.import(*it)) - {}//ret = true; // just putting a transaction in the queue isn't enough to change the state - it might have an invalid nonce... - else - m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on. - m_incomingTransactions.clear(); - - auto h = _bc.currentHash(); - bool resendAll = (h != m_latestBlockSent); - - // Send any new transactions. - for (auto j: m_peers) - if (auto p = j.second.lock()) - { - bytes b; - uint n = 0; - for (auto const& i: _tq.transactions()) - if ((!m_transactionsSent.count(i.first) && !p->m_knownTransactions.count(i.first)) || p->m_requireTransactions || resendAll) - { - b += i.second; - ++n; - m_transactionsSent.insert(i.first); - } - if (n) - { - RLPStream ts; - PeerSession::prep(ts); - ts.appendList(n + 1) << TransactionsPacket; - ts.appendRaw(b, n).swapOut(b); - seal(b); - p->send(&b); - } - p->m_knownTransactions.clear(); - p->m_requireTransactions = false; - } - - // Send any new blocks. - if (h != m_latestBlockSent) - { - // TODO: find where they diverge and send complete new branch. - RLPStream ts; - PeerSession::prep(ts); - ts.appendList(2) << BlocksPacket; - bytes b; - ts.appendRaw(_bc.block(_bc.currentHash())).swapOut(b); - seal(b); - for (auto j: m_peers) - if (auto p = j.second.lock()) - { - if (!p->m_knownBlocks.count(_bc.currentHash())) - p->send(&b); - p->m_knownBlocks.clear(); - } - } - m_latestBlockSent = h; - - for (int accepted = 1, n = 0; accepted; ++n) - { - accepted = 0; - - if (m_incomingBlocks.size()) - for (auto it = prev(m_incomingBlocks.end());; --it) - { - try - { - _bc.import(*it, _o); - it = m_incomingBlocks.erase(it); - ++accepted; - ret = true; - } - catch (UnknownParent) - { - // Don't (yet) know its parent. Leave it for later. - m_unknownParentBlocks.push_back(*it); - it = m_incomingBlocks.erase(it); - } - catch (...) - { - // Some other error - erase it. - it = m_incomingBlocks.erase(it); - } - - if (it == m_incomingBlocks.begin()) - break; - } - if (!n && accepted) - { - for (auto i: m_unknownParentBlocks) - m_incomingBlocks.push_back(i); - m_unknownParentBlocks.clear(); - } - } - - // Connect to additional peers - while (m_peers.size() < m_idealPeerCount) - { - if (m_freePeers.empty()) - { - if (chrono::steady_clock::now() > m_lastPeersRequest + chrono::seconds(10)) - { - RLPStream s; - bytes b; - (PeerSession::prep(s).appendList(1) << GetPeersPacket).swapOut(b); - seal(b); - for (auto const& i: m_peers) - if (auto p = i.second.lock()) - if (p->isOpen()) - p->send(&b); - m_lastPeersRequest = chrono::steady_clock::now(); - } - - - if (!m_accepting) - ensureAccepting(); - - break; - } - - auto x = time(0) % m_freePeers.size(); - m_incomingPeers[m_freePeers[x]].second++; - connect(m_incomingPeers[m_freePeers[x]].first); - m_freePeers.erase(m_freePeers.begin() + x); - } - } - - // platform for consensus of social contract. - // restricts your freedom but does so fairly. and that's the value proposition. - // guarantees that everyone else respect the rules of the system. (i.e. obeys laws). - - // We'll keep at most twice as many as is ideal, halfing what counts as "too young to kill" until we get there. - for (uint old = 15000; m_peers.size() > m_idealPeerCount * 2 && old > 100; old /= 2) - while (m_peers.size() > m_idealPeerCount) - { - // look for worst peer to kick off - // first work out how many are old enough to kick off. - shared_ptr worst; - unsigned agedPeers = 0; - for (auto i: m_peers) - if (auto p = i.second.lock()) - if ((m_mode != NodeMode::PeerServer || p->m_caps != 0x01) && chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers. - { - ++agedPeers; - if ((!worst || p->m_rating < worst->m_rating || (p->m_rating == worst->m_rating && p->m_connect > worst->m_connect))) // kill older ones - worst = p; - } - if (!worst || agedPeers <= m_idealPeerCount) - break; - worst->disconnect(TooManyPeers); - } - - return ret; -} - -std::vector PeerServer::peers() const -{ - const_cast(this)->pingAll(); - this_thread::sleep_for(chrono::milliseconds(200)); - std::vector ret; - for (auto& i: m_peers) - if (auto j = i.second.lock()) - if (j->m_socket.is_open()) - ret.push_back(j->m_info); - return ret; -} - -void PeerServer::pingAll() -{ - for (auto& i: m_peers) - if (auto j = i.second.lock()) - j->ping(); -} - -bytes PeerServer::savePeers() const -{ - RLPStream ret; - int n = 0; - for (auto& i: m_peers) - if (auto p = i.second.lock()) - if (p->m_socket.is_open() && p->endpoint().port()) - { - ret.appendList(3) << p->endpoint().address().to_v4().to_bytes() << p->endpoint().port() << p->m_id; - n++; - } - return RLPStream(n).appendRaw(ret.out(), n).out(); -} - -void PeerServer::restorePeers(bytesConstRef _b) -{ - for (auto i: RLP(_b)) - { - auto k = (Public)i[2]; - if (!m_incomingPeers.count(k)) - { - m_incomingPeers.insert(make_pair(k, make_pair(bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()), 0))); - m_freePeers.push_back(k); - } - } -} diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index aa5f42da7..955a4c86e 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -17,17 +17,16 @@ /** @file PeerNetwork.h * @author Gav Wood * @date 2014 + * + * Miscellanea required for the PeerServer/PeerSession classes. */ #pragma once -#include -#include -#include +#include #include #include -#include -#include "RLP.h" +#include #include "Common.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; @@ -37,8 +36,11 @@ namespace eth bool isPrivateAddress(bi::address _addressToCheck); +class Overlay; class BlockChain; class TransactionQueue; +class PeerServer; +class PeerSession; struct NetWarn: public LogChannel { static const char* name() { return "!N!"; } static const int verbosity = 0; }; struct NetNote: public LogChannel { static const char* name() { return "*N*"; } static const int verbosity = 1; }; @@ -76,7 +78,8 @@ enum DisconnectReason ClientQuit }; -class PeerServer; +/// @returns the string form of the given disconnection reason. +std::string reasonOf(DisconnectReason _r); struct PeerInfo { @@ -86,63 +89,7 @@ struct PeerInfo std::chrono::steady_clock::duration lastPing; }; -class PeerSession: public std::enable_shared_from_this -{ - friend class PeerServer; - -public: - PeerSession(PeerServer* _server, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, unsigned short _peerPort = 0); - ~PeerSession(); - - void start(); - void disconnect(int _reason); - - void ping(); - - bool isOpen() const { return m_socket.is_open(); } - - static int protocolVersion(); - static int networkId(); - - bi::tcp::endpoint endpoint() const; ///< for other peers to connect to. - -private: - void dropped(); - void doRead(); - void doWrite(std::size_t length); - bool interpret(RLP const& _r); - - /// @returns true iff the _msg forms a valid message for sending or receiving on the network. - static bool checkPacket(bytesConstRef _msg); - - static RLPStream& prep(RLPStream& _s); - void sealAndSend(RLPStream& _s); - void sendDestroy(bytes& _msg); - void send(bytesConstRef _msg); - PeerServer* m_server; - - bi::tcp::socket m_socket; - std::array m_data; - PeerInfo m_info; - Public m_id; - - bytes m_incoming; - uint m_protocolVersion; - uint m_networkId; - uint m_reqNetworkId; - unsigned short m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers. - uint m_caps; - - std::chrono::steady_clock::time_point m_ping; - std::chrono::steady_clock::time_point m_connect; - std::chrono::steady_clock::time_point m_disconnect; - - uint m_rating; - bool m_requireTransactions; - - std::set m_knownBlocks; - std::set m_knownTransactions; -}; +class UPnP; enum class NodeMode { @@ -150,99 +97,4 @@ enum class NodeMode PeerServer }; -class UPnP; - -class PeerServer -{ - friend class PeerSession; - -public: - /// Start server, listening for connections on the given port. - PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, unsigned short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); - /// Start server, but don't listen. - PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m = NodeMode::Full); - ~PeerServer(); - - /// Connect to a peer explicitly. - void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; - void connect(bi::tcp::endpoint const& _ep); - - /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. - bool sync(BlockChain& _bc, TransactionQueue&, Overlay& _o); - bool sync(); - - /// Conduct I/O, polling, syncing, whatever. - /// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway. - /// This won't touch alter the blockchain. - void process() { if (isInitialised()) m_ioService.poll(); } - - /// Set ideal number of peers. - void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } - - void setMode(NodeMode _m) { m_mode = _m; } - - /// Get peer information. - std::vector peers() const; - - /// Get number of peers connected; equivalent to, but faster than, peers().size(). - size_t peerCount() const { return m_peers.size(); } - - /// Ping the peers, to update the latency information. - void pingAll(); - - /// Get the port we're listening on currently. - unsigned short listenPort() const { return m_public.port(); } - - bytes savePeers() const; - void restorePeers(bytesConstRef _b); - -private: - void seal(bytes& _b); - void populateAddresses(); - void determinePublic(std::string const& _publicAddress, bool _upnp); - void ensureAccepting(); - - /// Check to see if the network peer-state initialisation has happened. - bool isInitialised() const { return m_latestBlockSent; } - /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. - bool ensureInitialised(BlockChain& _bc, TransactionQueue& _tq); - - std::map potentialPeers(); - - std::string m_clientVersion; - NodeMode m_mode = NodeMode::Full; - - unsigned short m_listenPort; - - BlockChain const* m_chain = nullptr; - ba::io_service m_ioService; - bi::tcp::acceptor m_acceptor; - bi::tcp::socket m_socket; - - UPnP* m_upnp = nullptr; - bi::tcp::endpoint m_public; - KeyPair m_key; - - uint m_requiredNetworkId; - std::map> m_peers; - - std::vector m_incomingTransactions; - std::vector m_incomingBlocks; - std::vector m_unknownParentBlocks; - std::vector m_freePeers; - std::map> m_incomingPeers; - - h256 m_latestBlockSent; - std::set m_transactionsSent; - - std::chrono::steady_clock::time_point m_lastPeersRequest; - unsigned m_idealPeerCount = 5; - - std::vector m_addresses; - std::vector m_peerAddresses; - - bool m_accepting = false; -}; - - } diff --git a/libethereum/PeerServer.cpp b/libethereum/PeerServer.cpp new file mode 100644 index 000000000..408835ca3 --- /dev/null +++ b/libethereum/PeerServer.cpp @@ -0,0 +1,542 @@ +/* + 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 . +*/ +/** @file PeerNetwork.cpp + * @authors: + * Gav Wood + * Eric Lombrozo + * @date 2014 + */ + +#include "PeerServer.h" + +#include +#ifdef _WIN32 +// winsock is already included +// #include +#else +#include +#endif + +#include +#include +#include +#include "Exceptions.h" +#include "Common.h" +#include "BlockChain.h" +#include "BlockInfo.h" +#include "TransactionQueue.h" +#include "UPnP.h" +#include "PeerSession.h" +using namespace std; +using namespace eth; + +// Addresses we will skip during network interface discovery +// Use a vector as the list is small +// Why this and not names? +// Under MacOSX loopback (127.0.0.1) can be named lo0 and br0 are bridges (0.0.0.0) +static const set c_rejectAddresses = { + {bi::address_v4::from_string("127.0.0.1")}, + {bi::address_v6::from_string("::1")}, + {bi::address_v4::from_string("0.0.0.0")}, + {bi::address_v6::from_string("::")} +}; + +PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, unsigned short _port, NodeMode _m, string const& _publicAddress, bool _upnp): + m_clientVersion(_clientVersion), + m_mode(_m), + m_listenPort(_port), + m_chain(&_ch), + m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)), + m_socket(m_ioService), + m_key(KeyPair::create()), + m_networkId(_networkId) +{ + populateAddresses(); + determinePublic(_publicAddress, _upnp); + ensureAccepting(); + clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); +} + +PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m): + m_clientVersion(_clientVersion), + m_mode(_m), + m_listenPort(0), + m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), + m_socket(m_ioService), + m_key(KeyPair::create()), + m_networkId(_networkId) +{ + // populate addresses. + populateAddresses(); + clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); +} + +PeerServer::~PeerServer() +{ + for (auto const& i: m_peers) + if (auto p = i.second.lock()) + p->disconnect(ClientQuit); + delete m_upnp; +} + +unsigned PeerServer::protocolVersion() +{ + return 8; +} + +void PeerServer::determinePublic(string const& _publicAddress, bool _upnp) +{ + if (_upnp) + try + { + m_upnp = new UPnP; + } + catch (NoUPnPDevice) {} // let m_upnp continue as null - we handle it properly. + + bi::tcp::resolver r(m_ioService); + if (m_upnp && m_upnp->isValid() && m_peerAddresses.size()) + { + clog(NetNote) << "External addr: " << m_upnp->externalIP(); + int p = m_upnp->addRedirect(m_peerAddresses[0].to_string().c_str(), m_listenPort); + if (p) + clog(NetNote) << "Punched through NAT and mapped local port" << m_listenPort << "onto external port" << p << "."; + else + { + // couldn't map + clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place). Assuming " << m_listenPort << " is local & external port."; + p = m_listenPort; + } + + auto eip = m_upnp->externalIP(); + if (eip == string("0.0.0.0") && _publicAddress.empty()) + m_public = bi::tcp::endpoint(bi::address(), (unsigned short)p); + else + { + m_public = bi::tcp::endpoint(bi::address::from_string(_publicAddress.empty() ? eip : _publicAddress), (unsigned short)p); + m_addresses.push_back(m_public.address().to_v4()); + } + } + else + { + // No UPnP - fallback on given public address or, if empty, the assumed peer address. + m_public = bi::tcp::endpoint(_publicAddress.size() ? bi::address::from_string(_publicAddress) + : m_peerAddresses.size() ? m_peerAddresses[0] + : bi::address(), m_listenPort); + m_addresses.push_back(m_public.address().to_v4()); + } +} + +void PeerServer::populateAddresses() +{ +#ifdef _WIN32 + WSAData wsaData; + if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) + throw NoNetworking(); + + char ac[80]; + if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) + { + clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name."; + WSACleanup(); + throw NoNetworking(); + } + + struct hostent* phe = gethostbyname(ac); + if (phe == 0) + { + clog(NetWarn) << "Bad host lookup."; + WSACleanup(); + throw NoNetworking(); + } + + for (int i = 0; phe->h_addr_list[i] != 0; ++i) + { + struct in_addr addr; + memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); + char *addrStr = inet_ntoa(addr); + bi::address ad(bi::address::from_string(addrStr)); + m_addresses.push_back(ad.to_v4()); + bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); + if (!isLocal) + m_peerAddresses.push_back(ad.to_v4()); + clog(NetNote) << "Address: " << ac << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); + } + + WSACleanup(); +#else + ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) + throw NoNetworking(); + + bi::tcp::resolver r(m_ioService); + + for (ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) + { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_addr->sa_family == AF_INET) + { + char host[NI_MAXHOST]; + if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) + continue; + // TODO: Make exception safe when no internet. + auto it = r.resolve({host, "30303"}); + bi::tcp::endpoint ep = it->endpoint(); + bi::address ad = ep.address(); + m_addresses.push_back(ad.to_v4()); + bool isLocal = std::find(c_rejectAddresses.begin(), c_rejectAddresses.end(), ad) != c_rejectAddresses.end(); + if (!isLocal) + m_peerAddresses.push_back(ad.to_v4()); + clog(NetNote) << "Address: " << host << " = " << m_addresses.back() << (isLocal ? " [LOCAL]" : " [PEER]"); + } + } + + freeifaddrs(ifaddr); +#endif +} + +std::map PeerServer::potentialPeers() +{ + std::map ret; + if (!m_public.address().is_unspecified()) + ret.insert(make_pair(m_key.pub(), m_public)); + for (auto i: m_peers) + if (auto j = i.second.lock()) + { + auto ep = j->endpoint(); + // Skip peers with a listen port of zero or are on a private network + bool peerOnNet = (j->m_listenPort != 0 && !isPrivateAddress(ep.address())); + if (peerOnNet && ep.port() && j->m_id) + ret.insert(make_pair(i.first, ep)); + } + return ret; +} + +void PeerServer::ensureAccepting() +{ + if (m_accepting == false) + { + clog(NetNote) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")"; + m_accepting = true; + m_acceptor.async_accept(m_socket, [=](boost::system::error_code ec) + { + if (!ec) + try + { + try { + clog(NetNote) << "Accepted connection from " << m_socket.remote_endpoint(); + } catch (...){} + bi::address remoteAddress = m_socket.remote_endpoint().address(); + // Port defaults to 0 - we let the hello tell us which port the peer listens to + auto p = std::make_shared(this, std::move(m_socket), m_networkId, remoteAddress); + p->start(); + } + catch (std::exception const& _e) + { + clog(NetWarn) << "ERROR: " << _e.what(); + } + m_accepting = false; + if (ec.value() != 1 && (m_mode == NodeMode::PeerServer || m_peers.size() < m_idealPeerCount * 2)) + ensureAccepting(); + }); + } +} + +void PeerServer::connect(std::string const& _addr, unsigned short _port) noexcept +{ + try + { + connect(bi::tcp::endpoint(bi::address::from_string(_addr), _port)); + } + catch (exception const& e) + { + // Couldn't connect + clog(NetNote) << "Bad host " << _addr << " (" << e.what() << ")"; + } +} + +void PeerServer::connect(bi::tcp::endpoint const& _ep) +{ + clog(NetNote) << "Attempting connection to " << _ep; + bi::tcp::socket* s = new bi::tcp::socket(m_ioService); + s->async_connect(_ep, [=](boost::system::error_code const& ec) + { + if (ec) + { + clog(NetNote) << "Connection refused to " << _ep << " (" << ec.message() << ")"; + for (auto i = m_incomingPeers.begin(); i != m_incomingPeers.end(); ++i) + if (i->second.first == _ep && i->second.second < 3) + { + m_freePeers.push_back(i->first); + goto OK; + } + // for-else + clog(NetNote) << "Giving up."; + OK:; + } + else + { + auto p = make_shared(this, std::move(*s), m_networkId, _ep.address(), _ep.port()); + clog(NetNote) << "Connected to " << _ep; + p->start(); + } + delete s; + }); +} + +bool PeerServer::sync() +{ + bool ret = false; + if (isInitialised()) + for (auto i = m_peers.begin(); i != m_peers.end();) + { + auto p = i->second.lock(); + if (p && p->m_socket.is_open() && + (p->m_disconnect == chrono::steady_clock::time_point::max() || chrono::steady_clock::now() - p->m_disconnect < chrono::seconds(1))) // kill old peers that should be disconnected. + ++i; + else + { + i = m_peers.erase(i); + ret = true; + } + } + return ret; +} + +bool PeerServer::ensureInitialised(BlockChain& _bc, TransactionQueue& _tq) +{ + if (m_latestBlockSent == h256()) + { + // First time - just initialise. + m_latestBlockSent = _bc.currentHash(); + clog(NetNote) << "Initialising: latest=" << m_latestBlockSent; + + for (auto const& i: _tq.transactions()) + m_transactionsSent.insert(i.first); + m_lastPeersRequest = chrono::steady_clock::time_point::min(); + return true; + } + return false; +} + +bool PeerServer::sync(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) +{ + bool ret = ensureInitialised(_bc, _tq); + + if (sync()) + ret = true; + + if (m_mode == NodeMode::Full) + { + for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it) + if (_tq.import(*it)) + {}//ret = true; // just putting a transaction in the queue isn't enough to change the state - it might have an invalid nonce... + else + m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on. + m_incomingTransactions.clear(); + + auto h = _bc.currentHash(); + bool resendAll = (h != m_latestBlockSent); + + // Send any new transactions. + for (auto j: m_peers) + if (auto p = j.second.lock()) + { + bytes b; + uint n = 0; + for (auto const& i: _tq.transactions()) + if ((!m_transactionsSent.count(i.first) && !p->m_knownTransactions.count(i.first)) || p->m_requireTransactions || resendAll) + { + b += i.second; + ++n; + m_transactionsSent.insert(i.first); + } + if (n) + { + RLPStream ts; + PeerSession::prep(ts); + ts.appendList(n + 1) << TransactionsPacket; + ts.appendRaw(b, n).swapOut(b); + seal(b); + p->send(&b); + } + p->m_knownTransactions.clear(); + p->m_requireTransactions = false; + } + + // Send any new blocks. + if (h != m_latestBlockSent) + { + // TODO: find where they diverge and send complete new branch. + RLPStream ts; + PeerSession::prep(ts); + ts.appendList(2) << BlocksPacket; + bytes b; + ts.appendRaw(_bc.block(_bc.currentHash())).swapOut(b); + seal(b); + for (auto j: m_peers) + if (auto p = j.second.lock()) + { + if (!p->m_knownBlocks.count(_bc.currentHash())) + p->send(&b); + p->m_knownBlocks.clear(); + } + } + m_latestBlockSent = h; + + for (int accepted = 1, n = 0; accepted; ++n) + { + accepted = 0; + + if (m_incomingBlocks.size()) + for (auto it = prev(m_incomingBlocks.end());; --it) + { + try + { + _bc.import(*it, _o); + it = m_incomingBlocks.erase(it); + ++accepted; + ret = true; + } + catch (UnknownParent) + { + // Don't (yet) know its parent. Leave it for later. + m_unknownParentBlocks.push_back(*it); + it = m_incomingBlocks.erase(it); + } + catch (...) + { + // Some other error - erase it. + it = m_incomingBlocks.erase(it); + } + + if (it == m_incomingBlocks.begin()) + break; + } + if (!n && accepted) + { + for (auto i: m_unknownParentBlocks) + m_incomingBlocks.push_back(i); + m_unknownParentBlocks.clear(); + } + } + + // Connect to additional peers + while (m_peers.size() < m_idealPeerCount) + { + if (m_freePeers.empty()) + { + if (chrono::steady_clock::now() > m_lastPeersRequest + chrono::seconds(10)) + { + RLPStream s; + bytes b; + (PeerSession::prep(s).appendList(1) << GetPeersPacket).swapOut(b); + seal(b); + for (auto const& i: m_peers) + if (auto p = i.second.lock()) + if (p->isOpen()) + p->send(&b); + m_lastPeersRequest = chrono::steady_clock::now(); + } + + + if (!m_accepting) + ensureAccepting(); + + break; + } + + auto x = time(0) % m_freePeers.size(); + m_incomingPeers[m_freePeers[x]].second++; + connect(m_incomingPeers[m_freePeers[x]].first); + m_freePeers.erase(m_freePeers.begin() + x); + } + } + + // platform for consensus of social contract. + // restricts your freedom but does so fairly. and that's the value proposition. + // guarantees that everyone else respect the rules of the system. (i.e. obeys laws). + + // We'll keep at most twice as many as is ideal, halfing what counts as "too young to kill" until we get there. + for (uint old = 15000; m_peers.size() > m_idealPeerCount * 2 && old > 100; old /= 2) + while (m_peers.size() > m_idealPeerCount) + { + // look for worst peer to kick off + // first work out how many are old enough to kick off. + shared_ptr worst; + unsigned agedPeers = 0; + for (auto i: m_peers) + if (auto p = i.second.lock()) + if ((m_mode != NodeMode::PeerServer || p->m_caps != 0x01) && chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers. + { + ++agedPeers; + if ((!worst || p->m_rating < worst->m_rating || (p->m_rating == worst->m_rating && p->m_connect > worst->m_connect))) // kill older ones + worst = p; + } + if (!worst || agedPeers <= m_idealPeerCount) + break; + worst->disconnect(TooManyPeers); + } + + return ret; +} + +std::vector PeerServer::peers() const +{ + const_cast(this)->pingAll(); + this_thread::sleep_for(chrono::milliseconds(200)); + std::vector ret; + for (auto& i: m_peers) + if (auto j = i.second.lock()) + if (j->m_socket.is_open()) + ret.push_back(j->m_info); + return ret; +} + +void PeerServer::pingAll() +{ + for (auto& i: m_peers) + if (auto j = i.second.lock()) + j->ping(); +} + +bytes PeerServer::savePeers() const +{ + RLPStream ret; + int n = 0; + for (auto& i: m_peers) + if (auto p = i.second.lock()) + if (p->m_socket.is_open() && p->endpoint().port()) + { + ret.appendList(3) << p->endpoint().address().to_v4().to_bytes() << p->endpoint().port() << p->m_id; + n++; + } + return RLPStream(n).appendRaw(ret.out(), n).out(); +} + +void PeerServer::restorePeers(bytesConstRef _b) +{ + for (auto i: RLP(_b)) + { + auto k = (Public)i[2]; + if (!m_incomingPeers.count(k)) + { + m_incomingPeers.insert(make_pair(k, make_pair(bi::tcp::endpoint(bi::address_v4(i[0].toArray()), i[1].toInt()), 0))); + m_freePeers.push_back(k); + } + } +} diff --git a/libethereum/PeerSession.cpp b/libethereum/PeerSession.cpp new file mode 100644 index 000000000..0c8699829 --- /dev/null +++ b/libethereum/PeerSession.cpp @@ -0,0 +1,585 @@ +/* + 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 . +*/ +/** @file PeerSession.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "PeerSession.h" + +#include +#include "Exceptions.h" +#include "Common.h" +#include "BlockChain.h" +#include "BlockInfo.h" +#include "PeerServer.h" +using namespace std; +using namespace eth; + +#define clogS(X) eth::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " + +static const eth::uint c_maxHashes = 32; ///< Maximum number of hashes GetChain will ever send. +static const eth::uint c_maxBlocks = 32; ///< Maximum number of blocks Blocks will ever send. BUG: if this gets too big (e.g. 2048) stuff starts going wrong. +static const eth::uint c_maxBlocksAsk = 256; ///< Maximum number of blocks we ask to receive in Blocks (when using GetChain). + +PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, unsigned short _peerPort): + m_server(_s), + m_socket(std::move(_socket)), + m_reqNetworkId(_rNId), + m_listenPort(_peerPort), + m_rating(0) +{ + m_disconnect = std::chrono::steady_clock::time_point::max(); + m_connect = std::chrono::steady_clock::now(); + m_info = PeerInfo({"?", _peerAddress.to_string(), m_listenPort, std::chrono::steady_clock::duration(0)}); +} + +PeerSession::~PeerSession() +{ + m_socket.close(); +} + +bi::tcp::endpoint PeerSession::endpoint() const +{ + if (m_socket.is_open()) + try { + return bi::tcp::endpoint(m_socket.remote_endpoint().address(), m_listenPort); + } catch (...){} + + return bi::tcp::endpoint(); +} + +// TODO: BUG! 256 -> work out why things start to break with big packet sizes -> g.t. ~370 blocks. + +bool PeerSession::interpret(RLP const& _r) +{ + clogS(NetRight) << _r; + switch (_r[0].toInt()) + { + case HelloPacket: + { + m_protocolVersion = _r[1].toInt(); + m_networkId = _r[2].toInt(); + auto clientVersion = _r[3].toString(); + m_caps = _r[4].toInt(); + m_listenPort = _r[5].toInt(); + m_id = _r[6].toHash(); + + clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << m_id.abridged() << showbase << hex << m_caps << dec << m_listenPort; + + if (m_server->m_peers.count(m_id)) + if (auto l = m_server->m_peers[m_id].lock()) + if (l.get() != this && l->isOpen()) + { + // Already connected. + cwarn << "Already have peer id" << m_id.abridged() << "at" << l->endpoint() << "rather than" << endpoint(); + disconnect(DuplicatePeer); + return false; + } + + if (m_protocolVersion != PeerServer::protocolVersion() || m_networkId != m_server->networkId() || !m_id) + { + disconnect(IncompatibleProtocol); + return false; + } + try + { m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), m_listenPort, std::chrono::steady_clock::duration()}); } + catch (...) + { + disconnect(BadProtocol); + return false; + } + + m_server->m_peers[m_id] = shared_from_this(); + + // Grab their block chain off them. + { + clogS(NetAllDetail) << "Want chain. Latest:" << m_server->m_latestBlockSent << ", number:" << m_server->m_chain->details(m_server->m_latestBlockSent).number; + uint count = std::min(c_maxHashes, m_server->m_chain->details(m_server->m_latestBlockSent).number + 1); + RLPStream s; + prep(s).appendList(2 + count); + s << GetChainPacket; + auto h = m_server->m_latestBlockSent; + for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) + { + clogS(NetAllDetail) << " " << i << ":" << h; + s << h; + } + + s << c_maxBlocksAsk; + sealAndSend(s); + s.clear(); + prep(s).appendList(1); + s << GetTransactionsPacket; + sealAndSend(s); + } + break; + } + case DisconnectPacket: + { + string reason = "Unspecified"; + if (_r[1].isInt()) + reason = reasonOf((DisconnectReason)_r[1].toInt()); + + clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")"; + if (m_socket.is_open()) + clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); + else + clogS(NetNote) << "Remote closed."; + m_socket.close(); + return false; + } + case PingPacket: + { +// clogS(NetMessageSummary) << "Ping"; + RLPStream s; + sealAndSend(prep(s).appendList(1) << PongPacket); + break; + } + case PongPacket: + m_info.lastPing = std::chrono::steady_clock::now() - m_ping; +// clogS(NetMessageSummary) << "Latency: " << chrono::duration_cast(m_lastPing).count() << " ms"; + break; + case GetPeersPacket: + { + clogS(NetMessageSummary) << "GetPeers"; + auto peers = m_server->potentialPeers(); + RLPStream s; + prep(s).appendList(peers.size() + 1); + s << PeersPacket; + for (auto i: peers) + { + clogS(NetMessageDetail) << "Sending peer " << asHex(i.first.ref().cropped(0, 4)) << i.second; + s.appendList(3) << i.second.address().to_v4().to_bytes() << i.second.port() << i.first; + } + sealAndSend(s); + break; + } + case PeersPacket: + clogS(NetMessageSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; + for (unsigned i = 1; i < _r.itemCount(); ++i) + { + bi::address_v4 peerAddress(_r[i][0].toArray()); + auto ep = bi::tcp::endpoint(peerAddress, _r[i][1].toInt()); + Public id = _r[i][2].toHash(); + if (isPrivateAddress(peerAddress)) + goto CONTINUE; + + clogS(NetAllDetail) << "Checking: " << ep << "(" << asHex(id.ref().cropped(0, 4)) << ")"; + + // check that it's not us or one we already know: + if (id && (m_server->m_key.pub() == id || m_server->m_peers.count(id) || m_server->m_incomingPeers.count(id))) + goto CONTINUE; + + // check that we're not already connected to addr: + if (!ep.port()) + goto CONTINUE; + for (auto i: m_server->m_addresses) + if (ep.address() == i && ep.port() == m_server->listenPort()) + goto CONTINUE; + for (auto i: m_server->m_peers) + if (shared_ptr p = i.second.lock()) + { + clogS(NetAllDetail) << " ...against " << p->endpoint(); + if (p->m_socket.is_open() && p->endpoint() == ep) + goto CONTINUE; + } + for (auto i: m_server->m_incomingPeers) + if (i.second.first == ep) + goto CONTINUE; + m_server->m_incomingPeers[id] = make_pair(ep, 0); + m_server->m_freePeers.push_back(id); + clogS(NetMessageDetail) << "New peer: " << ep << "(" << id << ")"; + CONTINUE:; + } + break; + case TransactionsPacket: + if (m_server->m_mode == NodeMode::PeerServer) + break; + clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << " entries)"; + m_rating += _r.itemCount() - 1; + for (unsigned i = 1; i < _r.itemCount(); ++i) + { + m_server->m_incomingTransactions.push_back(_r[i].data().toBytes()); + m_knownTransactions.insert(sha3(_r[i].data())); + } + break; + case BlocksPacket: + { + if (m_server->m_mode == NodeMode::PeerServer) + break; + clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << " entries)"; + unsigned used = 0; + for (unsigned i = 1; i < _r.itemCount(); ++i) + { + auto h = sha3(_r[i].data()); + if (!m_server->m_chain->details(h)) + { + m_server->m_incomingBlocks.push_back(_r[i].data().toBytes()); + m_knownBlocks.insert(h); + used++; + } + } + m_rating += used; + if (g_logVerbosity >= 3) + for (unsigned i = 1; i < _r.itemCount(); ++i) + { + auto h = sha3(_r[i].data()); + BlockInfo bi(_r[i].data()); + if (!m_server->m_chain->details(bi.parentHash) && !m_knownBlocks.count(bi.parentHash)) + clogS(NetMessageDetail) << "Unknown parent " << bi.parentHash << " of block " << h; + else + clogS(NetMessageDetail) << "Known parent " << bi.parentHash << " of block " << h; + } + if (used) // we received some - check if there's any more + { + RLPStream s; + prep(s).appendList(3); + s << GetChainPacket; + s << sha3(_r[1].data()); + s << c_maxBlocksAsk; + sealAndSend(s); + } + break; + } + case GetChainPacket: + { + if (m_server->m_mode == NodeMode::PeerServer) + break; + clogS(NetMessageSummary) << "GetChain (" << (_r.itemCount() - 2) << " hashes, " << (_r[_r.itemCount() - 1].toInt()) << ")"; + // ******************************************************************** + // NEEDS FULL REWRITE! + h256s parents; + parents.reserve(_r.itemCount() - 2); + for (unsigned i = 1; i < _r.itemCount() - 1; ++i) + parents.push_back(_r[i].toHash()); + if (_r.itemCount() == 2) + break; + // return 2048 block max. + uint baseCount = (uint)min(_r[_r.itemCount() - 1].toInt(), c_maxBlocks); + clogS(NetMessageSummary) << "GetChain (" << baseCount << " max, from " << parents.front() << " to " << parents.back() << ")"; + for (auto parent: parents) + { + auto h = m_server->m_chain->currentHash(); + h256 latest = m_server->m_chain->currentHash(); + uint latestNumber = 0; + uint parentNumber = 0; + RLPStream s; + + if (m_server->m_chain->details(parent)) + { + latestNumber = m_server->m_chain->details(latest).number; + parentNumber = m_server->m_chain->details(parent).number; + uint count = min(latestNumber - parentNumber, baseCount); + clogS(NetAllDetail) << "Requires " << dec << (latestNumber - parentNumber) << " blocks from " << latestNumber << " to " << parentNumber; + clogS(NetAllDetail) << latest << " - " << parent; + + prep(s); + s.appendList(1 + count) << BlocksPacket; + uint endNumber = m_server->m_chain->details(parent).number; + uint startNumber = endNumber + count; + clogS(NetAllDetail) << "Sending " << dec << count << " blocks from " << startNumber << " to " << endNumber; + + uint n = latestNumber; + for (; n > startNumber; n--, h = m_server->m_chain->details(h).parent) {} + for (uint i = 0; i < count; ++i, --n, h = m_server->m_chain->details(h).parent) + { + if (h == parent || n == endNumber) + { + cwarn << "BUG! Couldn't create the reply for GetChain!"; + return true; + } + clogS(NetAllDetail) << " " << dec << i << " " << h; + s.appendRaw(m_server->m_chain->block(h)); + } + clogS(NetAllDetail) << "Parent: " << h; + } + else if (parent != parents.back()) + continue; + + if (h != parent) + { + // not in the blockchain; + if (parent == parents.back()) + { + // out of parents... + clogS(NetAllDetail) << "GetChain failed; not in chain"; + // No good - must have been on a different branch. + s.clear(); + prep(s).appendList(2) << NotInChainPacket << parents.back(); + } + else + // still some parents left - try them. + continue; + } + // send the packet (either Blocks or NotInChain) & exit. + sealAndSend(s); + break; + // ******************************************************************** + } + break; + } + case NotInChainPacket: + { + if (m_server->m_mode == NodeMode::PeerServer) + break; + h256 noGood = _r[1].toHash(); + clogS(NetMessageSummary) << "NotInChain (" << noGood << ")"; + if (noGood == m_server->m_chain->genesisHash()) + { + clogS(NetWarn) << "Discordance over genesis block! Disconnect."; + disconnect(WrongGenesis); + } + else + { + uint count = std::min(c_maxHashes, m_server->m_chain->details(noGood).number); + RLPStream s; + prep(s).appendList(2 + count); + s << GetChainPacket; + auto h = m_server->m_chain->details(noGood).parent; + for (uint i = 0; i < count; ++i, h = m_server->m_chain->details(h).parent) + s << h; + s << c_maxBlocksAsk; + sealAndSend(s); + } + break; + } + case GetTransactionsPacket: + { + if (m_server->m_mode == NodeMode::PeerServer) + break; + m_requireTransactions = true; + break; + } + default: + break; + } + return true; +} + +void PeerSession::ping() +{ + RLPStream s; + sealAndSend(prep(s).appendList(1) << PingPacket); + m_ping = std::chrono::steady_clock::now(); +} + +RLPStream& PeerSession::prep(RLPStream& _s) +{ + return _s.appendRaw(bytes(8, 0)); +} + +void PeerServer::seal(bytes& _b) +{ + _b[0] = 0x22; + _b[1] = 0x40; + _b[2] = 0x08; + _b[3] = 0x91; + uint32_t len = (uint32_t)_b.size() - 8; + _b[4] = (len >> 24) & 0xff; + _b[5] = (len >> 16) & 0xff; + _b[6] = (len >> 8) & 0xff; + _b[7] = len & 0xff; +} + +void PeerSession::sealAndSend(RLPStream& _s) +{ + bytes b; + _s.swapOut(b); + m_server->seal(b); + sendDestroy(b); +} + +bool PeerSession::checkPacket(bytesConstRef _msg) +{ + if (_msg.size() < 8) + return false; + if (!(_msg[0] == 0x22 && _msg[1] == 0x40 && _msg[2] == 0x08 && _msg[3] == 0x91)) + return false; + uint32_t len = ((_msg[4] * 256 + _msg[5]) * 256 + _msg[6]) * 256 + _msg[7]; + if (_msg.size() != len + 8) + return false; + RLP r(_msg.cropped(8)); + if (r.actualSize() != len) + return false; + return true; +} + +void PeerSession::sendDestroy(bytes& _msg) +{ + clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); + + if (!checkPacket(bytesConstRef(&_msg))) + { + cwarn << "INVALID PACKET CONSTRUCTED!"; + } + + auto self(shared_from_this()); + bytes* buffer = new bytes(std::move(_msg)); + ba::async_write(m_socket, ba::buffer(*buffer), [self, buffer](boost::system::error_code ec, std::size_t length) + { + delete buffer; + if (ec) + { + cwarn << "Error sending: " << ec.message(); + self->dropped(); + } +// cbug << length << " bytes written (EC: " << ec << ")"; + }); +} + +void PeerSession::send(bytesConstRef _msg) +{ + clogS(NetLeft) << RLP(_msg.cropped(8)); + + if (!checkPacket(_msg)) + { + cwarn << "INVALID PACKET CONSTRUCTED!"; + } + + auto self(shared_from_this()); + bytes* buffer = new bytes(_msg.toBytes()); + ba::async_write(m_socket, ba::buffer(*buffer), [self, buffer](boost::system::error_code ec, std::size_t length) + { + delete buffer; + if (ec) + { + cwarn << "Error sending: " << ec.message(); + self->dropped(); + } +// cbug << length << " bytes written (EC: " << ec << ")"; + }); +} + +void PeerSession::dropped() +{ + if (m_socket.is_open()) + try { + clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); + m_socket.close(); + }catch (...){} + for (auto i = m_server->m_peers.begin(); i != m_server->m_peers.end(); ++i) + if (i->second.lock().get() == this) + { + m_server->m_peers.erase(i); + break; + } +} + +void PeerSession::disconnect(int _reason) +{ + clogS(NetNote) << "Disconnecting (reason:" << reasonOf((DisconnectReason)_reason) << ")"; + if (m_socket.is_open()) + { + if (m_disconnect == chrono::steady_clock::time_point::max()) + { + RLPStream s; + prep(s); + s.appendList(2) << DisconnectPacket << _reason; + sealAndSend(s); + m_disconnect = chrono::steady_clock::now(); + } + else + dropped(); + } +} + +void PeerSession::start() +{ + RLPStream s; + prep(s); + s.appendList(7) << HelloPacket << (uint)PeerServer::protocolVersion() << m_server->networkId() << m_server->m_clientVersion << (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0) << m_server->m_public.port() << m_server->m_key.pub(); + sealAndSend(s); + + ping(); + + doRead(); +} + +void PeerSession::doRead() +{ + auto self(shared_from_this()); + m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) + { + if (ec) + { + cwarn << "Error reading: " << ec.message(); + dropped(); + } + else + { + try + { + m_incoming.resize(m_incoming.size() + length); + memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); + while (m_incoming.size() > 8) + { + if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) + { + clogS(NetWarn) << "Out of alignment."; + disconnect(BadProtocol); + return; + clogS(NetNote) << "Skipping: " << hex << showbase << (int)m_incoming[0] << dec; + memmove(m_incoming.data(), m_incoming.data() + 1, m_incoming.size() - 1); + m_incoming.resize(m_incoming.size() - 1); + } + else + { + uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data() + 4, 4)); + uint32_t tlen = len + 8; + if (m_incoming.size() < tlen) + break; + + // enough has come in. +// cerr << "Received " << len << ": " << asHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; + auto data = bytesConstRef(m_incoming.data(), tlen); + if (!checkPacket(data)) + { + cerr << "Received " << len << ": " << asHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; + cwarn << "INVALID MESSAGE RECEIVED"; + disconnect(BadProtocol); + return; + } + else + { + RLP r(data.cropped(8)); + if (!interpret(r)) + { + // error + dropped(); + return; + } + } + memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); + m_incoming.resize(m_incoming.size() - tlen); + } + } + doRead(); + } + catch (Exception const& _e) + { + clogS(NetWarn) << "ERROR: " << _e.description(); + dropped(); + } + catch (std::exception const& _e) + { + clogS(NetWarn) << "ERROR: " << _e.what(); + dropped(); + } + } + }); +} diff --git a/test/peer.cpp b/test/peer.cpp index 21aa85004..7a1f3248f 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include using namespace std; using namespace eth; using boost::asio::ip::tcp; From 607a5de3408758b0d2b6f7e0a75ff9df2be45448 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 4 Mar 2014 11:46:26 -0600 Subject: [PATCH 21/28] Repotted Common.h. --- alethzero/MainWin.cpp | 14 +- alethzero/MainWin.h | 2 +- eth/main.cpp | 16 +- libethereum/BlockChain.cpp | 2 +- libethereum/BlockChain.h | 3 +- libethereum/Common.cpp | 280 -------------- libethereum/Common.h | 611 +------------------------------ libethereum/CommonData.cpp | 96 +++++ libethereum/CommonEth.cpp | 200 ++++++++++ libethereum/CommonIO.cpp | 48 +++ libethereum/Dagger.h | 3 +- libethereum/Exceptions.h | 12 +- libethereum/FixedHash.cpp | 25 ++ libethereum/Instruction.cpp | 3 +- libethereum/Log.cpp | 41 +++ libethereum/MemTrie.cpp | 12 +- libethereum/PeerNetwork.h | 1 + libethereum/PeerServer.cpp | 4 +- libethereum/PeerSession.cpp | 8 +- libethereum/State.cpp | 8 +- libethereum/State.h | 4 +- libethereum/Transaction.cpp | 2 +- libethereum/Transaction.h | 2 +- libethereum/TransactionQueue.cpp | 1 + libethereum/TrieDB.h | 6 +- libethereum/TrieHash.cpp | 12 +- libethereum/TrieHash.h | 1 + libethereum/UPnP.cpp | 1 + test/crypto.cpp | 37 +- test/dagger.cpp | 1 + test/hexPrefix.cpp | 5 +- test/main.cpp | 2 +- test/rlp.cpp | 5 +- test/trie.cpp | 6 +- test/vm.cpp | 1 + 35 files changed, 507 insertions(+), 968 deletions(-) create mode 100644 libethereum/CommonData.cpp create mode 100644 libethereum/CommonEth.cpp create mode 100644 libethereum/CommonIO.cpp create mode 100644 libethereum/FixedHash.cpp create mode 100644 libethereum/Log.cpp diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 5fd9b5509..e0415ca8b 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -31,12 +31,12 @@ using eth::Secret; using eth::Transaction; // functions -using eth::asHex; +using eth::toHex; using eth::assemble; using eth::compileLisp; using eth::disassemble; using eth::formatBalance; -using eth::fromUserHex; +using eth::fromHex; using eth::right160; using eth::simpleDebugOut; using eth::toLog2; @@ -180,7 +180,7 @@ Address Main::fromString(QString const& _a) const if (h256 a = state().contractMemory(m_nameReg, n)) return right160(a); if (_a.size() == 40) - return Address(fromUserHex(_a.toStdString())); + return Address(fromHex(_a.toStdString())); else return Address(); } @@ -258,7 +258,7 @@ void Main::on_nameReg_textChanged() string s = ui->nameReg->text().toStdString(); if (s.size() == 40) { - m_nameReg = Address(fromUserHex(s)); + m_nameReg = Address(fromHex(s)); refresh(true); } } @@ -512,7 +512,7 @@ void Main::on_ourAccounts_doubleClicked() { auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); - qApp->clipboard()->setText(QString::fromStdString(asHex(h.asArray()))); + qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); } void Main::on_log_doubleClicked() @@ -524,14 +524,14 @@ void Main::on_accounts_doubleClicked() { auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray(); auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); - qApp->clipboard()->setText(QString::fromStdString(asHex(h.asArray()))); + qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); } void Main::on_contracts_doubleClicked() { auto hba = ui->contracts->currentItem()->data(Qt::UserRole).toByteArray(); auto h = Address((byte const*)hba.data(), Address::ConstructFromPointer); - qApp->clipboard()->setText(QString::fromStdString(asHex(h.asArray()))); + qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); } void Main::on_destination_textChanged() diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 70b2d051c..9f8f781f6 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include namespace Ui { class Main; diff --git a/eth/main.cpp b/eth/main.cpp index b8e26aefb..955721f05 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -138,9 +138,9 @@ int main(int argc, char** argv) else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) clientName = argv[++i]; else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) - coinbase = h160(fromUserHex(argv[++i])); + coinbase = h160(fromHex(argv[++i])); else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) - us = KeyPair(h256(fromUserHex(argv[++i]))); + us = KeyPair(h256(fromHex(argv[++i]))); else if (arg == "-i" || arg == "--interactive") interactive = true; else if ((arg == "-d" || arg == "--path" || arg == "--db-path") && i + 1 < argc) @@ -228,13 +228,13 @@ int main(int argc, char** argv) else if (cmd == "address") { cout << endl; - cout << "Current address: " + asHex(us.address().asArray()) << endl; + cout << "Current address: " + toHex(us.address().asArray()) << endl; cout << "===" << endl; } else if (cmd == "secret") { cout << endl; - cout << "Current secret: " + asHex(us.secret().asArray()) << endl; + cout << "Current secret: " + toHex(us.secret().asArray()) << endl; cout << "===" << endl; } else if (cmd == "balance") @@ -251,8 +251,8 @@ int main(int argc, char** argv) string rechex; u256 amount; cin >> sechex >> rechex >> amount; - Secret secret = h256(fromUserHex(sechex)); - Address dest = h160(fromUserHex(rechex)); + Secret secret = h256(fromHex(sechex)); + Address dest = h160(fromHex(rechex)); c.transact(secret, dest, amount); } else if (cmd == "send") @@ -260,7 +260,7 @@ int main(int argc, char** argv) string rechex; u256 amount; cin >> rechex >> amount; - Address dest = h160(fromUserHex(rechex)); + Address dest = h160(fromHex(rechex)); c.transact(us.secret(), dest, amount); } else if (cmd == "exit") @@ -271,7 +271,7 @@ int main(int argc, char** argv) } else { - cout << "Address: " << endl << asHex(us.address().asArray()) << endl; + cout << "Address: " << endl << toHex(us.address().asArray()) << endl; c.startNetwork(listenPort, remoteHost, remotePort, mode, peers, publicIP, upnp); eth::uint n = c.blockChain().details().number; if (mining) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 7bf7606da..644c5e2d9 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -45,7 +45,7 @@ std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc) if (it->key().ToString() != "best") { BlockDetails d(RLP(it->value().ToString())); - _out << asHex(it->key().ToString()) << ": " << d.number << " @ " << d.parent << (cmp == it->key().ToString() ? " BEST" : "") << std::endl; + _out << toHex(it->key().ToString()) << ": " << d.number << " @ " << d.parent << (cmp == it->key().ToString() ? " BEST" : "") << std::endl; } delete it; return _out; diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index ee208e508..b295253c7 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -22,7 +22,8 @@ #pragma once #include -#include "Common.h" +#include "CommonEth.h" +#include "Log.h" #include "AddressState.h" namespace ldb = leveldb; diff --git a/libethereum/Common.cpp b/libethereum/Common.cpp index 2b08c84fd..7c1d80ff2 100644 --- a/libethereum/Common.cpp +++ b/libethereum/Common.cpp @@ -21,285 +21,5 @@ #include "Common.h" -#include -#include -#if WIN32 -#pragma warning(push) -#pragma warning(disable:4244) -#else -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -#include -#include -#if WIN32 -#pragma warning(pop) -#else -#endif -#include "Exceptions.h" using namespace std; using namespace eth; - -//#define ETH_ADDRESS_DEBUG 1 - -// Logging -int eth::g_logVerbosity = 8; -map eth::g_logOverride; - -ThreadLocalLogName eth::t_logThreadName("main"); - -void eth::simpleDebugOut(std::string const& _s, char const*) -{ - cout << _s << endl << flush; -} - -std::function eth::g_logPost = simpleDebugOut; - -std::string eth::escaped(std::string const& _s, bool _all) -{ - std::string ret; - ret.reserve(_s.size()); - ret.push_back('"'); - for (auto i: _s) - if (i == '"' && !_all) - ret += "\\\""; - else if (i == '\\' && !_all) - ret += "\\\\"; - else if (i < ' ' || i > 127 || _all) - { - ret += "\\x"; - ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); - ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); - } - else - ret.push_back(i); - ret.push_back('"'); - return ret; -} - -std::string eth::randomWord() -{ - static std::mt19937_64 s_eng(0); - std::string ret(std::uniform_int_distribution(4, 10)(s_eng), ' '); - char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; - std::uniform_int_distribution d(0, sizeof(n) - 2); - for (char& c: ret) - c = n[d(s_eng)]; - return ret; -} - -int eth::fromHex(char _i) -{ - if (_i >= '0' && _i <= '9') - return _i - '0'; - if (_i >= 'a' && _i <= 'f') - return _i - 'a' + 10; - if (_i >= 'A' && _i <= 'F') - return _i - 'A' + 10; - throw BadHexCharacter(); -} - -bytes eth::fromUserHex(std::string const& _s) -{ - assert(_s.size() % 2 == 0); - if (_s.size() < 2) - return bytes(); - uint s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; - std::vector ret; - ret.reserve((_s.size() - s) / 2); - for (uint i = s; i < _s.size(); i += 2) - ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); - return ret; -} - -bytes eth::toHex(std::string const& _s) -{ - std::vector ret; - ret.reserve(_s.size() * 2); - for (auto i: _s) - { - ret.push_back(i / 16); - ret.push_back(i % 16); - } - return ret; -} - -std::string eth::sha3(std::string const& _input, bool _hex) -{ - if (!_hex) - { - string ret(32, '\0'); - sha3(bytesConstRef((byte const*)_input.data(), _input.size()), bytesRef((byte*)ret.data(), 32)); - return ret; - } - - uint8_t buf[32]; - sha3(bytesConstRef((byte const*)_input.data(), _input.size()), bytesRef((byte*)&(buf[0]), 32)); - std::string ret(64, '\0'); - for (unsigned int i = 0; i < 32; i++) - sprintf((char*)(ret.data())+i*2, "%02x", buf[i]); - return ret; -} - -void eth::sha3(bytesConstRef _input, bytesRef _output) -{ - CryptoPP::SHA3_256 ctx; - ctx.Update((byte*)_input.data(), _input.size()); - assert(_output.size() >= 32); - ctx.Final(_output.data()); -} - -bytes eth::sha3Bytes(bytesConstRef _input) -{ - bytes ret(32); - sha3(_input, &ret); - return ret; -} - -h256 eth::sha3(bytesConstRef _input) -{ - h256 ret; - sha3(_input, bytesRef(&ret[0], 32)); - return ret; -} - -Address eth::toAddress(Secret _private) -{ - secp256k1_start(); - - byte pubkey[65]; - int pubkeylen = 65; - int ok = secp256k1_ecdsa_seckey_verify(_private.data()); - if (!ok) - return Address(); - ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, _private.data(), 0); - assert(pubkeylen == 65); - if (!ok) - return Address(); - ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); - if (!ok) - return Address(); - auto ret = right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); -#if ETH_ADDRESS_DEBUG - cout << "---- ADDRESS -------------------------------" << endl; - cout << "SEC: " << _private << endl; - cout << "PUB: " << asHex(bytesConstRef(&(pubkey[1]), 64)) << endl; - cout << "ADR: " << ret << endl; -#endif - return ret; -} - -KeyPair KeyPair::create() -{ - secp256k1_start(); - static std::mt19937_64 s_eng(time(0)); - std::uniform_int_distribution d(0, 255); - - for (int i = 0; i < 100; ++i) - { - h256 sec; - for (unsigned i = 0; i < 32; ++i) - sec[i] = (byte)d(s_eng); - - KeyPair ret(sec); - if (ret.address()) - return ret; - } - return KeyPair(); -} - -KeyPair::KeyPair(h256 _sec): - m_secret(_sec) -{ - int ok = secp256k1_ecdsa_seckey_verify(m_secret.data()); - if (!ok) - return; - - byte pubkey[65]; - int pubkeylen = 65; - ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, m_secret.data(), 0); - if (!ok || pubkeylen != 65) - return; - - ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); - if (!ok) - return; - - m_secret = m_secret; - memcpy(m_public.data(), &(pubkey[1]), 64); - m_address = right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); - -#if ETH_ADDRESS_DEBUG - cout << "---- ADDRESS -------------------------------" << endl; - cout << "SEC: " << m_secret << endl; - cout << "PUB: " << m_public << endl; - cout << "ADR: " << m_address << endl; -#endif -} - -static const vector> g_units = -{ - {((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Uether"}, - {((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Vether"}, - {((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Dether"}, - {(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Nether"}, - {(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Yether"}, - {(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Zether"}, - {((u256(1000000000) * 1000000000) * 1000000000) * 1000000000, "Eether"}, - {((u256(1000000000) * 1000000000) * 1000000000) * 1000000, "Pether"}, - {((u256(1000000000) * 1000000000) * 1000000000) * 1000, "Tether"}, - {(u256(1000000000) * 1000000000) * 1000000000, "Gether"}, - {(u256(1000000000) * 1000000000) * 1000000, "Mether"}, - {(u256(1000000000) * 1000000000) * 1000, "Kether"}, - {u256(1000000000) * 1000000000, "ether"}, - {u256(1000000000) * 1000000, "finney"}, - {u256(1000000000) * 1000, "szabo"}, - {u256(1000000000), "Gwei"}, - {u256(1000000), "Mwei"}, - {u256(1000), "Kwei"}, - {u256(1), "wei"} -}; - -vector> const& eth::units() -{ - return g_units; -} - -std::string eth::formatBalance(u256 _b) -{ - ostringstream ret; - if (_b > g_units[0].first * 10000) - { - ret << (_b / g_units[0].first) << " " << g_units[0].second; - return ret.str(); - } - ret << setprecision(5); - for (auto const& i: g_units) - if (i.first != 1 && _b >= i.first * 100) - { - ret << (double(_b / (i.first / 1000)) / 1000.0) << " " << i.second; - return ret.str(); - } - ret << _b << " wei"; - return ret.str(); -} - -bytes eth::contents(std::string const& _file) -{ - std::ifstream is(_file, std::ifstream::binary); - if (!is) - return bytes(); - // get length of file: - is.seekg (0, is.end); - streamoff length = is.tellg(); - is.seekg (0, is.beg); - bytes ret(length); - is.read((char*)ret.data(), length); - is.close(); - return ret; -} - -void eth::writeFile(std::string const& _file, bytes const& _data) -{ - ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size()); -} - diff --git a/libethereum/Common.h b/libethereum/Common.h index df1d8569e..7d38e2cf7 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -18,7 +18,7 @@ * @author Gav Wood * @date 2014 * - * Shared algorithms and data types. + * Very common stuff (i.e. that every other header needs except vector_ref.h). */ #pragma once @@ -36,22 +36,10 @@ #define noexcept throw() #endif -#include -#include -#include #include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include "vector_ref.h" // CryptoPP defines byte in the global namespace, so so must we. @@ -81,132 +69,8 @@ using u160s = std::vector; using u256Set = std::set; using u160Set = std::set; -template inline void toBigEndian(T _val, Out& o_out); -template inline T fromBigEndian(In const& _bytes); - -/// Convert a series of bytes to the corresponding string of hex duplets. -/// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte. -/// @example asHex("A\x69") == "4169" -template -std::string asHex(_T const& _data, int _w = 2) -{ - std::ostringstream ret; - for (auto i: _data) - ret << std::hex << std::setfill('0') << std::setw(_w) << (int)(typename std::make_unsigned::type)i; - return ret.str(); -} - -/// Converts a (printable) ASCII hex string into the corresponding byte stream. -/// @example fromUserHex("41626261") == asBytes("Abba") -bytes fromUserHex(std::string const& _s); - template class UnitTest {}; -template -class FixedHash -{ - using Arith = boost::multiprecision::number>; - -public: - enum { size = N }; - enum ConstructFromPointerType { ConstructFromPointer }; - - FixedHash() { m_data.fill(0); } - FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); } - explicit FixedHash(bytes const& _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); } - explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } - explicit FixedHash(std::string const& _user): FixedHash(fromUserHex(_user)) {} - - operator Arith() const { return fromBigEndian(m_data); } - - operator bool() const { return ((Arith)*this) != 0; } - - bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } - bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; } - bool operator<(FixedHash const& _c) const { return m_data < _c.m_data; } - - FixedHash& operator^=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; } - FixedHash operator^(FixedHash const& _c) const { return FixedHash(*this) ^= _c; } - FixedHash& operator|=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] |= _c.m_data[i]; return *this; } - FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } - FixedHash& operator&=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } - FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } - FixedHash& operator~() { for (auto i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } - - std::string abridged() const { return asHex(ref().cropped(0, 4)) + ".."; } - - byte& operator[](unsigned _i) { return m_data[_i]; } - byte operator[](unsigned _i) const { return m_data[_i]; } - - bytesRef ref() { return bytesRef(m_data.data(), N); } - bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); } - - byte* data() { return m_data.data(); } - byte const* data() const { return m_data.data(); } - - bytes asBytes() const { return bytes(data(), data() + N); } - std::array& asArray() { return m_data; } - std::array const& asArray() const { return m_data; } - - // generic std::hash compatible function object - struct hash - { - size_t operator()(FixedHash const& value) const - { - size_t h = 0; - for (auto i: value.m_data) - h = (h << 5 - h) + i; - return h; - } - }; - -private: - std::array m_data; -}; - - -// fast equality for h256 -template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const -{ - const uint64_t* hash1 = (const uint64_t*)this->data(); - const uint64_t* hash2 = (const uint64_t*)_other.data(); - return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]); -} - -// fast std::hash compatible hash function object for h256 -template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& value) const -{ - const uint64_t*data = (const uint64_t*)value.data(); - uint64_t hash = data[0]; - hash ^= data[1]; - hash ^= data[2]; - hash ^= data[3]; - return (size_t)hash; -} - -template -inline std::ostream& operator<<(std::ostream& _out, FixedHash const& _h) -{ - _out << std::noshowbase << std::hex << std::setfill('0'); - for (unsigned i = 0; i < N; ++i) - _out << std::setw(2) << (int)_h[i]; - _out << std::dec; - return _out; -} - -using h512 = FixedHash<64>; -using h256 = FixedHash<32>; -using h160 = FixedHash<20>; -using h256s = std::vector; -using h160s = std::vector; -using h256Set = std::set; -using h160Set = std::set; - -using Secret = h256; -using Public = h512; -using Address = h160; -using Addresses = h160s; - // Map types. using StringMap = std::map; using u256Map = std::map; @@ -216,475 +80,4 @@ using HexMap = std::map; static const u256 Invalid256 = ~(u256)0; static const bytes NullBytes; -/// Logging -class NullOutputStream -{ -public: - template NullOutputStream& operator<<(T const&) { return *this; } -}; - -extern std::map g_logOverride; - -struct ThreadLocalLogName -{ - ThreadLocalLogName(std::string _name) { m_name.reset(new std::string(_name)); }; - boost::thread_specific_ptr m_name; -}; - -extern ThreadLocalLogName t_logThreadName; -inline void setThreadName(char const* _n) { t_logThreadName.m_name.reset(new std::string(_n)); } - -struct LogChannel { static const char* name() { return " "; } static const int verbosity = 1; }; -struct LeftChannel: public LogChannel { static const char* name() { return "<<<"; } }; -struct RightChannel: public LogChannel { static const char* name() { return ">>>"; } }; -struct WarnChannel: public LogChannel { static const char* name() { return "!!!"; } static const int verbosity = 0; }; -struct NoteChannel: public LogChannel { static const char* name() { return "***"; } }; -struct DebugChannel: public LogChannel { static const char* name() { return "---"; } static const int verbosity = 0; }; - -extern int g_logVerbosity; -extern std::function g_logPost; - -void simpleDebugOut(std::string const&, char const* ); - -template -class LogOutputStream -{ -public: - LogOutputStream(bool _term = true) - { - std::type_info const* i = &typeid(Id); - auto it = g_logOverride.find(i); - if ((it != g_logOverride.end() && it->second == true) || (it == g_logOverride.end() && Id::verbosity <= g_logVerbosity)) - { - time_t rawTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - char buf[24]; - if (strftime(buf, 24, "%X", localtime(&rawTime)) == 0) - buf[0] = '\0'; // empty if case strftime fails - sstr << Id::name() << " [ " << buf << " | " << *(t_logThreadName.m_name.get()) << (_term ? " ] " : ""); - } - } - ~LogOutputStream() { if (Id::verbosity <= g_logVerbosity) g_logPost(sstr.str(), Id::name()); } - template LogOutputStream& operator<<(T const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && sstr.str().size() && sstr.str().back() != ' ') sstr << " "; sstr << _t; } return *this; } - std::stringstream sstr; -}; - -// Dirties the global namespace, but oh so convenient... -#define cnote eth::LogOutputStream() -#define cwarn eth::LogOutputStream() - -#define ndebug if (true) {} else eth::NullOutputStream() -#define nlog(X) if (true) {} else eth::NullOutputStream() -#define nslog(X) if (true) {} else eth::NullOutputStream() - -#if NDEBUG -#define cdebug ndebug -#else -#define cdebug eth::LogOutputStream() -#endif - -#if NLOG -#define clog(X) nlog(X) -#define cslog(X) nslog(X) -#else -#define clog(X) eth::LogOutputStream() -#define cslog(X) eth::LogOutputStream() -#endif - - - - - - - -/// User-friendly string representation of the amount _b in wei. -std::string formatBalance(u256 _b); - -/// Converts arbitrary value to string representation using std::stringstream. -template -std::string toString(_T const& _t) -{ - std::ostringstream o; - o << _t; - return o.str(); -} - -/// Converts byte array to a string containing the same (binary) data. Unless -/// the byte array happens to contain ASCII data, this won't be printable. -inline std::string asString(bytes const& _b) -{ - return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); -} - -/// Converts a string to a byte array containing the string's (byte) data. -inline bytes asBytes(std::string const& _b) -{ - return bytes((byte const*)_b.data(), (byte const*)(_b.data() + _b.size())); -} - -/// Trims a given number of elements from the front of a collection. -/// Only works for POD element types. -template -void trimFront(_T& _t, uint _elements) -{ - static_assert(std::is_pod::value, ""); - memmove(_t.data(), _t.data() + _elements, (_t.size() - _elements) * sizeof(_t[0])); - _t.resize(_t.size() - _elements); -} - -/// Pushes an element on to the front of a collection. -/// Only works for POD element types. -template -void pushFront(_T& _t, _U _e) -{ - static_assert(std::is_pod::value, ""); - _t.push_back(_e); - memmove(_t.data() + 1, _t.data(), (_t.size() - 1) * sizeof(_e)); - _t[0] = _e; -} - -/// Creates a random, printable, word. -std::string randomWord(); - -/// Escapes a string into the C-string representation. -/// @p _all if true will escape all characters, not just the unprintable ones. -std::string escaped(std::string const& _s, bool _all = true); - -/// Converts a (printable) ASCII hex character into the correspnding integer value. -/// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5 -int fromHex(char _i); - -/// Converts a string into the big-endian base-16 stream of integers (NOT ASCII). -/// @example toHex("A")[0] == 4 && toHex("A")[1] == 1 -bytes toHex(std::string const& _s); - -/// Converts a templated integer value to the big-endian byte-stream represented on a templated collection. -/// The size of the collection object will be unchanged. If it is too small, it will not represent the -/// value properly, if too big then the additional elements will be zeroed out. -/// @a _Out will typically be either std::string or bytes. -/// @a _T will typically by uint, u160, u256 or bigint. -template -inline void toBigEndian(_T _val, _Out& o_out) -{ - for (auto i = o_out.size(); i-- != 0; _val >>= 8) - o_out[i] = (typename _Out::value_type)(uint8_t)_val; -} - -/// Converts a big-endian byte-stream represented on a templated collection to a templated integer value. -/// @a _In will typically be either std::string or bytes. -/// @a _T will typically by uint, u160, u256 or bigint. -template -inline _T fromBigEndian(_In const& _bytes) -{ - _T ret = 0; - for (auto i: _bytes) - ret = (ret << 8) | (byte)(typename std::make_unsigned::type)i; - return ret; -} - -/// Convenience functions for toBigEndian -inline std::string toBigEndianString(u256 _val) { std::string ret(32, '\0'); toBigEndian(_val, ret); return ret; } -inline std::string toBigEndianString(u160 _val) { std::string ret(20, '\0'); toBigEndian(_val, ret); return ret; } -inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } -inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; } - -/// Convenience function for toBigEndian. -/// @returns a string just big enough to represent @a _val. -template -inline std::string toCompactBigEndianString(_T _val) -{ - int i = 0; - for (_T v = _val; v; ++i, v >>= 8) {} - std::string ret(i, '\0'); - toBigEndian(_val, ret); - return ret; -} - -/// Determines the length of the common prefix of the two collections given. -/// @returns the number of elements both @a _t and @a _u share, in order, at the beginning. -/// @example commonPrefix("Hello world!", "Hello, world!") == 5 -template -uint commonPrefix(_T const& _t, _U const& _u) -{ - uint s = std::min(_t.size(), _u.size()); - for (uint i = 0;; ++i) - if (i == s || _t[i] != _u[i]) - return i; - return s; -} - -/// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes. -inline h160 right160(h256 const& _t) -{ - h160 ret; - memcpy(ret.data(), _t.data() + 12, 20); - return ret; -} - -/// Convert the given value into h160 (160-bit unsigned integer) using the left 20 bytes. -inline h160 left160(h256 const& _t) -{ - h160 ret; - memcpy(&ret[0], _t.data(), 20); - return ret; -} - -/// Convert the given value into u160 (160-bit unsigned integer) by taking the lowest order 160-bits and discarding the rest. -inline u160 low160(u256 const& _t) -{ - return (u160)(_t & ((((u256)1) << 160) - 1)); -} - -inline u160 low160(bigint const& _t) -{ - return (u160)(_t & ((((bigint)1) << 160) - 1)); -} - -/// Convert the given value into u160 (160-bit unsigned integer) by taking the lowest order 160-bits and discarding the rest. -inline u160 high160(u256 const& _t) -{ - return (u160)(_t >> 96); -} - - -/// Concatenate two vectors of elements. _T must be POD. -template -inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) -{ - auto s = _a.size(); - _a.resize(_a.size() + _b.size()); - memcpy(_a.data() + s, _b.data(), _b.size() * sizeof(_T)); - return _a; - -} - -/// Concatenate two vectors of elements. _T must be POD. -template -inline std::vector<_T> operator+(std::vector::value, _T>::type> const& _a, std::vector<_T> const& _b) -{ - std::vector<_T> ret(_a); - return ret += _b; -} - -/// SHA-3 convenience routines. -void sha3(bytesConstRef _input, bytesRef _output); -std::string sha3(std::string const& _input, bool _hex); -bytes sha3Bytes(bytesConstRef _input); -inline bytes sha3Bytes(std::string const& _input) { return sha3Bytes((std::string*)&_input); } -inline bytes sha3Bytes(bytes const& _input) { return sha3Bytes((bytes*)&_input); } -h256 sha3(bytesConstRef _input); -inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef((bytes*)&_input)); } -inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); } - -/// Get information concerning the currency denominations. -std::vector> const& units(); - -/// Convert a private key into the public key equivalent. -/// @returns 0 if it's not a valid private key. -Address toAddress(h256 _private); - -class KeyPair -{ -public: - KeyPair() {} - KeyPair(Secret _k); - - static KeyPair create(); - - Secret const& secret() const { return m_secret; } - Secret const& sec() const { return m_secret; } - Public const& pub() const { return m_public; } - - Address const& address() const { return m_address; } - -private: - Secret m_secret; - Public m_public; - Address m_address; -}; - - -static const u256 Uether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; -static const u256 Vether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; -static const u256 Dether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; -static const u256 Nether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; -static const u256 Yether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; -static const u256 Zether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; -static const u256 Eether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000000; -static const u256 Pether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000; -static const u256 Tether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000; -static const u256 Gether = (u256(1000000000) * 1000000000) * 1000000000; -static const u256 Mether = (u256(1000000000) * 1000000000) * 1000000; -static const u256 Kether = (u256(1000000000) * 1000000000) * 1000; -static const u256 ether = u256(1000000000) * 1000000000; -static const u256 finney = u256(1000000000) * 1000000; -static const u256 szabo = u256(1000000000) * 1000; -static const u256 Gwei = u256(1000000000); -static const u256 Mwei = u256(1000000); -static const u256 Kwei = u256(1000); -static const u256 wei = u256(1); - - -// Stream IO - - - -template struct StreamOut { static S& bypass(S& _out, T const& _t) { _out << _t; return _out; } }; -template struct StreamOut { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } }; - -template -inline S& streamout(S& _out, std::vector const& _e) -{ - _out << "["; - if (!_e.empty()) - { - StreamOut::bypass(_out, _e.front()); - for (auto i = ++_e.begin(); i != _e.end(); ++i) - StreamOut::bypass(_out << ",", *i); - } - _out << "]"; - return _out; -} - -template inline std::ostream& operator<<(std::ostream& _out, std::vector const& _e) { streamout(_out, _e); return _out; } - -template -inline S& streamout(S& _out, std::array const& _e) -{ - _out << "["; - if (!_e.empty()) - { - StreamOut::bypass(_out, _e.front()); - auto i = _e.begin(); - for (++i; i != _e.end(); ++i) - StreamOut::bypass(_out << ",", *i); - } - _out << "]"; - return _out; -} -template inline std::ostream& operator<<(std::ostream& _out, std::array const& _e) { streamout(_out, _e); return _out; } - -template -inline S& streamout(S& _out, std::array const& _e) -{ - _out << "["; - if (!_e.empty()) - { - StreamOut::bypass(_out, _e.front()); - auto i = _e.begin(); - for (++i; i != _e.end(); ++i) - StreamOut::bypass(_out << ",", *i); - } - _out << "]"; - return _out; -} -template inline std::ostream& operator<<(std::ostream& _out, std::array const& _e) { streamout(_out, _e); return _out; } - -template -inline S& streamout(S& _out, std::list const& _e) -{ - _out << "["; - if (!_e.empty()) - { - _out << _e.front(); - for (auto i = ++_e.begin(); i != _e.end(); ++i) - _out << "," << *i; - } - _out << "]"; - return _out; -} -template inline std::ostream& operator<<(std::ostream& _out, std::list const& _e) { streamout(_out, _e); return _out; } - -template -inline S& streamout(S& _out, std::pair const& _e) -{ - _out << "(" << _e.first << "," << _e.second << ")"; - return _out; -} -template inline std::ostream& operator<<(std::ostream& _out, std::pair const& _e) { streamout(_out, _e); return _out; } - -template -inline S& streamout(S& _out, std::tuple const& _t) -{ - _out << "(" << std::get<0>(_t) << "," << std::get<1>(_t) << "," << std::get<2>(_t) << ")"; - return _out; -} -template inline std::ostream& operator<<(std::ostream& _out, std::tuple const& _e) { streamout(_out, _e); return _out; } - -template -S& streamout(S& _out, std::map const& _v) -{ - if (_v.empty()) - return _out << "{}"; - int i = 0; - for (auto p: _v) - _out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; - return _out << " }"; -} -template inline std::ostream& operator<<(std::ostream& _out, std::map const& _e) { streamout(_out, _e); return _out; } - -template -S& streamout(S& _out, std::unordered_map const& _v) -{ - if (_v.empty()) - return _out << "{}"; - int i = 0; - for (auto p: _v) - _out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; - return _out << " }"; -} -template inline std::ostream& operator<<(std::ostream& _out, std::unordered_map const& _e) { streamout(_out, _e); return _out; } - -template -S& streamout(S& _out, std::set const& _v) -{ - if (_v.empty()) - return _out << "{}"; - int i = 0; - for (auto p: _v) - _out << (!(i++) ? "{ " : ", ") << p; - return _out << " }"; -} -template inline std::ostream& operator<<(std::ostream& _out, std::set const& _e) { streamout(_out, _e); return _out; } - -template -S& streamout(S& _out, std::multiset const& _v) -{ - if (_v.empty()) - return _out << "{}"; - int i = 0; - for (auto p: _v) - _out << (!(i++) ? "{ " : ", ") << p; - return _out << " }"; -} -template inline std::ostream& operator<<(std::ostream& _out, std::multiset const& _e) { streamout(_out, _e); return _out; } - -template -S& streamout(S& _out, std::multimap const& _v) -{ - if (_v.empty()) - return _out << "{}"; - T l; - int i = 0; - for (auto p: _v) - if (!(i++)) - _out << "{ " << (l = p.first) << " => " << p.second; - else if (l == p.first) - _out << ", " << p.second; - else - _out << "; " << (l = p.first) << " => " << p.second; - return _out << " }"; -} -template inline std::ostream& operator<<(std::ostream& _out, std::multimap const& _e) { streamout(_out, _e); return _out; } - -template _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p) { if (_p) _out << "@" << (*_p); else _out << "nullptr"; return _out; } - -bytes contents(std::string const& _file); -void writeFile(std::string const& _file, bytes const& _data); - -} - -namespace std -{ - // forward std::hash to eth::h256::hash - template<> struct hash: eth::h256::hash {}; } diff --git a/libethereum/CommonData.cpp b/libethereum/CommonData.cpp new file mode 100644 index 000000000..261d110c1 --- /dev/null +++ b/libethereum/CommonData.cpp @@ -0,0 +1,96 @@ +/* + 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 . +*/ +/** @file Common.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "CommonData.h" + +#include +#include "Exceptions.h" +using namespace std; +using namespace eth; + +std::string eth::escaped(std::string const& _s, bool _all) +{ + std::string ret; + ret.reserve(_s.size()); + ret.push_back('"'); + for (auto i: _s) + if (i == '"' && !_all) + ret += "\\\""; + else if (i == '\\' && !_all) + ret += "\\\\"; + else if (i < ' ' || i > 127 || _all) + { + ret += "\\x"; + ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); + ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); + } + else + ret.push_back(i); + ret.push_back('"'); + return ret; +} + +std::string eth::randomWord() +{ + static std::mt19937_64 s_eng(0); + std::string ret(std::uniform_int_distribution(4, 10)(s_eng), ' '); + char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; + std::uniform_int_distribution d(0, sizeof(n) - 2); + for (char& c: ret) + c = n[d(s_eng)]; + return ret; +} + +int eth::fromHex(char _i) +{ + if (_i >= '0' && _i <= '9') + return _i - '0'; + if (_i >= 'a' && _i <= 'f') + return _i - 'a' + 10; + if (_i >= 'A' && _i <= 'F') + return _i - 'A' + 10; + throw BadHexCharacter(); +} + +bytes eth::fromHex(std::string const& _s) +{ + assert(_s.size() % 2 == 0); + if (_s.size() < 2) + return bytes(); + uint s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; + std::vector ret; + ret.reserve((_s.size() - s) / 2); + for (uint i = s; i < _s.size(); i += 2) + ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); + return ret; +} + +bytes eth::asNibbles(std::string const& _s) +{ + std::vector ret; + ret.reserve(_s.size() * 2); + for (auto i: _s) + { + ret.push_back(i / 16); + ret.push_back(i % 16); + } + return ret; +} diff --git a/libethereum/CommonEth.cpp b/libethereum/CommonEth.cpp new file mode 100644 index 000000000..93d484a6a --- /dev/null +++ b/libethereum/CommonEth.cpp @@ -0,0 +1,200 @@ +/* + 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 . +*/ +/** @file CommonEth.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "CommonEth.h" + +#if WIN32 +#pragma warning(push) +#pragma warning(disable:4244) +#else +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +#include +#include +#if WIN32 +#pragma warning(pop) +#else +#endif +#include "Exceptions.h" +using namespace std; +using namespace eth; + +//#define ETH_ADDRESS_DEBUG 1 + +static const vector> g_units = +{ + {((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Uether"}, + {((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Vether"}, + {((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Dether"}, + {(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Nether"}, + {(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Yether"}, + {(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Zether"}, + {((u256(1000000000) * 1000000000) * 1000000000) * 1000000000, "Eether"}, + {((u256(1000000000) * 1000000000) * 1000000000) * 1000000, "Pether"}, + {((u256(1000000000) * 1000000000) * 1000000000) * 1000, "Tether"}, + {(u256(1000000000) * 1000000000) * 1000000000, "Gether"}, + {(u256(1000000000) * 1000000000) * 1000000, "Mether"}, + {(u256(1000000000) * 1000000000) * 1000, "Kether"}, + {u256(1000000000) * 1000000000, "ether"}, + {u256(1000000000) * 1000000, "finney"}, + {u256(1000000000) * 1000, "szabo"}, + {u256(1000000000), "Gwei"}, + {u256(1000000), "Mwei"}, + {u256(1000), "Kwei"}, + {u256(1), "wei"} +}; + +vector> const& eth::units() +{ + return g_units; +} + +std::string eth::formatBalance(u256 _b) +{ + ostringstream ret; + if (_b > g_units[0].first * 10000) + { + ret << (_b / g_units[0].first) << " " << g_units[0].second; + return ret.str(); + } + ret << setprecision(5); + for (auto const& i: g_units) + if (i.first != 1 && _b >= i.first * 100) + { + ret << (double(_b / (i.first / 1000)) / 1000.0) << " " << i.second; + return ret.str(); + } + ret << _b << " wei"; + return ret.str(); +} + +Address eth::toAddress(Secret _private) +{ + secp256k1_start(); + + byte pubkey[65]; + int pubkeylen = 65; + int ok = secp256k1_ecdsa_seckey_verify(_private.data()); + if (!ok) + return Address(); + ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, _private.data(), 0); + assert(pubkeylen == 65); + if (!ok) + return Address(); + ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); + if (!ok) + return Address(); + auto ret = right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); +#if ETH_ADDRESS_DEBUG + cout << "---- ADDRESS -------------------------------" << endl; + cout << "SEC: " << _private << endl; + cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl; + cout << "ADR: " << ret << endl; +#endif + return ret; +} + +KeyPair KeyPair::create() +{ + secp256k1_start(); + static std::mt19937_64 s_eng(time(0)); + std::uniform_int_distribution d(0, 255); + + for (int i = 0; i < 100; ++i) + { + h256 sec; + for (unsigned i = 0; i < 32; ++i) + sec[i] = (byte)d(s_eng); + + KeyPair ret(sec); + if (ret.address()) + return ret; + } + return KeyPair(); +} + +KeyPair::KeyPair(h256 _sec): + m_secret(_sec) +{ + int ok = secp256k1_ecdsa_seckey_verify(m_secret.data()); + if (!ok) + return; + + byte pubkey[65]; + int pubkeylen = 65; + ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, m_secret.data(), 0); + if (!ok || pubkeylen != 65) + return; + + ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); + if (!ok) + return; + + m_secret = m_secret; + memcpy(m_public.data(), &(pubkey[1]), 64); + m_address = right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); + +#if ETH_ADDRESS_DEBUG + cout << "---- ADDRESS -------------------------------" << endl; + cout << "SEC: " << m_secret << endl; + cout << "PUB: " << m_public << endl; + cout << "ADR: " << m_address << endl; +#endif +} + +std::string eth::sha3(std::string const& _input, bool _hex) +{ + if (!_hex) + { + string ret(32, '\0'); + sha3(bytesConstRef((byte const*)_input.data(), _input.size()), bytesRef((byte*)ret.data(), 32)); + return ret; + } + + uint8_t buf[32]; + sha3(bytesConstRef((byte const*)_input.data(), _input.size()), bytesRef((byte*)&(buf[0]), 32)); + std::string ret(64, '\0'); + for (unsigned int i = 0; i < 32; i++) + sprintf((char*)(ret.data())+i*2, "%02x", buf[i]); + return ret; +} + +void eth::sha3(bytesConstRef _input, bytesRef _output) +{ + CryptoPP::SHA3_256 ctx; + ctx.Update((byte*)_input.data(), _input.size()); + assert(_output.size() >= 32); + ctx.Final(_output.data()); +} + +bytes eth::sha3Bytes(bytesConstRef _input) +{ + bytes ret(32); + sha3(_input, &ret); + return ret; +} + +h256 eth::sha3(bytesConstRef _input) +{ + h256 ret; + sha3(_input, bytesRef(&ret[0], 32)); + return ret; +} diff --git a/libethereum/CommonIO.cpp b/libethereum/CommonIO.cpp new file mode 100644 index 000000000..9def88b2a --- /dev/null +++ b/libethereum/CommonIO.cpp @@ -0,0 +1,48 @@ +/* + 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 . +*/ +/** @file CommonIO.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Common.h" + +#include +#include "Exceptions.h" +using namespace std; +using namespace eth; + +bytes eth::contents(std::string const& _file) +{ + std::ifstream is(_file, std::ifstream::binary); + if (!is) + return bytes(); + // get length of file: + is.seekg (0, is.end); + streamoff length = is.tellg(); + is.seekg (0, is.beg); + bytes ret(length); + is.read((char*)ret.data(), length); + is.close(); + return ret; +} + +void eth::writeFile(std::string const& _file, bytes const& _data) +{ + ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size()); +} + diff --git a/libethereum/Dagger.h b/libethereum/Dagger.h index 3f89b293b..ec12ae193 100644 --- a/libethereum/Dagger.h +++ b/libethereum/Dagger.h @@ -23,7 +23,8 @@ #pragma once -#include "Common.h" +#include "FixedHash.h" +#include "CommonEth.h" #define FAKE_DAGGER 1 diff --git a/libethereum/Exceptions.h b/libethereum/Exceptions.h index e55530dfe..e390ec3bb 100644 --- a/libethereum/Exceptions.h +++ b/libethereum/Exceptions.h @@ -1,7 +1,9 @@ #pragma once #include -#include "Common.h" +#include "CommonIO.h" +#include "CommonData.h" +#include "FixedHash.h" namespace eth { @@ -31,13 +33,13 @@ class NoSuchContract: public Exception {}; class ContractAddressCollision: public Exception {}; class FeeTooSmall: public Exception {}; class InvalidSignature: public Exception {}; -class InvalidTransactionFormat: public Exception { public: InvalidTransactionFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid transaction format: Bad field " + toString(m_f) + " (" + asHex(m_d) + ")"; } }; -class InvalidBlockFormat: public Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block format: Bad field " + toString(m_f) + " (" + asHex(m_d) + ")"; } }; -class InvalidBlockHeaderFormat: public Exception { public: InvalidBlockHeaderFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block header format: Bad field " + toString(m_f) + " (" + asHex(m_d) + ")"; } }; +class InvalidTransactionFormat: public Exception { public: InvalidTransactionFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid transaction format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; +class InvalidBlockFormat: public Exception { public: InvalidBlockFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; +class InvalidBlockHeaderFormat: public Exception { public: InvalidBlockHeaderFormat(int _f, bytesConstRef _d): m_f(_f), m_d(_d.toBytes()) {} int m_f; bytes m_d; virtual std::string description() const { return "Invalid block header format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"; } }; class InvalidUnclesHash: public Exception {}; class InvalidUncle: public Exception {}; class InvalidStateRoot: public Exception {}; -class InvalidTransactionsHash: public Exception { public: InvalidTransactionsHash(h256 _head, h256 _real): m_head(_head), m_real(_real) {} h256 m_head; h256 m_real; virtual std::string description() const { return "Invalid transactions hash: header says: " + asHex(m_head.ref()) + " block is:" + asHex(m_real.ref()); } }; +class InvalidTransactionsHash: public Exception { public: InvalidTransactionsHash(h256 _head, h256 _real): m_head(_head), m_real(_real) {} h256 m_head; h256 m_real; virtual std::string description() const { return "Invalid transactions hash: header says: " + toHex(m_head.ref()) + " block is:" + toHex(m_real.ref()); } }; class InvalidTransaction: public Exception {}; class InvalidDifficulty: public Exception {}; class InvalidTimestamp: public Exception {}; diff --git a/libethereum/FixedHash.cpp b/libethereum/FixedHash.cpp new file mode 100644 index 000000000..9d16cacb2 --- /dev/null +++ b/libethereum/FixedHash.cpp @@ -0,0 +1,25 @@ +/* + 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 . +*/ +/** @file FixedHash.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "FixedHash.h" + +using namespace std; +using namespace eth; diff --git a/libethereum/Instruction.cpp b/libethereum/Instruction.cpp index 660747cfe..9e412ee3f 100644 --- a/libethereum/Instruction.cpp +++ b/libethereum/Instruction.cpp @@ -22,7 +22,8 @@ #include "Instruction.h" #include -#include "Common.h" +#include "CommonEth.h" +#include "Log.h" using namespace std; using namespace eth; diff --git a/libethereum/Log.cpp b/libethereum/Log.cpp new file mode 100644 index 000000000..0ead148f0 --- /dev/null +++ b/libethereum/Log.cpp @@ -0,0 +1,41 @@ +/* + 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 . +*/ +/** @file Log.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Log.h" + +#include +#include +using namespace std; +using namespace eth; + +// Logging +int eth::g_logVerbosity = 8; +map eth::g_logOverride; + +ThreadLocalLogName eth::t_logThreadName("main"); + +void eth::simpleDebugOut(std::string const& _s, char const*) +{ + cout << _s << endl << flush; +} + +std::function eth::g_logPost = simpleDebugOut; + diff --git a/libethereum/MemTrie.cpp b/libethereum/MemTrie.cpp index 7d2f9f54c..c9d257c05 100644 --- a/libethereum/MemTrie.cpp +++ b/libethereum/MemTrie.cpp @@ -19,7 +19,7 @@ * @date 2014 */ -#include "Common.h" +#include "CommonEth.h" #include "TrieCommon.h" #include "MemTrie.h" using namespace std; @@ -147,7 +147,7 @@ public: assert(m_value.size()); std::cerr << _indent; if (m_ext.size()) - std::cerr << asHex(m_ext, 1) << ": "; + std::cerr << toHex(m_ext, 1) << ": "; else std::cerr << "@: "; std::cerr << m_value << std::endl; @@ -174,7 +174,7 @@ public: #if ENABLE_DEBUG_PRINT virtual void debugPrintBody(std::string const& _indent) const { - std::cerr << _indent << asHex(m_ext, 1) << ": "; + std::cerr << _indent << toHex(m_ext, 1) << ": "; m_next->debugPrint(_indent + " "); } #endif @@ -454,7 +454,7 @@ std::string const& MemTrie::at(std::string const& _key) const { if (!m_root) return c_nullString; - auto h = toHex(_key); + auto h = asNibbles(_key); return m_root->at(bytesConstRef(&h)); } @@ -462,7 +462,7 @@ void MemTrie::insert(std::string const& _key, std::string const& _value) { if (_value.empty()) remove(_key); - auto h = toHex(_key); + auto h = asNibbles(_key); m_root = m_root ? m_root->insert(&h, _value) : new TrieLeafNode(bytesConstRef(&h), _value); } @@ -470,7 +470,7 @@ void MemTrie::remove(std::string const& _key) { if (m_root) { - auto h = toHex(_key); + auto h = asNibbles(_key); m_root = m_root->remove(&h); } } diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index 955a4c86e..8f5dc1463 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -28,6 +28,7 @@ #include #include #include "Common.h" +#include "Log.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; diff --git a/libethereum/PeerServer.cpp b/libethereum/PeerServer.cpp index 408835ca3..f4f630f86 100644 --- a/libethereum/PeerServer.cpp +++ b/libethereum/PeerServer.cpp @@ -68,7 +68,7 @@ PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, populateAddresses(); determinePublic(_publicAddress, _upnp); ensureAccepting(); - clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); + clog(NetNote) << "Id:" << toHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); } PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m): @@ -82,7 +82,7 @@ PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId, NodeM { // populate addresses. populateAddresses(); - clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); + clog(NetNote) << "Id:" << toHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); } PeerServer::~PeerServer() diff --git a/libethereum/PeerSession.cpp b/libethereum/PeerSession.cpp index 0c8699829..0bd7f53a9 100644 --- a/libethereum/PeerSession.cpp +++ b/libethereum/PeerSession.cpp @@ -163,7 +163,7 @@ bool PeerSession::interpret(RLP const& _r) s << PeersPacket; for (auto i: peers) { - clogS(NetMessageDetail) << "Sending peer " << asHex(i.first.ref().cropped(0, 4)) << i.second; + clogS(NetMessageDetail) << "Sending peer " << toHex(i.first.ref().cropped(0, 4)) << i.second; s.appendList(3) << i.second.address().to_v4().to_bytes() << i.second.port() << i.first; } sealAndSend(s); @@ -179,7 +179,7 @@ bool PeerSession::interpret(RLP const& _r) if (isPrivateAddress(peerAddress)) goto CONTINUE; - clogS(NetAllDetail) << "Checking: " << ep << "(" << asHex(id.ref().cropped(0, 4)) << ")"; + clogS(NetAllDetail) << "Checking: " << ep << "(" << toHex(id.ref().cropped(0, 4)) << ")"; // check that it's not us or one we already know: if (id && (m_server->m_key.pub() == id || m_server->m_peers.count(id) || m_server->m_incomingPeers.count(id))) @@ -545,11 +545,11 @@ void PeerSession::doRead() break; // enough has come in. -// cerr << "Received " << len << ": " << asHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; +// cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; auto data = bytesConstRef(m_incoming.data(), tlen); if (!checkPacket(data)) { - cerr << "Received " << len << ": " << asHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; + cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; cwarn << "INVALID MESSAGE RECEIVED"; disconnect(BadProtocol); return; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index c06453c6f..8a88a0a98 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -42,10 +42,10 @@ std::map const& eth::genesisState() if (s_ret.empty()) { // Initialise. - s_ret[Address(fromUserHex("8a40bfaa73256b60764c1bf40675a99083efb075"))] = 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); + s_ret[Address(fromHex("8a40bfaa73256b60764c1bf40675a99083efb075"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); + s_ret[Address(fromHex("e6716f9544a56c530d868e4bfbacb172315bdead"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); + s_ret[Address(fromHex("1e12515ce3e0f817a4ddef9ca55788a1d66bd2df"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); + s_ret[Address(fromHex("1a26338f0d905e295fccb71fa9ea849ffa12aaf4"))] = AddressState(u256(1) << 200, 0, AddressType::Normal); } return s_ret; } diff --git a/libethereum/State.h b/libethereum/State.h index e3670fe98..be53c2b9c 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -322,7 +322,7 @@ inline std::ostream& operator<<(std::ostream& _out, State const& _s) std::map mem; for (auto const& j: memdb) { - _out << std::endl << " [" << j.first << ":" << asHex(j.second) << "]"; + _out << std::endl << " [" << j.first << ":" << toHex(j.second) << "]"; #ifdef __clang__ auto mFinder = mem.find(j.first); if (mFinder == mem.end()) @@ -359,7 +359,7 @@ inline std::ostream& operator<<(std::ostream& _out, State const& _s) std::map mem; for (auto const& j: memdb) { - _out << std::endl << " [" << j.first << ":" << asHex(j.second) << "]"; + _out << std::endl << " [" << j.first << ":" << toHex(j.second) << "]"; #ifdef __clang__ auto mFinder = mem.find(j.first); if (mFinder == mem.end()) diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index e751815af..f7c5431d3 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -79,7 +79,7 @@ Address Transaction::sender() const cout << "---- RECOVER -------------------------------" << endl; cout << "MSG: " << msg << endl; cout << "R S V: " << sig[0] << " " << sig[1] << " " << (int)(vrs.v - 27) << "+27" << endl; - cout << "PUB: " << asHex(bytesConstRef(&(pubkey[1]), 64)) << endl; + cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl; cout << "ADR: " << ret << endl; #endif return ret; diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index aca8a125c..ea034ac9f 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -21,7 +21,7 @@ #pragma once -#include "Common.h" +#include "CommonEth.h" #include "RLP.h" namespace eth diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 55d225273..3d50e069f 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -19,6 +19,7 @@ * @date 2014 */ +#include "Log.h" #include "Transaction.h" #include "TransactionQueue.h" using namespace std; diff --git a/libethereum/TrieDB.h b/libethereum/TrieDB.h index 36fede58b..fbd0b238c 100644 --- a/libethereum/TrieDB.h +++ b/libethereum/TrieDB.h @@ -25,6 +25,8 @@ #include #include #include "Exceptions.h" +#include "CommonEth.h" +#include "Log.h" #include "TrieCommon.h" namespace ldb = leveldb; @@ -54,7 +56,7 @@ inline std::ostream& operator<<(std::ostream& _out, BasicMap const& _m) { _out << i.first << ": "; _out << RLP(i.second); - _out << " " << asHex(i.second); + _out << " " << toHex(i.second); _out << std::endl; } return _out; @@ -177,7 +179,7 @@ public: } if (!(rlp.isList() && (rlp.itemCount() == 2 || rlp.itemCount() == 17))) { - cdebug << b.rlp.size() << asHex(b.rlp); + cdebug << b.rlp.size() << toHex(b.rlp); cdebug << rlp; auto c = rlp.itemCount(); cdebug << c; diff --git a/libethereum/TrieHash.cpp b/libethereum/TrieHash.cpp index bbe5c1fe7..b4e3a7eb4 100644 --- a/libethereum/TrieHash.cpp +++ b/libethereum/TrieHash.cpp @@ -19,7 +19,7 @@ * @date 2014 */ -#include "Common.h" +#include "CommonEth.h" #include "TrieCommon.h" #include "TrieHash.h" using namespace std; @@ -58,7 +58,7 @@ void hash256rlp(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i _rlp.appendList(2) << hexPrefixEncode(_begin->first, true, _preLen) << _begin->second; #if ENABLE_DEBUG_PRINT if (g_hashDebug) - std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, _begin->first.size() - _preLen), 1) << ": " << _begin->second << " = " << sha3(_rlp.out()) << std::endl; + std::cerr << s_indent << toHex(bytesConstRef(_begin->first.data() + _preLen, _begin->first.size() - _preLen), 1) << ": " << _begin->second << " = " << sha3(_rlp.out()) << std::endl; #endif } else @@ -79,7 +79,7 @@ void hash256rlp(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i // if they all have the same next nibble, we also want a pair. #if ENABLE_DEBUG_PRINT if (g_hashDebug) - std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, sharedPre), 1) << ": " << std::endl; + std::cerr << s_indent << toHex(bytesConstRef(_begin->first.data() + _preLen, sharedPre), 1) << ": " << std::endl; #endif _rlp.appendList(2) << hexPrefixEncode(_begin->first, false, _preLen, (int)sharedPre); hash256aux(_s, _begin, _end, (unsigned)sharedPre, _rlp); @@ -162,7 +162,7 @@ h256 hash256(StringMap const& _s) return h256(); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[toHex(i->first)] = i->second; + hexMap[asNibbles(i->first)] = i->second; RLPStream s; hash256rlp(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); return sha3(s.out()); @@ -175,7 +175,7 @@ bytes rlp256(StringMap const& _s) return bytes(); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[toHex(i->first)] = i->second; + hexMap[asNibbles(i->first)] = i->second; RLPStream s; hash256aux(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); return s.out(); @@ -188,7 +188,7 @@ h256 hash256(u256Map const& _s) return h256(); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[toHex(toBigEndianString(i->first))] = asString(rlp(i->second)); + hexMap[asNibbles(toBigEndianString(i->first))] = asString(rlp(i->second)); RLPStream s; hash256rlp(hexMap, hexMap.cbegin(), hexMap.cend(), 0, s); return sha3(s.out()); diff --git a/libethereum/TrieHash.h b/libethereum/TrieHash.h index bf3b557f2..dcdcfd86a 100644 --- a/libethereum/TrieHash.h +++ b/libethereum/TrieHash.h @@ -22,6 +22,7 @@ #pragma once #include "Common.h" +#include "FixedHash.h" namespace eth { diff --git a/libethereum/UPnP.cpp b/libethereum/UPnP.cpp index 6008bc7de..bcf769c0a 100644 --- a/libethereum/UPnP.cpp +++ b/libethereum/UPnP.cpp @@ -26,6 +26,7 @@ #include #include #include "Common.h" +#include "Log.h" #include "Exceptions.h" #include "UPnP.h" using namespace std; diff --git a/test/crypto.cpp b/test/crypto.cpp index 8a57e1d57..93d4d2571 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include using namespace std; using namespace eth; @@ -33,21 +34,21 @@ int cryptoTest() cnote << "Testing Crypto..."; secp256k1_start(); - KeyPair p(Secret(fromUserHex("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4"))); - assert(p.pub() == Public(fromUserHex("97466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f"))); - assert(p.address() == Address(fromUserHex("8a40bfaa73256b60764c1bf40675a99083efb075"))); + KeyPair p(Secret(fromHex("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4"))); + assert(p.pub() == Public(fromHex("97466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f"))); + assert(p.address() == Address(fromHex("8a40bfaa73256b60764c1bf40675a99083efb075"))); { Transaction t; t.nonce = 0; - t.receiveAddress = h160(fromUserHex("944400f4b88ac9589a0f17ed4671da26bddb668b")); + t.receiveAddress = h160(fromHex("944400f4b88ac9589a0f17ed4671da26bddb668b")); t.value = 1000; t.data = u256s(); cnote << RLP(t.rlp(false)); - cnote << asHex(t.rlp(false)); + cnote << toHex(t.rlp(false)); cnote << t.sha3(false); t.sign(p.secret()); cnote << RLP(t.rlp(true)); - cnote << asHex(t.rlp(true)); + cnote << toHex(t.rlp(true)); cnote << t.sha3(true); assert(t.sender() == p.address()); } @@ -55,7 +56,7 @@ int cryptoTest() #if 0 // Test transaction. - bytes tx = fromUserHex("88005401010101010101010101010101010101010101011f0de0b6b3a76400001ce8d4a5100080181c373130a009ba1f10285d4e659568bfcfec85067855c5a3c150100815dad4ef98fd37cf0593828c89db94bd6c64e210a32ef8956eaa81ea9307194996a3b879441f5d"); + bytes tx = fromHex("88005401010101010101010101010101010101010101011f0de0b6b3a76400001ce8d4a5100080181c373130a009ba1f10285d4e659568bfcfec85067855c5a3c150100815dad4ef98fd37cf0593828c89db94bd6c64e210a32ef8956eaa81ea9307194996a3b879441f5d"); cout << "TX: " << RLP(tx) << endl; Transaction t2(tx); @@ -69,13 +70,13 @@ int cryptoTest() t.receiveAddress = toAddress(sha3("123")); bytes sig64 = toBigEndian(t.vrs.r) + toBigEndian(t.vrs.s); - cout << "SIG: " << sig64.size() << " " << asHex(sig64) << " " << t.vrs.v << endl; + cout << "SIG: " << sig64.size() << " " << toHex(sig64) << " " << t.vrs.v << endl; auto msg = t.rlp(false); cout << "TX w/o SIG: " << RLP(msg) << endl; - cout << "RLP(TX w/o SIG): " << asHex(t.rlpString(false)) << endl; + cout << "RLP(TX w/o SIG): " << toHex(t.rlpString(false)) << endl; std::string hmsg = sha3(t.rlpString(false), false); - cout << "SHA256(RLP(TX w/o SIG)): 0x" << asHex(hmsg) << endl; + cout << "SHA256(RLP(TX w/o SIG)): 0x" << toHex(hmsg) << endl; bytes privkey = sha3Bytes("123"); @@ -84,12 +85,12 @@ int cryptoTest() int pubkeylen = 65; int ret = secp256k1_ecdsa_seckey_verify(privkey.data()); - cout << "SEC: " << dec << ret << " " << asHex(privkey) << endl; + cout << "SEC: " << dec << ret << " " << toHex(privkey) << endl; ret = secp256k1_ecdsa_pubkey_create(pubkey.data(), &pubkeylen, privkey.data(), 1); pubkey.resize(pubkeylen); int good = secp256k1_ecdsa_pubkey_verify(pubkey.data(), (int)pubkey.size()); - cout << "PUB: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << (good ? " GOOD" : " BAD") << endl; + cout << "PUB: " << dec << ret << " " << pubkeylen << " " << toHex(pubkey) << (good ? " GOOD" : " BAD") << endl; } // Test roundtrip... @@ -97,17 +98,17 @@ int cryptoTest() bytes sig(64); u256 nonce = 0; int v = 0; - cout << asHex(hmsg) << endl; - cout << asHex(privkey) << endl; + cout << toHex(hmsg) << endl; + cout << toHex(privkey) << endl; cout << hex << nonce << dec << endl; int ret = secp256k1_ecdsa_sign_compact((byte const*)hmsg.data(), (int)hmsg.size(), sig.data(), privkey.data(), (byte const*)&nonce, &v); - cout << "MYSIG: " << dec << ret << " " << sig.size() << " " << asHex(sig) << " " << v << endl; + cout << "MYSIG: " << dec << ret << " " << sig.size() << " " << toHex(sig) << " " << v << endl; bytes pubkey(65); int pubkeylen = 65; ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), (int)hmsg.size(), (byte const*)sig.data(), pubkey.data(), &pubkeylen, 0, v); pubkey.resize(pubkeylen); - cout << "MYREC: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << endl; + cout << "MYREC: " << dec << ret << " " << pubkeylen << " " << toHex(pubkey) << endl; } { @@ -115,8 +116,8 @@ int cryptoTest() int pubkeylen = 65; int ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), (int)hmsg.size(), (byte const*)sig64.data(), pubkey.data(), &pubkeylen, 0, (int)t.vrs.v - 27); pubkey.resize(pubkeylen); - cout << "RECPUB: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << endl; - cout << "SENDER: " << hex << low160(eth::sha3(bytesConstRef(&pubkey).cropped(1))) << dec << endl; + cout << "RECPUB: " << dec << ret << " " << pubkeylen << " " << toHex(pubkey) << endl; + cout << "SENDER: " << hex << toAddress(eth::sha3(bytesConstRef(&pubkey).cropped(1))) << dec << endl; } #endif return 0; diff --git a/test/dagger.cpp b/test/dagger.cpp index 8f7df77d3..728c8382a 100644 --- a/test/dagger.cpp +++ b/test/dagger.cpp @@ -21,6 +21,7 @@ */ #include +#include "Log.h" #include "Dagger.h" using namespace std; using namespace std::chrono; diff --git a/test/hexPrefix.cpp b/test/hexPrefix.cpp index b8bc1f48c..62f4d4916 100644 --- a/test/hexPrefix.cpp +++ b/test/hexPrefix.cpp @@ -24,6 +24,7 @@ #include "../json_spirit/json_spirit_reader_template.h" #include "../json_spirit/json_spirit_writer_template.h" #include "TrieCommon.h" +#include "Log.h" using namespace std; using namespace eth; namespace js = json_spirit; @@ -48,11 +49,11 @@ public: for (auto& i: o["seq"].get_array()) v.push_back((byte)i.get_int()); auto e = hexPrefixEncode(v, o["term"].get_bool()); - if (!o["out"].is_null() && o["out"].get_str() != asHex(e)) + if (!o["out"].is_null() && o["out"].get_str() != toHex(e)) { cwarn << "Test failed."; cwarn << "Test says:" << o["out"].get_str(); - cwarn << "Impl says:" << asHex(e); + cwarn << "Impl says:" << toHex(e); passed = false; } } diff --git a/test/main.cpp b/test/main.cpp index 311a43701..c5d750204 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -39,7 +39,7 @@ int main(int, char**) /* RLPStream s; BlockInfo::genesis().fillStream(s, false); std::cout << RLP(s.out()) << std::endl; - std::cout << asHex(s.out()) << std::endl; + std::cout << toHex(s.out()) << std::endl; std::cout << sha3(s.out()) << std::endl;*/ int r = 0; diff --git a/test/rlp.cpp b/test/rlp.cpp index bc8fb6031..f82af521f 100644 --- a/test/rlp.cpp +++ b/test/rlp.cpp @@ -23,6 +23,7 @@ #include #include "../json_spirit/json_spirit_reader_template.h" #include "../json_spirit/json_spirit_writer_template.h" +#include #include using namespace std; using namespace eth; @@ -67,11 +68,11 @@ public: cnote << i.first; RLPStream s; buildRLP(o["in"], s); - if (!o["out"].is_null() && o["out"].get_str() != asHex(s.out())) + if (!o["out"].is_null() && o["out"].get_str() != toHex(s.out())) { cwarn << "Test failed."; cwarn << "Test says:" << o["out"].get_str(); - cwarn << "Impl says:" << asHex(s.out()); + cwarn << "Impl says:" << toHex(s.out()); passed = false; } } diff --git a/test/trie.cpp b/test/trie.cpp index 3092238ce..419602d62 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -61,11 +61,11 @@ public: t.init(); for (auto const& k: ss) t.insert(k.first, k.second); - if (!o["root"].is_null() && o["root"].get_str() != asHex(t.root().asArray())) + if (!o["root"].is_null() && o["root"].get_str() != toHex(t.root().asArray())) { cwarn << "Test failed on permutation " << j; cwarn << "Test says:" << o["root"].get_str(); - cwarn << "Impl says:" << asHex(t.root().asArray()); + cwarn << "Impl says:" << toHex(t.root().asArray()); passed = false; } } @@ -154,7 +154,7 @@ int trieTest() t.insert("doe", "reindeer"); cout << hex << t.hash256() << endl; cout << RLP(t.rlp()) << endl; - cout << asHex(t.rlp()) << endl; + cout << toHex(t.rlp()) << endl; } { BasicMap m; diff --git a/test/vm.cpp b/test/vm.cpp index 55299f906..03893525c 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include using namespace std; using namespace json_spirit; From adc4061d9cbf36f79c1afd8d27d2d21236ecca05 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 4 Mar 2014 12:15:05 -0600 Subject: [PATCH 22/28] Document FixedHash. --- libethereum/Common.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libethereum/Common.h b/libethereum/Common.h index 7d38e2cf7..ec5e85b26 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -45,6 +45,7 @@ // CryptoPP defines byte in the global namespace, so so must we. using byte = uint8_t; +// Quote a given token stream to turn it into a string. #define ETH_QUOTED_HELPER(s) #s #define ETH_QUOTED(s) ETH_QUOTED_HELPER(s) @@ -69,8 +70,6 @@ using u160s = std::vector; using u256Set = std::set; using u160Set = std::set; -template class UnitTest {}; - // Map types. using StringMap = std::map; using u256Map = std::map; @@ -80,4 +79,7 @@ using HexMap = std::map; static const u256 Invalid256 = ~(u256)0; static const bytes NullBytes; +/// Trivial UnitTest type that everyone can agree on, mainly to allow befriending for test classes & their code. +template class UnitTest {}; + } From 4cccb079f0b2623a5a76b0b4731a28125dbd1ffd Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 4 Mar 2014 12:18:56 -0600 Subject: [PATCH 23/28] Documentation for Instruction.h. --- libethereum/Instruction.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libethereum/Instruction.h b/libethereum/Instruction.h index 1c17e61a6..cc37d0e79 100644 --- a/libethereum/Instruction.h +++ b/libethereum/Instruction.h @@ -84,14 +84,16 @@ enum class Instruction: uint8_t SUICIDE = 0x3f }; +/// Information structure for a particular instruction. struct InstructionInfo { - std::string name; - int additional; - int args; - int ret; + std::string name; ///< The name of the instruction. + int additional; ///< Additional items required in memory for this instructions (only for PUSH). + int args; ///< Number of items required on the stack for this instruction (and, for the purposes of ret, the number taken from the stack). + int ret; ///< Number of items placed (back) on the stack by this instruction, assuming args items were removed. }; +/// Information on all the instructions. static const std::map c_instructionInfo = { { Instruction::STOP, { "STOP", 0, 0, 0 } }, @@ -147,6 +149,7 @@ static const std::map c_instructionInfo = { Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} } }; +/// Convert from string mnemonic to Instruction type. static const std::map c_instructions = { { "STOP", Instruction::STOP }, @@ -202,8 +205,13 @@ static const std::map c_instructions = { "SUICIDE", Instruction::SUICIDE } }; +/// Convert from simple EVM assembly language to EVM code. u256s assemble(std::string const& _code, bool _quiet = false); + +/// Convert from EVM code to simple EVM assembly language. std::string disassemble(u256s const& _mem); + +/// Compile a Low-level Lisp-like Language program into EVM-code. u256s compileLisp(std::string const& _code, bool _quiet = false); } From 736557ca4c614d215365e02b8eafdf60418b1444 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 4 Mar 2014 12:32:20 -0600 Subject: [PATCH 24/28] Various headers from the repotting. Log has documentation. --- libethereum/CommonData.h | 186 +++++++++++++++++++++++++++++++ libethereum/CommonEth.h | 137 +++++++++++++++++++++++ libethereum/CommonIO.h | 223 ++++++++++++++++++++++++++++++++++++++ libethereum/FixedHash.h | 195 +++++++++++++++++++++++++++++++++ libethereum/Log.h | 134 +++++++++++++++++++++++ libethereum/PeerServer.h | 133 +++++++++++++++++++++++ libethereum/PeerSession.h | 90 +++++++++++++++ 7 files changed, 1098 insertions(+) create mode 100644 libethereum/CommonData.h create mode 100644 libethereum/CommonEth.h create mode 100644 libethereum/CommonIO.h create mode 100644 libethereum/FixedHash.h create mode 100644 libethereum/Log.h create mode 100644 libethereum/PeerServer.h create mode 100644 libethereum/PeerSession.h diff --git a/libethereum/CommonData.h b/libethereum/CommonData.h new file mode 100644 index 000000000..ca1fad1a6 --- /dev/null +++ b/libethereum/CommonData.h @@ -0,0 +1,186 @@ +/* + 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 . +*/ +/** @file Common.h + * @author Gav Wood + * @date 2014 + * + * Shared algorithms and data types. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "Common.h" + +namespace eth +{ + +// String conversion functions, mainly to/from hex/nibble/byte representations. + +/// Convert a series of bytes to the corresponding string of hex duplets. +/// @param _w specifies the width of each of the elements. Defaults to two - enough to represent a byte. +/// @example toHex("A\x69") == "4169" +template +std::string toHex(_T const& _data, int _w = 2) +{ + std::ostringstream ret; + for (auto i: _data) + ret << std::hex << std::setfill('0') << std::setw(_w) << (int)(typename std::make_unsigned::type)i; + return ret.str(); +} + +/// Converts a (printable) ASCII hex character into the correspnding integer value. +/// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5 +int fromHex(char _i); + +/// Converts a (printable) ASCII hex string into the corresponding byte stream. +/// @example fromHex("41626261") == asBytes("Abba") +bytes fromHex(std::string const& _s); + +/// Converts byte array to a string containing the same (binary) data. Unless +/// the byte array happens to contain ASCII data, this won't be printable. +inline std::string asString(bytes const& _b) +{ + return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); +} + +/// Converts a string to a byte array containing the string's (byte) data. +inline bytes asBytes(std::string const& _b) +{ + return bytes((byte const*)_b.data(), (byte const*)(_b.data() + _b.size())); +} + +/// Converts a string into the big-endian base-16 stream of integers (NOT ASCII). +/// @example asNibbles("A")[0] == 4 && asNibbles("A")[1] == 1 +bytes asNibbles(std::string const& _s); + + +// Big-endian to/from host endian conversion functions. + +/// Converts a templated integer value to the big-endian byte-stream represented on a templated collection. +/// The size of the collection object will be unchanged. If it is too small, it will not represent the +/// value properly, if too big then the additional elements will be zeroed out. +/// @a _Out will typically be either std::string or bytes. +/// @a _T will typically by uint, u160, u256 or bigint. +template +inline void toBigEndian(_T _val, _Out& o_out) +{ + for (auto i = o_out.size(); i-- != 0; _val >>= 8) + o_out[i] = (typename _Out::value_type)(uint8_t)_val; +} + +/// Converts a big-endian byte-stream represented on a templated collection to a templated integer value. +/// @a _In will typically be either std::string or bytes. +/// @a _T will typically by uint, u160, u256 or bigint. +template +inline _T fromBigEndian(_In const& _bytes) +{ + _T ret = 0; + for (auto i: _bytes) + ret = (ret << 8) | (byte)(typename std::make_unsigned::type)i; + return ret; +} + +/// Convenience functions for toBigEndian +inline std::string toBigEndianString(u256 _val) { std::string ret(32, '\0'); toBigEndian(_val, ret); return ret; } +inline std::string toBigEndianString(u160 _val) { std::string ret(20, '\0'); toBigEndian(_val, ret); return ret; } +inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } +inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; } + +/// Convenience function for toBigEndian. +/// @returns a string just big enough to represent @a _val. +template +inline std::string toCompactBigEndianString(_T _val) +{ + int i = 0; + for (_T v = _val; v; ++i, v >>= 8) {} + std::string ret(i, '\0'); + toBigEndian(_val, ret); + return ret; +} + + +// Algorithms for string and string-like collections. + +/// Escapes a string into the C-string representation. +/// @p _all if true will escape all characters, not just the unprintable ones. +std::string escaped(std::string const& _s, bool _all = true); + +/// Determines the length of the common prefix of the two collections given. +/// @returns the number of elements both @a _t and @a _u share, in order, at the beginning. +/// @example commonPrefix("Hello world!", "Hello, world!") == 5 +template +uint commonPrefix(_T const& _t, _U const& _u) +{ + uint s = std::min(_t.size(), _u.size()); + for (uint i = 0;; ++i) + if (i == s || _t[i] != _u[i]) + return i; + return s; +} + +/// Creates a random, printable, word. +std::string randomWord(); + + +// General datatype convenience functions. + +/// Trims a given number of elements from the front of a collection. +/// Only works for POD element types. +template +void trimFront(_T& _t, uint _elements) +{ + static_assert(std::is_pod::value, ""); + memmove(_t.data(), _t.data() + _elements, (_t.size() - _elements) * sizeof(_t[0])); + _t.resize(_t.size() - _elements); +} + +/// Pushes an element on to the front of a collection. +/// Only works for POD element types. +template +void pushFront(_T& _t, _U _e) +{ + static_assert(std::is_pod::value, ""); + _t.push_back(_e); + memmove(_t.data() + 1, _t.data(), (_t.size() - 1) * sizeof(_e)); + _t[0] = _e; +} + +/// Concatenate two vectors of elements. _T must be POD. +template +inline std::vector<_T>& operator+=(std::vector::value, _T>::type>& _a, std::vector<_T> const& _b) +{ + auto s = _a.size(); + _a.resize(_a.size() + _b.size()); + memcpy(_a.data() + s, _b.data(), _b.size() * sizeof(_T)); + return _a; + +} + +/// Concatenate two vectors of elements. _T must be POD. +template +inline std::vector<_T> operator+(std::vector::value, _T>::type> const& _a, std::vector<_T> const& _b) +{ + std::vector<_T> ret(_a); + return ret += _b; +} + +} diff --git a/libethereum/CommonEth.h b/libethereum/CommonEth.h new file mode 100644 index 000000000..1605f00f7 --- /dev/null +++ b/libethereum/CommonEth.h @@ -0,0 +1,137 @@ +/* + 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 . +*/ +/** @file CommonEth.h + * @author Gav Wood + * @date 2014 + * + * Ethereum-specific data structures & algorithms. + */ + +#pragma once + +#include "Common.h" +#include "FixedHash.h" + +namespace eth +{ + +/// A secret key: 32 bytes. +/// @NOTE This is not endian-specific; it's just a bunch of bytes. +using Secret = h256; + +/// A public key: 64 bytes. +/// @NOTE This is not endian-specific; it's just a bunch of bytes. +using Public = h512; + +/// An Ethereum address: 20 bytes. +/// @NOTE This is not endian-specific; it's just a bunch of bytes. +using Address = h160; + +/// A vector of Ethereum addresses. +using Addresses = h160s; + +/// User-friendly string representation of the amount _b in wei. +std::string formatBalance(u256 _b); + +/// Get information concerning the currency denominations. +std::vector> const& units(); + +// The various denominations; here for ease of use where needed within code. +static const u256 Uether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; +static const u256 Vether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; +static const u256 Dether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; +static const u256 Nether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; +static const u256 Yether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; +static const u256 Zether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; +static const u256 Eether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000000; +static const u256 Pether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000000; +static const u256 Tether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000; +static const u256 Gether = (u256(1000000000) * 1000000000) * 1000000000; +static const u256 Mether = (u256(1000000000) * 1000000000) * 1000000; +static const u256 Kether = (u256(1000000000) * 1000000000) * 1000; +static const u256 ether = u256(1000000000) * 1000000000; +static const u256 finney = u256(1000000000) * 1000000; +static const u256 szabo = u256(1000000000) * 1000; +static const u256 Gwei = u256(1000000000); +static const u256 Mwei = u256(1000000); +static const u256 Kwei = u256(1000); +static const u256 wei = u256(1); + +/// Convert a private key into the public key equivalent. +/// @returns 0 if it's not a valid private key. +Address toAddress(h256 _private); + +/// Simple class that represents a "key pair". +/// All of the data of the class can be regenerated from the secret key (m_secret) alone. +/// Actually stores a tuplet of secret, public and address (the right 160-bits of the public). +class KeyPair +{ +public: + /// Null constructor. + KeyPair() {} + + /// Normal constructor - populates object from the given secret key. + KeyPair(Secret _k); + + /// Create a new, randomly generated object. + static KeyPair create(); + + /// Retrieve the secret key. + Secret const& secret() const { return m_secret; } + /// Retrieve the secret key. + Secret const& sec() const { return m_secret; } + + /// Retrieve the public key. + Public const& pub() const { return m_public; } + + /// Retrieve the associated address of the public key. + Address const& address() const { return m_address; } + +private: + Secret m_secret; + Public m_public; + Address m_address; +}; + + +// SHA-3 convenience routines. + +/// Calculate SHA3-256 hash of the given input and load it into the given output. +void sha3(bytesConstRef _input, bytesRef _output); + +/// Calculate SHA3-256 hash of the given input, possibly interpreting it as nibbles, and return the hash as a string filled with binary data. +std::string sha3(std::string const& _input, bool _isNibbles); + +/// Calculate SHA3-256 hash of the given input, returning as a byte array. +bytes sha3Bytes(bytesConstRef _input); + +/// Calculate SHA3-256 hash of the given input (presented as a binary string), returning as a byte array. +inline bytes sha3Bytes(std::string const& _input) { return sha3Bytes((std::string*)&_input); } + +/// Calculate SHA3-256 hash of the given input, returning as a byte array. +inline bytes sha3Bytes(bytes const& _input) { return sha3Bytes((bytes*)&_input); } + +/// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. +h256 sha3(bytesConstRef _input); + +/// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. +inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef((bytes*)&_input)); } + +/// Calculate SHA3-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash. +inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); } + +} diff --git a/libethereum/CommonIO.h b/libethereum/CommonIO.h new file mode 100644 index 000000000..a388fa8f9 --- /dev/null +++ b/libethereum/CommonIO.h @@ -0,0 +1,223 @@ +/* + 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 . +*/ +/** @file CommonIO.h + * @author Gav Wood + * @date 2014 + * + * File & stream I/O routines. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Common.h" + +namespace eth +{ + +/// Retrieve and returns the contents of the given file. If the file doesn't exist or isn't readable, returns an empty bytes. +bytes contents(std::string const& _file); + +/// Write the given binary data into the given file, replacing the file if it pre-exists. +void writeFile(std::string const& _file, bytes const& _data); + +/// Converts arbitrary value to string representation using std::stringstream. +template +std::string toString(_T const& _t) +{ + std::ostringstream o; + o << _t; + return o.str(); +} + +// Stream I/O functions. +// Provides templated stream I/O for all STL collections so they can be shifted on to any iostream-like interface. + +template struct StreamOut { static S& bypass(S& _out, T const& _t) { _out << _t; return _out; } }; +template struct StreamOut { static S& bypass(S& _out, uint8_t const& _t) { _out << (int)_t; return _out; } }; + +template +inline S& streamout(S& _out, std::vector const& _e) +{ + _out << "["; + if (!_e.empty()) + { + StreamOut::bypass(_out, _e.front()); + for (auto i = ++_e.begin(); i != _e.end(); ++i) + StreamOut::bypass(_out << ",", *i); + } + _out << "]"; + return _out; +} + +template inline std::ostream& operator<<(std::ostream& _out, std::vector const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::array const& _e) +{ + _out << "["; + if (!_e.empty()) + { + StreamOut::bypass(_out, _e.front()); + auto i = _e.begin(); + for (++i; i != _e.end(); ++i) + StreamOut::bypass(_out << ",", *i); + } + _out << "]"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::array const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::array const& _e) +{ + _out << "["; + if (!_e.empty()) + { + StreamOut::bypass(_out, _e.front()); + auto i = _e.begin(); + for (++i; i != _e.end(); ++i) + StreamOut::bypass(_out << ",", *i); + } + _out << "]"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::array const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::list const& _e) +{ + _out << "["; + if (!_e.empty()) + { + _out << _e.front(); + for (auto i = ++_e.begin(); i != _e.end(); ++i) + _out << "," << *i; + } + _out << "]"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::list const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::pair const& _e) +{ + _out << "(" << _e.first << "," << _e.second << ")"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::pair const& _e) { streamout(_out, _e); return _out; } + +template +inline S& streamout(S& _out, std::tuple const& _t) +{ + _out << "(" << std::get<0>(_t) << "," << std::get<1>(_t) << "," << std::get<2>(_t) << ")"; + return _out; +} +template inline std::ostream& operator<<(std::ostream& _out, std::tuple const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::map const& _v) +{ + if (_v.empty()) + return _out << "{}"; + int i = 0; + for (auto p: _v) + _out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::map const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::unordered_map const& _v) +{ + if (_v.empty()) + return _out << "{}"; + int i = 0; + for (auto p: _v) + _out << (!(i++) ? "{ " : "; ") << p.first << " => " << p.second; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::unordered_map const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::set const& _v) +{ + if (_v.empty()) + return _out << "{}"; + int i = 0; + for (auto p: _v) + _out << (!(i++) ? "{ " : ", ") << p; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::set const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::unordered_set const& _v) +{ + if (_v.empty()) + return _out << "{}"; + int i = 0; + for (auto p: _v) + _out << (!(i++) ? "{ " : ", ") << p; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::unordered_set const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::multiset const& _v) +{ + if (_v.empty()) + return _out << "{}"; + int i = 0; + for (auto p: _v) + _out << (!(i++) ? "{ " : ", ") << p; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::multiset const& _e) { streamout(_out, _e); return _out; } + +template +S& streamout(S& _out, std::multimap const& _v) +{ + if (_v.empty()) + return _out << "{}"; + T l; + int i = 0; + for (auto p: _v) + if (!(i++)) + _out << "{ " << (l = p.first) << " => " << p.second; + else if (l == p.first) + _out << ", " << p.second; + else + _out << "; " << (l = p.first) << " => " << p.second; + return _out << " }"; +} +template inline std::ostream& operator<<(std::ostream& _out, std::multimap const& _e) { streamout(_out, _e); return _out; } + +template _S& operator<<(_S& _out, std::shared_ptr<_T> const& _p) { if (_p) _out << "@" << (*_p); else _out << "nullptr"; return _out; } + +} diff --git a/libethereum/FixedHash.h b/libethereum/FixedHash.h new file mode 100644 index 000000000..6fcf0ea2b --- /dev/null +++ b/libethereum/FixedHash.h @@ -0,0 +1,195 @@ +/* + 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 . +*/ +/** @file FixedHash.h + * @author Gav Wood + * @date 2014 + * + * The FixedHash fixed-size "hash" container type. + */ + +#pragma once + +#include +#include +#include "CommonData.h" + +namespace eth +{ + +/// Fixed-size raw-byte array container type, with an API optimised for storing hashes. +/// Transparently converts to/from the corresponding arithmetic type; this will +/// assume the data contained in the hash is big-endian. +template +class FixedHash +{ + /// The corresponding arithmetic type. + using Arith = boost::multiprecision::number>; + +public: + /// The size of the container. + enum { size = N }; + + /// A dummy flag to avoid accidental construction from pointer. + enum ConstructFromPointerType { ConstructFromPointer }; + + /// Method to convert from a string. + enum ConstructFromStringType { FromHex, FromBinary }; + + /// Construct an empty hash. + FixedHash() { m_data.fill(0); } + + /// Convert from the corresponding arithmetic type. + FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); } + + /// Explicitly construct, copying from a byte array. + explicit FixedHash(bytes const& _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min(_b.size(), N)); } + + /// Explicitly construct, copying from a bytes in memory with given pointer. + explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } + + /// Explicitly construct, copying from a string. + explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex): FixedHash(_t == FromHex ? fromHex(_s) : asBytes(_s)) {} + + /// Convert to arithmetic type. + operator Arith() const { return fromBigEndian(m_data); } + + /// @returns true iff this is the empty hash. + operator bool() const { return ((Arith)*this) != 0; } + + // The obvious comparison operators. + bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } + bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; } + bool operator<(FixedHash const& _c) const { return m_data < _c.m_data; } + + // The obvious binary operators. + FixedHash& operator^=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; } + FixedHash operator^(FixedHash const& _c) const { return FixedHash(*this) ^= _c; } + FixedHash& operator|=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] |= _c.m_data[i]; return *this; } + FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } + FixedHash& operator&=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } + FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } + FixedHash& operator~() { for (auto i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } + + /// @returns a particular byte from the hash. + byte& operator[](unsigned _i) { return m_data[_i]; } + /// @returns a particular byte from the hash. + byte operator[](unsigned _i) const { return m_data[_i]; } + + /// @returns an abridged version of the hash as a user-readable hex string. + std::string abridged() const { return toHex(ref().cropped(0, 4)) + ".."; } + + /// @returns a mutable byte vector_ref to the object's data. + bytesRef ref() { return bytesRef(m_data.data(), N); } + + /// @returns a constant byte vector_ref to the object's data. + bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); } + + /// @returns a mutable byte pointer to the object's data. + byte* data() { return m_data.data(); } + + /// @returns a constant byte pointer to the object's data. + byte const* data() const { return m_data.data(); } + + /// @returns a copy of the object's data as a byte vector. + bytes asBytes() const { return bytes(data(), data() + N); } + + /// @returns a mutable reference to the object's data as an STL array. + std::array& asArray() { return m_data; } + + /// @returns a constant reference to the object's data as an STL array. + std::array const& asArray() const { return m_data; } + + /// A generic std::hash compatible function object. + struct hash + { + /// Make a hash of the object's data. + size_t operator()(FixedHash const& value) const + { + size_t h = 0; + for (auto i: value.m_data) + h = (h << 5 - h) + i; + return h; + } + }; + +private: + std::array m_data; ///< The binary data. +}; + + +/// Fast equality operator for h256. +template<> inline bool FixedHash<32>::operator==(FixedHash<32> const& _other) const +{ + const uint64_t* hash1 = (const uint64_t*)this->data(); + const uint64_t* hash2 = (const uint64_t*)_other.data(); + return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && (hash1[3] == hash2[3]); +} + +/// Fast std::hash compatible hash function object for h256. +template<> inline size_t FixedHash<32>::hash::operator()(FixedHash<32> const& value) const +{ + const uint64_t*data = (const uint64_t*)value.data(); + uint64_t hash = data[0]; + hash ^= data[1]; + hash ^= data[2]; + hash ^= data[3]; + return (size_t)hash; +} + +/// Stream I/O for the FixedHash class. +template +inline std::ostream& operator<<(std::ostream& _out, FixedHash const& _h) +{ + _out << std::noshowbase << std::hex << std::setfill('0'); + for (unsigned i = 0; i < N; ++i) + _out << std::setw(2) << (int)_h[i]; + _out << std::dec; + return _out; +} + +// Common types of FixedHash. +using h512 = FixedHash<64>; +using h256 = FixedHash<32>; +using h160 = FixedHash<20>; +using h256s = std::vector; +using h160s = std::vector; +using h256Set = std::set; +using h160Set = std::set; + +/// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes. +inline h160 right160(h256 const& _t) +{ + h160 ret; + memcpy(ret.data(), _t.data() + 12, 20); + return ret; +} + +/// Convert the given value into h160 (160-bit unsigned integer) using the left 20 bytes. +inline h160 left160(h256 const& _t) +{ + h160 ret; + memcpy(&ret[0], _t.data(), 20); + return ret; +} + +} + +namespace std +{ + /// Forward std::hash to eth::h256::hash. + template<> struct hash: eth::h256::hash {}; +} diff --git a/libethereum/Log.h b/libethereum/Log.h new file mode 100644 index 000000000..52684fae1 --- /dev/null +++ b/libethereum/Log.h @@ -0,0 +1,134 @@ +/* + 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 . +*/ +/** @file Log.h + * @author Gav Wood + * @date 2014 + * + * The logging subsystem. + */ + +#pragma once + +#include +#include +#include +#include "vector_ref.h" + +namespace eth +{ + +/// The null output stream. Used when logging is disabled. +class NullOutputStream +{ +public: + template NullOutputStream& operator<<(T const&) { return *this; } +}; + +/// A simple log-output function that prints log messages to stdout. +void simpleDebugOut(std::string const&, char const* ); + +/// The logging system's current verbosity. +extern int g_logVerbosity; + +/// The current method that the logging system uses to output the log messages. Defaults to simpleDebugOut(). +extern std::function g_logPost; + +/// Map of Log Channel types to bool, false forces the channel to be disabled, true forces it to be enabled. +/// If a channel has no entry, then it will output as long as its verbosity (LogChannel::verbosity) is less than +/// or equal to the currently output verbosity (g_logVerbosity). +extern std::map g_logOverride; + +/// Associate a name with each thread for nice logging. +struct ThreadLocalLogName +{ + ThreadLocalLogName(std::string _name) { m_name.reset(new std::string(_name)); }; + boost::thread_specific_ptr m_name; +}; + +/// The current thread's name. +extern ThreadLocalLogName t_logThreadName; + +/// Set the current thread's log name. +inline void setThreadName(char const* _n) { t_logThreadName.m_name.reset(new std::string(_n)); } + +/// The default logging channels. Each has an associated verbosity and three-letter prefix (name() ). +/// Channels should inherit from LogChannel and define name() and verbosity. +struct LogChannel { static const char* name() { return " "; } static const int verbosity = 1; }; +struct LeftChannel: public LogChannel { static const char* name() { return "<<<"; } }; +struct RightChannel: public LogChannel { static const char* name() { return ">>>"; } }; +struct WarnChannel: public LogChannel { static const char* name() { return "!!!"; } static const int verbosity = 0; }; +struct NoteChannel: public LogChannel { static const char* name() { return "***"; } }; +struct DebugChannel: public LogChannel { static const char* name() { return "---"; } static const int verbosity = 0; }; + +/// Logging class, iostream-like, that can be shifted to. +template +class LogOutputStream +{ +public: + /// Construct a new object. + /// If _term is true the the prefix info is terminated with a ']' character; if not it ends only with a '|' character. + LogOutputStream(bool _term = true) + { + std::type_info const* i = &typeid(Id); + auto it = g_logOverride.find(i); + if ((it != g_logOverride.end() && it->second == true) || (it == g_logOverride.end() && Id::verbosity <= g_logVerbosity)) + { + time_t rawTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + char buf[24]; + if (strftime(buf, 24, "%X", localtime(&rawTime)) == 0) + buf[0] = '\0'; // empty if case strftime fails + m_sstr << Id::name() << " [ " << buf << " | " << *(t_logThreadName.m_name.get()) << (_term ? " ] " : ""); + } + } + + /// Destructor. Posts the accrued log entry to the g_logPost function. + ~LogOutputStream() { if (Id::verbosity <= g_logVerbosity) g_logPost(m_sstr.str(), Id::name()); } + + /// Shift arbitrary data to the log. Spaces will be added between items as required. + template LogOutputStream& operator<<(T const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && m_sstr.str().size() && m_sstr.str().back() != ' ') m_sstr << " "; m_sstr << _t; } return *this; } + +private: + std::stringstream m_sstr; ///< The accrued log entry. +}; + +// Simple cout-like stream objects for accessing common log channels. +// Dirties the global namespace, but oh so convenient... +#define cnote eth::LogOutputStream() +#define cwarn eth::LogOutputStream() + +// Null stream-like objects. +#define ndebug if (true) {} else eth::NullOutputStream() +#define nlog(X) if (true) {} else eth::NullOutputStream() +#define nslog(X) if (true) {} else eth::NullOutputStream() + +// Kill debugging log channel when we're in release mode. +#if NDEBUG +#define cdebug ndebug +#else +#define cdebug eth::LogOutputStream() +#endif + +// Kill all logs when when NLOG is defined. +#if NLOG +#define clog(X) nlog(X) +#define cslog(X) nslog(X) +#else +#define clog(X) eth::LogOutputStream() +#define cslog(X) eth::LogOutputStream() +#endif + +} diff --git a/libethereum/PeerServer.h b/libethereum/PeerServer.h new file mode 100644 index 000000000..98214feb7 --- /dev/null +++ b/libethereum/PeerServer.h @@ -0,0 +1,133 @@ +/* + 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 . +*/ +/** @file PeerServer.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "PeerNetwork.h" +#include "CommonEth.h" +namespace ba = boost::asio; +namespace bi = boost::asio::ip; + +namespace eth +{ + +class PeerServer +{ + friend class PeerSession; + +public: + /// Start server, listening for connections on the given port. + PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, unsigned short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); + /// Start server, but don't listen. + PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m = NodeMode::Full); + ~PeerServer(); + + static unsigned protocolVersion(); + unsigned networkId() { return m_networkId; } + + /// Connect to a peer explicitly. + void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; + void connect(bi::tcp::endpoint const& _ep); + + /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. + bool sync(BlockChain& _bc, TransactionQueue&, Overlay& _o); + bool sync(); + + /// Conduct I/O, polling, syncing, whatever. + /// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway. + /// This won't touch alter the blockchain. + void process() { if (isInitialised()) m_ioService.poll(); } + + /// Set ideal number of peers. + void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } + + void setMode(NodeMode _m) { m_mode = _m; } + + /// Get peer information. + std::vector peers() const; + + /// Get number of peers connected; equivalent to, but faster than, peers().size(). + size_t peerCount() const { return m_peers.size(); } + + /// Ping the peers, to update the latency information. + void pingAll(); + + /// Get the port we're listening on currently. + unsigned short listenPort() const { return m_public.port(); } + + bytes savePeers() const; + void restorePeers(bytesConstRef _b); + +private: + void seal(bytes& _b); + void populateAddresses(); + void determinePublic(std::string const& _publicAddress, bool _upnp); + void ensureAccepting(); + + /// Check to see if the network peer-state initialisation has happened. + bool isInitialised() const { return m_latestBlockSent; } + /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. + bool ensureInitialised(BlockChain& _bc, TransactionQueue& _tq); + + std::map potentialPeers(); + + std::string m_clientVersion; + NodeMode m_mode = NodeMode::Full; + + unsigned short m_listenPort; + + BlockChain const* m_chain = nullptr; + ba::io_service m_ioService; + bi::tcp::acceptor m_acceptor; + bi::tcp::socket m_socket; + + UPnP* m_upnp = nullptr; + bi::tcp::endpoint m_public; + KeyPair m_key; + + unsigned m_networkId; + std::map> m_peers; + + std::vector m_incomingTransactions; + std::vector m_incomingBlocks; + std::vector m_unknownParentBlocks; + std::vector m_freePeers; + std::map> m_incomingPeers; + + h256 m_latestBlockSent; + std::set m_transactionsSent; + + std::chrono::steady_clock::time_point m_lastPeersRequest; + unsigned m_idealPeerCount = 5; + + std::vector m_addresses; + std::vector m_peerAddresses; + + bool m_accepting = false; +}; + +} diff --git a/libethereum/PeerSession.h b/libethereum/PeerSession.h new file mode 100644 index 000000000..0621f4f92 --- /dev/null +++ b/libethereum/PeerSession.h @@ -0,0 +1,90 @@ +/* + 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 . +*/ +/** @file PeerSession.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include "RLP.h" +#include "CommonEth.h" +#include "PeerNetwork.h" + +namespace eth +{ + +class PeerSession: public std::enable_shared_from_this +{ + friend class PeerServer; + +public: + PeerSession(PeerServer* _server, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, unsigned short _peerPort = 0); + ~PeerSession(); + + void start(); + void disconnect(int _reason); + + void ping(); + + bool isOpen() const { return m_socket.is_open(); } + + bi::tcp::endpoint endpoint() const; ///< for other peers to connect to. + +private: + void dropped(); + void doRead(); + void doWrite(std::size_t length); + bool interpret(RLP const& _r); + + /// @returns true iff the _msg forms a valid message for sending or receiving on the network. + static bool checkPacket(bytesConstRef _msg); + + static RLPStream& prep(RLPStream& _s); + void sealAndSend(RLPStream& _s); + void sendDestroy(bytes& _msg); + void send(bytesConstRef _msg); + PeerServer* m_server; + + bi::tcp::socket m_socket; + std::array m_data; + PeerInfo m_info; + Public m_id; + + bytes m_incoming; + uint m_protocolVersion; + uint m_networkId; + uint m_reqNetworkId; + unsigned short m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers. + uint m_caps; + + std::chrono::steady_clock::time_point m_ping; + std::chrono::steady_clock::time_point m_connect; + std::chrono::steady_clock::time_point m_disconnect; + + uint m_rating; + bool m_requireTransactions; + + std::set m_knownBlocks; + std::set m_knownTransactions; +}; + +} From 82612a70271dfc3c7a00bd9cc6acfafa4fff1f65 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 4 Mar 2014 12:43:27 -0600 Subject: [PATCH 25/28] Move additional Trie implementations to tests where they belong. --- .gitignore | 3 --- libethereum/FixedHash.h | 2 +- {libethereum => test}/MemTrie.cpp | 5 +++-- {libethereum => test}/MemTrie.h | 3 ++- {libethereum => test}/TrieHash.cpp | 5 +++-- {libethereum => test}/TrieHash.h | 4 ++-- test/trie.cpp | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) rename {libethereum => test}/MemTrie.cpp (99%) rename {libethereum => test}/MemTrie.h (96%) rename {libethereum => test}/TrieHash.cpp (99%) rename {libethereum => test}/TrieHash.h (95%) diff --git a/.gitignore b/.gitignore index bcdbfa12d..90c2da58c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,9 +19,6 @@ ipch *.opensdf *.suo -# Generated headers -*.h - *.user *.user.* *~ diff --git a/libethereum/FixedHash.h b/libethereum/FixedHash.h index 6fcf0ea2b..a72b45e9f 100644 --- a/libethereum/FixedHash.h +++ b/libethereum/FixedHash.h @@ -62,7 +62,7 @@ public: explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); } /// Explicitly construct, copying from a string. - explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex): FixedHash(_t == FromHex ? fromHex(_s) : asBytes(_s)) {} + explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex): FixedHash(_t == FromHex ? fromHex(_s) : eth::asBytes(_s)) {} /// Convert to arithmetic type. operator Arith() const { return fromBigEndian(m_data); } diff --git a/libethereum/MemTrie.cpp b/test/MemTrie.cpp similarity index 99% rename from libethereum/MemTrie.cpp rename to test/MemTrie.cpp index c9d257c05..0a7d0ebe4 100644 --- a/libethereum/MemTrie.cpp +++ b/test/MemTrie.cpp @@ -19,9 +19,10 @@ * @date 2014 */ -#include "CommonEth.h" -#include "TrieCommon.h" #include "MemTrie.h" + +#include +#include using namespace std; using namespace eth; diff --git a/libethereum/MemTrie.h b/test/MemTrie.h similarity index 96% rename from libethereum/MemTrie.h rename to test/MemTrie.h index d956c1d8a..622ea531d 100644 --- a/libethereum/MemTrie.h +++ b/test/MemTrie.h @@ -21,7 +21,8 @@ #pragma once -#include "Common.h" +#include +#include namespace eth { diff --git a/libethereum/TrieHash.cpp b/test/TrieHash.cpp similarity index 99% rename from libethereum/TrieHash.cpp rename to test/TrieHash.cpp index b4e3a7eb4..61840b0e8 100644 --- a/libethereum/TrieHash.cpp +++ b/test/TrieHash.cpp @@ -19,9 +19,10 @@ * @date 2014 */ -#include "CommonEth.h" -#include "TrieCommon.h" #include "TrieHash.h" + +#include +#include using namespace std; using namespace eth; diff --git a/libethereum/TrieHash.h b/test/TrieHash.h similarity index 95% rename from libethereum/TrieHash.h rename to test/TrieHash.h index dcdcfd86a..e69b2b7b3 100644 --- a/libethereum/TrieHash.h +++ b/test/TrieHash.h @@ -21,8 +21,8 @@ #pragma once -#include "Common.h" -#include "FixedHash.h" +#include +#include namespace eth { diff --git a/test/trie.cpp b/test/trie.cpp index 419602d62..9d2bb3763 100644 --- a/test/trie.cpp +++ b/test/trie.cpp @@ -24,9 +24,9 @@ #include "../json_spirit/json_spirit_reader_template.h" #include "../json_spirit/json_spirit_writer_template.h" #include -#include #include -#include +#include "TrieHash.h" +#include "MemTrie.h" using namespace std; using namespace eth; From dff8e9ef46a07091389e87ad3928605f0128333c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 5 Mar 2014 10:52:07 -0600 Subject: [PATCH 26/28] Stop on div by zero rather than throw. --- libethereum/VM.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libethereum/VM.h b/libethereum/VM.h index caafc64b0..2f7d1fcaf 100644 --- a/libethereum/VM.h +++ b/libethereum/VM.h @@ -162,21 +162,29 @@ template void eth::VM::go(Ext& _ext, uint64_t _steps) break; case Instruction::DIV: require(2); + if (!m_stack[m_stack.size() - 2]) + return; m_stack[m_stack.size() - 2] = m_stack.back() / m_stack[m_stack.size() - 2]; m_stack.pop_back(); break; case Instruction::SDIV: require(2); + if (!m_stack[m_stack.size() - 2]) + return; (s256&)m_stack[m_stack.size() - 2] = (s256&)m_stack.back() / (s256&)m_stack[m_stack.size() - 2]; m_stack.pop_back(); break; case Instruction::MOD: require(2); + if (!m_stack[m_stack.size() - 2]) + return; m_stack[m_stack.size() - 2] = m_stack.back() % m_stack[m_stack.size() - 2]; m_stack.pop_back(); break; case Instruction::SMOD: require(2); + if (!m_stack[m_stack.size() - 2]) + return; (s256&)m_stack[m_stack.size() - 2] = (s256&)m_stack.back() % (s256&)m_stack[m_stack.size() - 2]; m_stack.pop_back(); break; From 764c5975771df24bf294c180fdb4ecefbc5a6423 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 5 Mar 2014 11:11:36 -0600 Subject: [PATCH 27/28] Change README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 80b31c7b8..d01e25450 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Contributors, builders and testers include Eric Lombrozo (cross-compilation), Ti ### Building -See https://github.com/ethereum/cpp-ethereum/wiki/Building and https://github.com/ethereum/cpp-ethereum/wiki/Compatibility-Info-and-Build-Tips . +See https://github.com/ethereum/cpp-ethereum/wiki/Build-Instructions and https://github.com/ethereum/cpp-ethereum/wiki/Compatibility-Info-and-Build-Tips . ### Testing @@ -16,7 +16,7 @@ To run the tests, make sure you clone the tests repository from github.com/ether ### Yet To Do -See TODO +See https://github.com/ethereum/cpp-ethereum/wiki/TODO ### License From 440800a4faff9aafde0b68886e33f22ee3d758ff Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 5 Mar 2014 14:05:09 -0600 Subject: [PATCH 28/28] Fix killExisting bug. --- libethereum/FeeStructure.h | 8 +++++--- libethereum/State.cpp | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/libethereum/FeeStructure.h b/libethereum/FeeStructure.h index 56b8e9c0b..b9bd0cd78 100644 --- a/libethereum/FeeStructure.h +++ b/libethereum/FeeStructure.h @@ -26,11 +26,13 @@ namespace eth { +/** + * The collection of fee amounts that makes up the fee structure. + */ struct FeeStructure { - /// The fee structure. Values yet to be agreed on... - void setMultiplier(u256 _x); ///< The current block multiplier. - u256 multiplier() const; + void setMultiplier(u256 _x); ///< Set the current block multiplier. + u256 multiplier() const; ///< @returns the current block multiplier. u256 m_stepFee; u256 m_dataFee; u256 m_memoryFee; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 8a88a0a98..4e710cb1d 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -56,6 +56,9 @@ Overlay State::openDB(std::string _path, bool _killExisting) _path = Defaults::get()->m_dbPath; boost::filesystem::create_directory(_path); + if (_killExisting) + boost::filesystem::remove_all(_path + "/state"); + ldb::Options o; o.create_if_missing = true; ldb::DB* db = nullptr;