Browse Source

VM nearly instruction-complete.

cl-refactor
Gav Wood 11 years ago
parent
commit
2c492f53f8
  1. 2
      CMakeLists.txt
  2. 8
      README.md
  3. 8
      libethereum/CMakeLists.txt
  4. 1
      libethereum/Common.h
  5. 129
      libethereum/VirtualMachine.cpp
  6. 37
      libethereum/VirtualMachine.h
  7. 2
      libethereum/rmd160.h
  8. 3
      test/CMakeLists.txt
  9. 2
      test/main.cpp

2
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")

8
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.

8
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)

1
libethereum/Common.h

@ -20,6 +20,7 @@ using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backe
using s256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>>;
using uint = uint64_t;
using sint = int64_t;
using u256s = std::vector<u256>;
template <class _T> std::string toString(_T const& _t) { std::ostringstream o; o << _t; return o.str(); }

129
libethereum/VirtualMachine.cpp

@ -1,12 +1,9 @@
#include "sha256.h"
#include <secp256k1.h>
#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:

37
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<u256, u256>& 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<u256, std::map<u256, u256>> 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<u256, u256> m_memory;
State* m_state;
std::vector<u256> m_stack;
u256 m_pc;
u256 m_stepCount;
u256 m_totalFee;
u256 m_stepFee;

2
libethereum/rmd160.h

@ -18,6 +18,8 @@
#ifndef RMD160H /* make sure this file is read only once */
#define RMD160H
#include <cstdint>
/********************************************************************/
/* function prototypes */

3
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)

2
test/main.cpp

@ -1,5 +1,5 @@
#include <random>
#include "Common.h"
#include <Common.h>
#include "RLP.h"
#include "Trie.h"
#include "VirtualMachine.h"

Loading…
Cancel
Save