From 2c492f53f8fd7c4596e354c5e9731395e4d1bb91 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 3 Jan 2014 17:25:46 +0000 Subject: [PATCH] VM nearly instruction-complete. --- CMakeLists.txt | 2 + README.md | 8 +- libethereum/CMakeLists.txt | 8 +- libethereum/Common.h | 1 + libethereum/VirtualMachine.cpp | 129 +++++++++++++++++++++++++++++---- libethereum/VirtualMachine.h | 37 +++++++++- libethereum/rmd160.h | 2 + test/CMakeLists.txt | 3 +- test/main.cpp | 2 +- 9 files changed, 169 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1cd458fc..6e585fc42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ project(ethereum) cmake_minimum_required(VERSION 2.8) +cmake_policy(SET CMP0015 NEW) + # Initialize CXXFLAGS. set(CMAKE_CXX_FLAGS "-Wall -std=c++11") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") diff --git a/README.md b/README.md index a3d8289f7..89e9b5169 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -ethereum -======== +# ethereum Ethereum C++ Client. + +## Dependencies + +secp256k1 implementation: https://github.com/sipa/secp256k1.git +Expects secp256k1 directory to be in same path as cpp-ethereum. diff --git a/libethereum/CMakeLists.txt b/libethereum/CMakeLists.txt index c2d80b5a1..7a9bb2e61 100644 --- a/libethereum/CMakeLists.txt +++ b/libethereum/CMakeLists.txt @@ -1,2 +1,8 @@ +cmake_policy(SET CMP0015 NEW) + +include_directories(../../secp256k1/include) +link_directories(../../secp256k1) aux_source_directory(. SRC_LIST) -add_library(libethereum ${SRC_LIST}) +add_library(ethereum ${SRC_LIST}) + +target_link_libraries(ethereum secp256k1) diff --git a/libethereum/Common.h b/libethereum/Common.h index 4155815a7..a1e00cc05 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -20,6 +20,7 @@ using u256 = boost::multiprecision::number>; using uint = uint64_t; using sint = int64_t; +using u256s = std::vector; template std::string toString(_T const& _t) { std::ostringstream o; o << _t; return o.str(); } diff --git a/libethereum/VirtualMachine.cpp b/libethereum/VirtualMachine.cpp index 9e2a0a107..32bca9955 100644 --- a/libethereum/VirtualMachine.cpp +++ b/libethereum/VirtualMachine.cpp @@ -1,12 +1,9 @@ #include "sha256.h" +#include #include "VirtualMachine.h" using namespace std; using namespace eth; -VirtualMachine::VirtualMachine() -{ -} - VirtualMachine::~VirtualMachine() { } @@ -14,20 +11,28 @@ VirtualMachine::~VirtualMachine() void VirtualMachine::go() { - bool stopped = false; - while (!stopped) + u256 curPC = 0; + u256 nextPC = 1; + auto& memory = m_state->memory(m_myAddress); + + auto require = [&](u256 _n) + { + if (m_stack.size() < _n) + throw StackTooSmall(_n, m_stack.size()); + }; + auto mem = [&](u256 _n) { - auto rawInst = m_memory[m_pc]; + auto i = memory.find(_n); + return i == memory.end() ? 0 : i->second; + }; + + for (bool stopped = false; !stopped; curPC = nextPC, nextPC = curPC + 1) + { + auto rawInst = mem(curPC); if (rawInst > 0xff) throw BadInstruction(); Instruction inst = (Instruction)(uint8_t)rawInst; - auto require = [&](uint _n) - { - if (m_stack.size() < _n) - throw StackTooSmall(_n, m_stack.size()); - }; - switch (inst) { case Instruction::ADD: @@ -165,8 +170,8 @@ void VirtualMachine::go() if (inst == Instruction::SHA256) m_stack.back() = sha256(b); else + // NOTE: this aligns to right of 256-bit container (low-order bytes). This won't work if they're treated as byte-arrays and thus left-aligned in a 256-bit container. m_stack.back() = ripemd160(&b); - break; } case Instruction::ECMUL: @@ -174,21 +179,117 @@ void VirtualMachine::go() case Instruction::ECSIGN: case Instruction::ECRECOVER: case Instruction::ECVALID: + // TODO + break; case Instruction::PUSH: + { + m_stack.push_back(mem(curPC + 1)); + nextPC = curPC + 2; + break; + } case Instruction::POP: + require(1); + m_stack.pop_back(); + break; case Instruction::DUP: + require(1); + m_stack.push_back(m_stack.back()); + break; case Instruction::DUPN: + { + auto s = mem(curPC + 1); + if (s == 0 || s > m_stack.size()) + throw OperandOutOfRange(1, m_stack.size(), s); + m_stack.push_back(m_stack[m_stack.size() - (uint)s]); + nextPC = curPC + 2; + break; + } case Instruction::SWAP: + { + require(2); + auto d = m_stack.back(); + m_stack.back() = m_stack[m_stack.size() - 2]; + m_stack[m_stack.size() - 2] = d; + break; + } case Instruction::SWAPN: + { + require(1); + auto d = m_stack.back(); + auto s = mem(curPC + 1); + if (s == 0 || s > m_stack.size()) + throw OperandOutOfRange(1, m_stack.size(), s); + m_stack.back() = m_stack[m_stack.size() - (uint)s]; + m_stack[m_stack.size() - (uint)s] = d; + nextPC = curPC + 2; + break; + } case Instruction::LOAD: + require(1); + m_stack.back() = mem(m_stack.back()); + break; case Instruction::STORE: + require(2); + mem(m_stack.back()) = m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + m_stack.pop_back(); + break; case Instruction::JMP: + require(1); + nextPC = m_stack.back(); + m_stack.pop_back(); + break; case Instruction::JMPI: + require(2); + if (m_stack.back()) + nextPC = m_stack[m_stack.size() - 2]; + m_stack.pop_back(); + m_stack.pop_back(); + break; case Instruction::IND: + m_stack.push_back(curPC); + break; case Instruction::EXTRO: + { + require(2); + auto memoryAddress = m_stack.back(); + m_stack.pop_back(); + auto contractAddress = m_stack.back(); + m_stack.back() = m_state->memory(contractAddress, memoryAddress); + break; + } case Instruction::BALANCE: + { + require(1); + m_stack.back() = m_state->balance(m_stack.back()); + break; + } case Instruction::MKTX: + { + require(4); + auto dest = m_stack.back(); + m_stack.pop_back(); + + auto amount = m_stack.back(); + m_stack.pop_back(); + + auto fee = m_stack.back(); + m_stack.pop_back(); + + auto itemCount = m_stack.back(); + m_stack.pop_back(); + if (m_stack.size() < itemCount) + throw OperandOutOfRange(0, m_stack.size(), itemCount); + u256s data; + data.reserve((uint)itemCount); + for (auto i = 0; i < itemCount; ++i) + { + data.push_back(m_stack.back()); + m_stack.pop_back(); + } + m_state->transact(m_myAddress, dest, amount, fee, data); break; + } case Instruction::SUICIDE: // TODO: Suicide... case Instruction::STOP: diff --git a/libethereum/VirtualMachine.h b/libethereum/VirtualMachine.h index 34f85d89b..49937d64c 100644 --- a/libethereum/VirtualMachine.h +++ b/libethereum/VirtualMachine.h @@ -65,7 +65,8 @@ enum class Instruction: uint8_t }; class BadInstruction: public std::exception {}; -class StackTooSmall: public std::exception { public: StackTooSmall(uint _req, uint _got): req(_req), got(_got) {} uint req; uint got; }; +class StackTooSmall: public std::exception { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; +class OperandOutOfRange: public std::exception { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; struct BlockInfo { @@ -76,21 +77,49 @@ struct BlockInfo u256 difficulty; }; +class State +{ +public: + State() {} + + u256 memory(u256 _contract, u256 _memory) const + { + auto m = m_memory.find(_contract); + if (m == m_memory.end()) + return 0; + auto i = m->second.find(_memory); + return i == m->second.end() ? 0 : i->second; + } + + std::map& memory(u256 _contract) + { + return m_memory[_contract]; + } + + u256 balance(u256 _id) const { return 0; } + bool transact(u256 _src, u256 _dest, u256 _amount, u256 _fee, u256s const& _data) { return false; } + +private: + std::map> m_memory; +}; + class VirtualMachine { public: - VirtualMachine(); + VirtualMachine(State& _s): m_state(&_s) {} + ~VirtualMachine(); + void initMemory(RLP _contract); void setMemory(RLP _state); void go(); private: - std::map m_memory; + State* m_state; + std::vector m_stack; - u256 m_pc; u256 m_stepCount; u256 m_totalFee; u256 m_stepFee; diff --git a/libethereum/rmd160.h b/libethereum/rmd160.h index f4493098c..52ef5234f 100644 --- a/libethereum/rmd160.h +++ b/libethereum/rmd160.h @@ -18,6 +18,8 @@ #ifndef RMD160H /* make sure this file is read only once */ #define RMD160H +#include + /********************************************************************/ /* function prototypes */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 336ccd15a..fbd393714 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,7 +3,8 @@ cmake_policy(SET CMP0015 NEW) aux_source_directory(. SRC_LIST) include_directories(../libethereum) link_directories(../libethereum) +link_directories(../../secp256k1) add_executable(testeth ${SRC_LIST}) -target_link_libraries(testeth libethereum) +target_link_libraries(testeth ethereum) diff --git a/test/main.cpp b/test/main.cpp index a1b0c8189..f9f527a83 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,5 +1,5 @@ #include -#include "Common.h" +#include #include "RLP.h" #include "Trie.h" #include "VirtualMachine.h"