Browse Source

Merge branch 'vmseparation' into develop

cl-refactor
Gav Wood 11 years ago
parent
commit
7634e23be3
  1. 2
      libethereum/Exceptions.h
  2. 49
      libethereum/FeeStructure.cpp
  3. 43
      libethereum/FeeStructure.h
  4. 590
      libethereum/State.cpp
  5. 80
      libethereum/State.h
  6. 60
      libethereum/VM.cpp
  7. 592
      libethereum/VM.h

2
libethereum/Exceptions.h

@ -17,6 +17,8 @@ class BadHexCharacter: public Exception {};
class NotEnoughCash: public Exception {}; class NotEnoughCash: public Exception {};
class VMException: public Exception {}; class VMException: public Exception {};
class StepsDone: public VMException {};
class BreakPointHit: public VMException {};
class BadInstruction: public VMException {}; class BadInstruction: public VMException {};
class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; class StackTooSmall: public VMException { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
class OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; class OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; };

49
libethereum/FeeStructure.cpp

@ -0,0 +1,49 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file FeeStructure.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "FeeStructure.h"
using namespace std;
using namespace eth;
u256 const c_stepFee = 1;
u256 const c_dataFee = 20;
u256 const c_memoryFee = 0;//5; memoryFee is 0 for PoC-3
u256 const c_extroFee = 40;
u256 const c_cryptoFee = 20;
u256 const c_newContractFee = 100;
u256 const c_txFee = 100;
void FeeStructure::setMultiplier(u256 _x)
{
m_stepFee = c_stepFee * _x;
m_dataFee = c_dataFee * _x;
m_memoryFee = c_memoryFee * _x;
m_extroFee = c_extroFee * _x;
m_cryptoFee = c_cryptoFee * _x;
m_newContractFee = c_newContractFee * _x;
m_txFee = c_txFee * _x;
}
u256 FeeStructure::multiplier() const
{
return m_stepFee / c_stepFee;
}

43
libethereum/FeeStructure.h

@ -0,0 +1,43 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file FeeStructure.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include "Common.h"
namespace eth
{
struct FeeStructure
{
/// The fee structure. Values yet to be agreed on...
void setMultiplier(u256 _x); ///< The current block multiplier.
u256 multiplier() const;
u256 m_stepFee;
u256 m_dataFee;
u256 m_memoryFee;
u256 m_extroFee;
u256 m_cryptoFee;
u256 m_newContractFee;
u256 m_txFee;
};
}

590
libethereum/State.cpp

@ -23,19 +23,6 @@
#include <secp256k1.h> #include <secp256k1.h>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#if WIN32
#pragma warning(push)
#pragma warning(disable:4244)
#else
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#include <sha.h>
#include <sha3.h>
#include <ripemd.h>
#if WIN32
#pragma warning(pop)
#else
#endif
#include <time.h> #include <time.h>
#include <random> #include <random>
#include "BlockChain.h" #include "BlockChain.h"
@ -43,17 +30,10 @@
#include "Exceptions.h" #include "Exceptions.h"
#include "Dagger.h" #include "Dagger.h"
#include "Defaults.h" #include "Defaults.h"
#include "VM.h"
using namespace std; using namespace std;
using namespace eth; using namespace eth;
u256 const c_stepFee = 1;
u256 const c_dataFee = 20;
u256 const c_memoryFee = 0;//5; memoryFee is 0 for PoC-3
u256 const c_extroFee = 40;
u256 const c_cryptoFee = 20;
u256 const c_newContractFee = 100;
u256 const c_txFee = 100;
u256 eth::c_genesisDifficulty = (u256)1 << 22; u256 eth::c_genesisDifficulty = (u256)1 << 22;
std::map<Address, AddressState> const& eth::genesisState() std::map<Address, AddressState> const& eth::genesisState()
@ -138,22 +118,6 @@ State& State::operator=(State const& _s)
return *this; return *this;
} }
void FeeStructure::setMultiplier(u256 _x)
{
m_stepFee = c_stepFee * _x;
m_dataFee = c_dataFee * _x;
m_memoryFee = c_memoryFee * _x;
m_extroFee = c_extroFee * _x;
m_cryptoFee = c_cryptoFee * _x;
m_newContractFee = c_newContractFee * _x;
m_txFee = c_txFee * _x;
}
u256 FeeStructure::multiplier() const
{
return m_stepFee / c_stepFee;
}
void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const
{ {
auto it = m_cache.find(_a); auto it = m_cache.find(_a);
@ -736,554 +700,10 @@ void State::executeBare(Transaction const& _t, Address _sender)
} }
} }
// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash.
// Currently we just pull out the left (high-order in BE) 160-bits.
// TODO: CHECK: check that this is correct.
inline Address asAddress(u256 _item)
{
return right160(h256(_item));
}
void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* _totalFee) void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* _totalFee)
{ {
std::vector<u256> stack; VM vm;
ExtVM evm(*this, _myAddress, _txSender, _txValue, _txData);
// Set up some local functions. vm.go(evm);
auto require = [&](u256 _n) *_totalFee = vm.runFee();
{
if (stack.size() < _n)
throw StackTooSmall(_n, stack.size());
};
ensureCached(_myAddress, true, true);
auto& myStore = m_cache[_myAddress].memory();
auto store = [&](u256 _n) -> u256
{
auto i = myStore.find(_n);
return i == myStore.end() ? 0 : i->second;
};
auto setStore = [&](u256 _n, u256 _v)
{
if (_v)
{
#ifdef __clang__
auto it = myStore.find(_n);
if (it == myStore.end())
myStore.insert(make_pair(_n, _v));
else
myStore.at(_n) = _v;
#else
myStore[_n] = _v;
#endif
}
else
myStore.erase(_n);
};
map<u256, u256> tempMem;
u256 curPC = 0;
u256 nextPC = 1;
u256 stepCount = 0;
for (bool stopped = false; !stopped; curPC = nextPC, nextPC = curPC + 1)
{
stepCount++;
bigint minerFee = stepCount > 16 ? m_fees.m_stepFee : 0;
bigint voidFee = 0;
auto rawInst = store(curPC);
if (rawInst > 0xff)
throw BadInstruction();
Instruction inst = (Instruction)(uint8_t)rawInst;
switch (inst)
{
case Instruction::SSTORE:
require(2);
if (!store(stack.back()) && stack[stack.size() - 2])
voidFee += m_fees.m_memoryFee;
if (store(stack.back()) && !stack[stack.size() - 2])
voidFee -= m_fees.m_memoryFee;
// continue on to...
case Instruction::SLOAD:
minerFee += m_fees.m_dataFee;
break;
case Instruction::EXTRO:
case Instruction::BALANCE:
minerFee += m_fees.m_extroFee;
break;
case Instruction::MKTX:
minerFee += m_fees.m_txFee;
break;
case Instruction::SHA256:
case Instruction::RIPEMD160:
case Instruction::ECMUL:
case Instruction::ECADD:
case Instruction::ECSIGN:
case Instruction::ECRECOVER:
case Instruction::ECVALID:
minerFee += m_fees.m_cryptoFee;
break;
default:
break;
}
if (minerFee + voidFee > balance(_myAddress))
throw NotEnoughCash();
subBalance(_myAddress, minerFee + voidFee);
*_totalFee += (u256)minerFee;
switch (inst)
{
case Instruction::ADD:
//pops two items and pushes S[-1] + S[-2] mod 2^256.
require(2);
stack[stack.size() - 2] += stack.back();
stack.pop_back();
break;
case Instruction::MUL:
//pops two items and pushes S[-1] * S[-2] mod 2^256.
require(2);
stack[stack.size() - 2] *= stack.back();
stack.pop_back();
break;
case Instruction::SUB:
require(2);
stack[stack.size() - 2] = stack.back() - stack[stack.size() - 2];
stack.pop_back();
break;
case Instruction::DIV:
require(2);
stack[stack.size() - 2] = stack.back() / stack[stack.size() - 2];
stack.pop_back();
break;
case Instruction::SDIV:
require(2);
(s256&)stack[stack.size() - 2] = (s256&)stack.back() / (s256&)stack[stack.size() - 2];
stack.pop_back();
break;
case Instruction::MOD:
require(2);
stack[stack.size() - 2] = stack.back() % stack[stack.size() - 2];
stack.pop_back();
break;
case Instruction::SMOD:
require(2);
(s256&)stack[stack.size() - 2] = (s256&)stack.back() % (s256&)stack[stack.size() - 2];
stack.pop_back();
break;
case Instruction::EXP:
{
// TODO: better implementation?
require(2);
auto n = stack.back();
auto x = stack[stack.size() - 2];
stack.pop_back();
for (u256 i = 0; i < x; ++i)
n *= n;
stack.back() = n;
break;
}
case Instruction::NEG:
require(1);
stack.back() = ~(stack.back() - 1);
break;
case Instruction::LT:
require(2);
stack[stack.size() - 2] = stack.back() < stack[stack.size() - 2] ? 1 : 0;
stack.pop_back();
break;
case Instruction::LE:
require(2);
stack[stack.size() - 2] = stack.back() <= stack[stack.size() - 2] ? 1 : 0;
stack.pop_back();
break;
case Instruction::GT:
require(2);
stack[stack.size() - 2] = stack.back() > stack[stack.size() - 2] ? 1 : 0;
stack.pop_back();
break;
case Instruction::GE:
require(2);
stack[stack.size() - 2] = stack.back() >= stack[stack.size() - 2] ? 1 : 0;
stack.pop_back();
break;
case Instruction::EQ:
require(2);
stack[stack.size() - 2] = stack.back() == stack[stack.size() - 2] ? 1 : 0;
stack.pop_back();
break;
case Instruction::NOT:
require(1);
stack.back() = stack.back() ? 0 : 1;
stack.pop_back();
break;
case Instruction::MYADDRESS:
stack.push_back((u160)_myAddress);
break;
case Instruction::TXSENDER:
stack.push_back((u160)_txSender);
break;
case Instruction::TXVALUE:
stack.push_back(_txValue);
break;
case Instruction::TXDATAN:
stack.push_back(_txData.size());
break;
case Instruction::TXDATA:
require(1);
stack.back() = stack.back() < _txData.size() ? _txData[(uint)stack.back()] : 0;
break;
case Instruction::BLK_PREVHASH:
stack.push_back(m_previousBlock.hash);
break;
case Instruction::BLK_COINBASE:
stack.push_back((u160)m_currentBlock.coinbaseAddress);
break;
case Instruction::BLK_TIMESTAMP:
stack.push_back(m_previousBlock.timestamp);
break;
case Instruction::BLK_NUMBER:
stack.push_back(m_currentNumber);
break;
case Instruction::BLK_DIFFICULTY:
stack.push_back(m_currentBlock.difficulty);
break;
case Instruction::BLK_NONCE:
stack.push_back(m_previousBlock.nonce);
break;
case Instruction::BASEFEE:
stack.push_back(m_fees.multiplier());
break;
case Instruction::SHA256:
{
uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32);
stack.pop_back();
CryptoPP::SHA256 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
bytes b = toBigEndian(stack.back());
digest.Update(b.data(), (int)min<u256>(32, s)); // b.size() == 32
stack.pop_back();
}
array<byte, 32> final;
digest.TruncatedFinal(final.data(), 32);
stack.push_back(fromBigEndian<u256>(final));
break;
}
case Instruction::RIPEMD160:
{
uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32);
stack.pop_back();
CryptoPP::RIPEMD160 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
bytes b = toBigEndian(stack.back());
digest.Update(b.data(), (int)min<u256>(32, s)); // b.size() == 32
stack.pop_back();
}
array<byte, 20> final;
digest.TruncatedFinal(final.data(), 20);
// 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.
stack.push_back((u256)fromBigEndian<u160>(final));
break;
}
case Instruction::ECMUL:
{
// ECMUL - pops three items.
// If (S[-2],S[-1]) are a valid point in secp256k1, including both coordinates being less than P, pushes (S[-1],S[-2]) * S[-3], using (0,0) as the point at infinity.
// Otherwise, pushes (0,0).
require(3);
bytes pub(1, 4);
pub += toBigEndian(stack[stack.size() - 2]);
pub += toBigEndian(stack.back());
stack.pop_back();
stack.pop_back();
bytes x = toBigEndian(stack.back());
stack.pop_back();
if (secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size())) // TODO: Check both are less than P.
{
secp256k1_ecdsa_pubkey_tweak_mul(pub.data(), (int)pub.size(), x.data());
stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(1, 32)));
stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(33, 32)));
}
else
{
stack.push_back(0);
stack.push_back(0);
}
break;
}
case Instruction::ECADD:
{
// ECADD - pops four items and pushes (S[-4],S[-3]) + (S[-2],S[-1]) if both points are valid, otherwise (0,0).
require(4);
bytes pub(1, 4);
pub += toBigEndian(stack[stack.size() - 2]);
pub += toBigEndian(stack.back());
stack.pop_back();
stack.pop_back();
bytes tweak(1, 4);
tweak += toBigEndian(stack[stack.size() - 2]);
tweak += toBigEndian(stack.back());
stack.pop_back();
stack.pop_back();
if (secp256k1_ecdsa_pubkey_verify(pub.data(),(int) pub.size()) && secp256k1_ecdsa_pubkey_verify(tweak.data(),(int) tweak.size()))
{
secp256k1_ecdsa_pubkey_tweak_add(pub.data(), (int)pub.size(), tweak.data());
stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(1, 32)));
stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(33, 32)));
}
else
{
stack.push_back(0);
stack.push_back(0);
}
break;
}
case Instruction::ECSIGN:
{
require(2);
bytes sig(64);
int v = 0;
u256 msg = stack.back();
stack.pop_back();
u256 priv = stack.back();
stack.pop_back();
bytes nonce = toBigEndian(Transaction::kFromMessage(msg, priv));
if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v))
throw InvalidSignature();
stack.push_back(v + 27);
stack.push_back(fromBigEndian<u256>(bytesConstRef(&sig).cropped(0, 32)));
stack.push_back(fromBigEndian<u256>(bytesConstRef(&sig).cropped(32)));
break;
}
case Instruction::ECRECOVER:
{
require(4);
bytes sig = toBigEndian(stack[stack.size() - 2]) + toBigEndian(stack.back());
stack.pop_back();
stack.pop_back();
int v = (int)stack.back();
stack.pop_back();
bytes msg = toBigEndian(stack.back());
stack.pop_back();
byte pubkey[65];
int pubkeylen = 65;
if (secp256k1_ecdsa_recover_compact(msg.data(), (int)msg.size(), sig.data(), pubkey, &pubkeylen, 0, v - 27))
{
stack.push_back(0);
stack.push_back(0);
}
else
{
stack.push_back(fromBigEndian<u256>(bytesConstRef(&pubkey[1], 32)));
stack.push_back(fromBigEndian<u256>(bytesConstRef(&pubkey[33], 32)));
}
break;
}
case Instruction::ECVALID:
{
require(2);
bytes pub(1, 4);
pub += toBigEndian(stack[stack.size() - 2]);
pub += toBigEndian(stack.back());
stack.pop_back();
stack.pop_back();
stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size()) ? 1 : 0;
break;
}
case Instruction::SHA3:
{
uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32);
stack.pop_back();
CryptoPP::SHA3_256 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
bytes b = toBigEndian(stack.back());
digest.Update(b.data(), (int)min<u256>(32, s)); // b.size() == 32
stack.pop_back();
}
array<byte, 32> final;
digest.TruncatedFinal(final.data(), 32);
stack.push_back(fromBigEndian<u256>(final));
break;
}
case Instruction::PUSH:
{
stack.push_back(store(curPC + 1));
nextPC = curPC + 2;
break;
}
case Instruction::POP:
require(1);
stack.pop_back();
break;
case Instruction::DUP:
require(1);
stack.push_back(stack.back());
break;
/*case Instruction::DUPN:
{
auto s = store(curPC + 1);
if (s == 0 || s > stack.size())
throw OperandOutOfRange(1, stack.size(), s);
stack.push_back(stack[stack.size() - (uint)s]);
nextPC = curPC + 2;
break;
}*/
case Instruction::SWAP:
{
require(2);
auto d = stack.back();
stack.back() = stack[stack.size() - 2];
stack[stack.size() - 2] = d;
break;
}
/*case Instruction::SWAPN:
{
require(1);
auto d = stack.back();
auto s = store(curPC + 1);
if (s == 0 || s > stack.size())
throw OperandOutOfRange(1, stack.size(), s);
stack.back() = stack[stack.size() - (uint)s];
stack[stack.size() - (uint)s] = d;
nextPC = curPC + 2;
break;
}*/
case Instruction::MLOAD:
{
require(1);
#ifdef __clang__
auto mFinder = tempMem.find(stack.back());
if (mFinder != tempMem.end())
stack.back() = mFinder->second;
else
stack.back() = 0;
#else
stack.back() = tempMem[stack.back()];
#endif
break;
}
case Instruction::MSTORE:
{
require(2);
#ifdef __clang__
auto mFinder = tempMem.find(stack.back());
if (mFinder == tempMem.end())
tempMem.insert(make_pair(stack.back(), stack[stack.size() - 2]));
else
mFinder->second = stack[stack.size() - 2];
#else
tempMem[stack.back()] = stack[stack.size() - 2];
#endif
stack.pop_back();
stack.pop_back();
break;
}
case Instruction::SLOAD:
require(1);
stack.back() = store(stack.back());
break;
case Instruction::SSTORE:
require(2);
setStore(stack.back(), stack[stack.size() - 2]);
stack.pop_back();
stack.pop_back();
break;
case Instruction::JMP:
require(1);
nextPC = stack.back();
stack.pop_back();
break;
case Instruction::JMPI:
require(2);
if (stack.back())
nextPC = stack[stack.size() - 2];
stack.pop_back();
stack.pop_back();
break;
case Instruction::IND:
stack.push_back(curPC);
break;
case Instruction::EXTRO:
{
require(2);
auto memoryAddress = stack.back();
stack.pop_back();
Address contractAddress = asAddress(stack.back());
stack.back() = contractMemory(contractAddress, memoryAddress);
break;
}
case Instruction::BALANCE:
{
require(1);
stack.back() = balance(asAddress(stack.back()));
break;
}
case Instruction::MKTX:
{
require(4);
Transaction t;
t.receiveAddress = asAddress(stack.back());
stack.pop_back();
t.value = stack.back();
stack.pop_back();
auto itemCount = stack.back();
stack.pop_back();
if (stack.size() < itemCount)
throw OperandOutOfRange(0, stack.size(), itemCount);
t.data.reserve((uint)itemCount);
for (auto i = 0; i < itemCount; ++i)
{
t.data.push_back(stack.back());
stack.pop_back();
}
t.nonce = transactionsFrom(_myAddress);
executeBare(t, _myAddress);
break;
}
case Instruction::SUICIDE:
{
require(1);
Address dest = asAddress(stack.back());
u256 minusVoidFee = myStore.size() * m_fees.m_memoryFee;
addBalance(dest, balance(_myAddress) + minusVoidFee);
m_cache[_myAddress].kill();
// ...follow through to...
}
case Instruction::STOP:
return;
default:
throw BadInstruction();
}
}
} }

80
libethereum/State.h

@ -32,7 +32,9 @@
#include "AddressState.h" #include "AddressState.h"
#include "Transaction.h" #include "Transaction.h"
#include "TrieDB.h" #include "TrieDB.h"
#include "FeeStructure.h"
#include "Dagger.h" #include "Dagger.h"
#include "ExtVMFace.h"
namespace eth namespace eth
{ {
@ -44,24 +46,12 @@ std::map<Address, AddressState> const& genesisState();
#define ETH_SENDER_PAYS_SETUP 1 #define ETH_SENDER_PAYS_SETUP 1
struct FeeStructure
{
/// The fee structure. Values yet to be agreed on...
void setMultiplier(u256 _x); ///< The current block multiplier.
u256 multiplier() const;
u256 m_stepFee;
u256 m_dataFee;
u256 m_memoryFee;
u256 m_extroFee;
u256 m_cryptoFee;
u256 m_newContractFee;
u256 m_txFee;
};
template <unsigned T> class UnitTest {}; template <unsigned T> class UnitTest {};
static const std::map<u256, u256> EmptyMapU256U256; static const std::map<u256, u256> EmptyMapU256U256;
class ExtVM;
/** /**
* @brief Model of the current state of the ledger. * @brief Model of the current state of the ledger.
* Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block).
@ -70,6 +60,8 @@ static const std::map<u256, u256> EmptyMapU256U256;
class State class State
{ {
template <unsigned T> friend class UnitTest; template <unsigned T> friend class UnitTest;
friend class ExtVM;
public: public:
/// Construct state object. /// Construct state object.
State(Address _coinbaseAddress, Overlay const& _db); State(Address _coinbaseAddress, Overlay const& _db);
@ -254,6 +246,66 @@ private:
friend std::ostream& operator<<(std::ostream& _out, State const& _s); friend std::ostream& operator<<(std::ostream& _out, State const& _s);
}; };
class ExtVM: public ExtVMFace
{
public:
ExtVM(State& _s, Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData):
ExtVMFace(_myAddress, _txSender, _txValue, _txData, _s.m_fees, _s.m_previousBlock, _s.m_currentBlock, _s.m_currentNumber), m_s(_s)
{
m_s.ensureCached(_myAddress, true, true);
m_store = &(m_s.m_cache[_myAddress].memory());
}
u256 store(u256 _n)
{
auto i = m_store->find(_n);
return i == m_store->end() ? 0 : i->second;
}
void setStore(u256 _n, u256 _v)
{
if (_v)
{
#ifdef __clang__
auto it = m_store->find(_n);
if (it == m_store->end())
m_store->insert(make_pair(_n, _v));
else
m_store->at(_n) = _v;
#else
(*m_store)[_n] = _v;
#endif
}
else
m_store->erase(_n);
}
void payFee(bigint _f)
{
if (_f > m_s.balance(myAddress))
throw NotEnoughCash();
m_s.subBalance(myAddress, _f);
}
void mktx(Transaction& _t)
{
_t.nonce = m_s.transactionsFrom(myAddress);
m_s.executeBare(_t, myAddress);
}
u256 balance(Address _a) { return m_s.balance(_a); }
u256 txCount(Address _a) { return m_s.transactionsFrom(_a); }
u256 extro(Address _a, u256 _pos) { return m_s.contractMemory(_a, _pos); }
u256 extroPrice(Address _a) { return 0; }
void suicide(Address _a)
{
m_s.addBalance(_a, m_s.balance(myAddress) + m_store->size() * fees.m_memoryFee);
m_s.m_cache[myAddress].kill();
}
private:
State& m_s;
std::map<u256, u256>* m_store;
};
inline std::ostream& operator<<(std::ostream& _out, State const& _s) inline std::ostream& operator<<(std::ostream& _out, State const& _s)
{ {
_out << "--- " << _s.rootHash() << std::endl; _out << "--- " << _s.rootHash() << std::endl;

60
libethereum/VM.cpp

@ -0,0 +1,60 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file VM.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "VM.h"
#include <secp256k1.h>
#include <boost/filesystem.hpp>
#if WIN32
#pragma warning(push)
#pragma warning(disable:4244)
#else
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#include <sha.h>
#include <sha3.h>
#include <ripemd.h>
#if WIN32
#pragma warning(pop)
#else
#endif
#include <ctime>
#include <random>
#include "BlockChain.h"
#include "Instruction.h"
#include "Exceptions.h"
#include "Dagger.h"
#include "Defaults.h"
using namespace std;
using namespace eth;
VM::VM()
{
reset();
}
void VM::reset()
{
m_curPC = 0;
m_nextPC = 1;
m_stepCount = 0;
m_runFee = 0;
}

592
libethereum/VM.h

@ -0,0 +1,592 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file VM.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <unordered_map>
#include <secp256k1.h>
#if WIN32
#pragma warning(push)
#pragma warning(disable:4244)
#else
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#include <sha.h>
#include <sha3.h>
#include <ripemd.h>
#if WIN32
#pragma warning(pop)
#else
#endif
#include "Common.h"
#include "Exceptions.h"
#include "FeeStructure.h"
#include "Instruction.h"
#include "BlockInfo.h"
#include "ExtVMFace.h"
namespace eth
{
// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash.
// Currently we just pull out the right (low-order in BE) 160-bits.
inline Address asAddress(u256 _item)
{
return right160(h256(_item));
}
inline u256 fromAddress(Address _a)
{
return (u160)_a;
}
/**
*/
class VM
{
template <unsigned T> friend class UnitTest;
public:
/// Construct VM object.
VM();
void reset();
template <class Ext>
void go(Ext& _ext, uint64_t _steps = (uint64_t)-1);
void require(u256 _n) { if (m_stack.size() < _n) throw StackTooSmall(_n, m_stack.size()); }
u256 runFee() const { return m_runFee; }
private:
u256 m_curPC = 0;
u256 m_nextPC = 1;
uint64_t m_stepCount = 0;
std::map<u256, u256> m_temp;
std::vector<u256> m_stack;
u256 m_runFee = 0;
};
}
// INLINE:
template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
{
for (bool stopped = false; !stopped && _steps--; m_curPC = m_nextPC, m_nextPC = m_curPC + 1)
{
m_stepCount++;
// INSTRUCTION...
auto rawInst = _ext.store(m_curPC);
if (rawInst > 0xff)
throw BadInstruction();
Instruction inst = (Instruction)(uint8_t)rawInst;
// FEES...
bigint runFee = m_stepCount > 16 ? _ext.fees.m_stepFee : 0;
bigint storeCostDelta = 0;
switch (inst)
{
case Instruction::SSTORE:
require(2);
if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2])
storeCostDelta += _ext.fees.m_memoryFee;
if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
storeCostDelta -= _ext.fees.m_memoryFee;
// continue on to...
case Instruction::SLOAD:
runFee += _ext.fees.m_dataFee;
break;
case Instruction::EXTRO:
case Instruction::BALANCE:
runFee += _ext.fees.m_extroFee;
break;
case Instruction::MKTX:
runFee += _ext.fees.m_txFee;
break;
case Instruction::SHA256:
case Instruction::RIPEMD160:
case Instruction::ECMUL:
case Instruction::ECADD:
case Instruction::ECSIGN:
case Instruction::ECRECOVER:
case Instruction::ECVALID:
runFee += _ext.fees.m_cryptoFee;
break;
default:
break;
}
_ext.payFee(runFee + storeCostDelta);
m_runFee += (u256)runFee;
// EXECUTE...
switch (inst)
{
case Instruction::ADD:
//pops two items and pushes S[-1] + S[-2] mod 2^256.
require(2);
m_stack[m_stack.size() - 2] += m_stack.back();
m_stack.pop_back();
break;
case Instruction::MUL:
//pops two items and pushes S[-1] * S[-2] mod 2^256.
require(2);
m_stack[m_stack.size() - 2] *= m_stack.back();
m_stack.pop_back();
break;
case Instruction::SUB:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() - m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
case Instruction::DIV:
require(2);
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);
(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);
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);
(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::EXP:
{
// TODO: better implementation?
require(2);
auto n = m_stack.back();
auto x = m_stack[m_stack.size() - 2];
m_stack.pop_back();
for (u256 i = 0; i < x; ++i)
n *= n;
m_stack.back() = n;
break;
}
case Instruction::NEG:
require(1);
m_stack.back() = ~(m_stack.back() - 1);
break;
case Instruction::LT:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() < m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::LE:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() <= m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::GT:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() > m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::GE:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() >= m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::EQ:
require(2);
m_stack[m_stack.size() - 2] = m_stack.back() == m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::NOT:
require(1);
m_stack.back() = m_stack.back() ? 0 : 1;
m_stack.pop_back();
break;
case Instruction::MYADDRESS:
m_stack.push_back(fromAddress(_ext.myAddress));
break;
case Instruction::TXSENDER:
m_stack.push_back(fromAddress(_ext.txSender));
break;
case Instruction::TXVALUE:
m_stack.push_back(_ext.txValue);
break;
case Instruction::TXDATAN:
m_stack.push_back(_ext.txData.size());
break;
case Instruction::TXDATA:
require(1);
m_stack.back() = m_stack.back() < _ext.txData.size() ? _ext.txData[(uint)m_stack.back()] : 0;
break;
case Instruction::BLK_PREVHASH:
m_stack.push_back(_ext.previousBlock.hash);
break;
case Instruction::BLK_COINBASE:
m_stack.push_back((u160)_ext.currentBlock.coinbaseAddress);
break;
case Instruction::BLK_TIMESTAMP:
m_stack.push_back(_ext.currentBlock.timestamp);
break;
case Instruction::BLK_NUMBER:
m_stack.push_back(_ext.currentNumber);
break;
case Instruction::BLK_DIFFICULTY:
m_stack.push_back(_ext.currentBlock.difficulty);
break;
case Instruction::BLK_NONCE:
m_stack.push_back(_ext.previousBlock.nonce);
break;
case Instruction::BASEFEE:
m_stack.push_back(_ext.fees.multiplier());
break;
case Instruction::SHA256:
{
uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32);
m_stack.pop_back();
CryptoPP::SHA256 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
bytes b = toBigEndian(m_stack.back());
digest.Update(b.data(), (int)std::min<u256>(32, s)); // b.size() == 32
m_stack.pop_back();
}
std::array<byte, 32> final;
digest.TruncatedFinal(final.data(), 32);
m_stack.push_back(fromBigEndian<u256>(final));
break;
}
case Instruction::RIPEMD160:
{
uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32);
m_stack.pop_back();
CryptoPP::RIPEMD160 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
bytes b = toBigEndian(m_stack.back());
digest.Update(b.data(), (int)std::min<u256>(32, s)); // b.size() == 32
m_stack.pop_back();
}
std::array<byte, 20> final;
digest.TruncatedFinal(final.data(), 20);
// 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.push_back((u256)fromBigEndian<u160>(final));
break;
}
case Instruction::ECMUL:
{
// ECMUL - pops three items.
// If (S[-2],S[-1]) are a valid point in secp256k1, including both coordinates being less than P, pushes (S[-1],S[-2]) * S[-3], using (0,0) as the point at infinity.
// Otherwise, pushes (0,0).
require(3);
bytes pub(1, 4);
pub += toBigEndian(m_stack[m_stack.size() - 2]);
pub += toBigEndian(m_stack.back());
m_stack.pop_back();
m_stack.pop_back();
bytes x = toBigEndian(m_stack.back());
m_stack.pop_back();
if (secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size())) // TODO: Check both are less than P.
{
secp256k1_ecdsa_pubkey_tweak_mul(pub.data(), (int)pub.size(), x.data());
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(1, 32)));
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(33, 32)));
}
else
{
m_stack.push_back(0);
m_stack.push_back(0);
}
break;
}
case Instruction::ECADD:
{
// ECADD - pops four items and pushes (S[-4],S[-3]) + (S[-2],S[-1]) if both points are valid, otherwise (0,0).
require(4);
bytes pub(1, 4);
pub += toBigEndian(m_stack[m_stack.size() - 2]);
pub += toBigEndian(m_stack.back());
m_stack.pop_back();
m_stack.pop_back();
bytes tweak(1, 4);
tweak += toBigEndian(m_stack[m_stack.size() - 2]);
tweak += toBigEndian(m_stack.back());
m_stack.pop_back();
m_stack.pop_back();
if (secp256k1_ecdsa_pubkey_verify(pub.data(),(int) pub.size()) && secp256k1_ecdsa_pubkey_verify(tweak.data(),(int) tweak.size()))
{
secp256k1_ecdsa_pubkey_tweak_add(pub.data(), (int)pub.size(), tweak.data());
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(1, 32)));
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(33, 32)));
}
else
{
m_stack.push_back(0);
m_stack.push_back(0);
}
break;
}
case Instruction::ECSIGN:
{
require(2);
bytes sig(64);
int v = 0;
u256 msg = m_stack.back();
m_stack.pop_back();
u256 priv = m_stack.back();
m_stack.pop_back();
bytes nonce = toBigEndian(Transaction::kFromMessage(msg, priv));
if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v))
throw InvalidSignature();
m_stack.push_back(v + 27);
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&sig).cropped(0, 32)));
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&sig).cropped(32)));
break;
}
case Instruction::ECRECOVER:
{
require(4);
bytes sig = toBigEndian(m_stack[m_stack.size() - 2]) + toBigEndian(m_stack.back());
m_stack.pop_back();
m_stack.pop_back();
int v = (int)m_stack.back();
m_stack.pop_back();
bytes msg = toBigEndian(m_stack.back());
m_stack.pop_back();
byte pubkey[65];
int pubkeylen = 65;
if (secp256k1_ecdsa_recover_compact(msg.data(), (int)msg.size(), sig.data(), pubkey, &pubkeylen, 0, v - 27))
{
m_stack.push_back(0);
m_stack.push_back(0);
}
else
{
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pubkey[1], 32)));
m_stack.push_back(fromBigEndian<u256>(bytesConstRef(&pubkey[33], 32)));
}
break;
}
case Instruction::ECVALID:
{
require(2);
bytes pub(1, 4);
pub += toBigEndian(m_stack[m_stack.size() - 2]);
pub += toBigEndian(m_stack.back());
m_stack.pop_back();
m_stack.pop_back();
m_stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size()) ? 1 : 0;
break;
}
case Instruction::SHA3:
{
uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32);
m_stack.pop_back();
CryptoPP::SHA3_256 digest;
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
bytes b = toBigEndian(m_stack.back());
digest.Update(b.data(), (int)std::min<u256>(32, s)); // b.size() == 32
m_stack.pop_back();
}
std::array<byte, 32> final;
digest.TruncatedFinal(final.data(), 32);
m_stack.push_back(fromBigEndian<u256>(final));
break;
}
case Instruction::PUSH:
{
m_stack.push_back(_ext.store(m_curPC + 1));
m_nextPC = m_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 = store(curPC + 1);
if (s == 0 || s > stack.size())
throw OperandOutOfRange(1, stack.size(), s);
stack.push_back(stack[stack.size() - (uint)s]);
nextPC = curPC + 2;
break;
}*/
case Instruction::SWAP:
{
require(2);
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 = stack.back();
auto s = store(curPC + 1);
if (s == 0 || s > stack.size())
throw OperandOutOfRange(1, stack.size(), s);
stack.back() = stack[stack.size() - (uint)s];
stack[stack.size() - (uint)s] = d;
nextPC = curPC + 2;
break;
}*/
case Instruction::MLOAD:
{
require(1);
#ifdef __clang__
auto mFinder = tempMem.find(stack.back());
if (mFinder != tempMem.end())
stack.back() = mFinder->second;
else
stack.back() = 0;
#else
m_stack.back() = m_temp[m_stack.back()];
#endif
break;
}
case Instruction::MSTORE:
{
require(2);
#ifdef __clang__
auto mFinder = tempMem.find(stack.back());
if (mFinder == tempMem.end())
tempMem.insert(make_pair(stack.back(), stack[stack.size() - 2]));
else
mFinder->second = stack[stack.size() - 2];
#else
m_temp[m_stack.back()] = m_stack[m_stack.size() - 2];
#endif
m_stack.pop_back();
m_stack.pop_back();
break;
}
case Instruction::SLOAD:
require(1);
m_stack.back() = _ext.store(m_stack.back());
break;
case Instruction::SSTORE:
require(2);
_ext.setStore(m_stack.back(), m_stack[m_stack.size() - 2]);
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::JMP:
require(1);
m_nextPC = m_stack.back();
m_stack.pop_back();
break;
case Instruction::JMPI:
require(2);
if (m_stack.back())
m_nextPC = m_stack[m_stack.size() - 2];
m_stack.pop_back();
m_stack.pop_back();
break;
case Instruction::IND:
m_stack.push_back(m_curPC);
break;
case Instruction::EXTRO:
{
require(2);
auto memoryAddress = m_stack.back();
m_stack.pop_back();
Address contractAddress = asAddress(m_stack.back());
m_stack.back() = _ext.extro(contractAddress, memoryAddress);
break;
}
case Instruction::BALANCE:
{
require(1);
m_stack.back() = _ext.balance(asAddress(m_stack.back()));
break;
}
case Instruction::MKTX:
{
require(3);
Transaction t;
t.receiveAddress = asAddress(m_stack.back());
m_stack.pop_back();
t.value = 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);
t.data.reserve((uint)itemCount);
for (auto i = 0; i < itemCount; ++i)
{
t.data.push_back(m_stack.back());
m_stack.pop_back();
}
_ext.mktx(t);
break;
}
case Instruction::SUICIDE:
{
require(1);
Address dest = asAddress(m_stack.back());
_ext.suicide(dest);
// ...follow through to...
}
case Instruction::STOP:
return;
default:
throw BadInstruction();
}
}
if (_steps == (unsigned)-1)
throw StepsDone();
}
Loading…
Cancel
Save