diff --git a/libethereum/Exceptions.h b/libethereum/Exceptions.h
index 3aa73fcac..7bee4699f 100644
--- a/libethereum/Exceptions.h
+++ b/libethereum/Exceptions.h
@@ -17,6 +17,8 @@ class BadHexCharacter: public Exception {};
class NotEnoughCash: public Exception {};
class VMException: public Exception {};
+class StepsDone: public VMException {};
+class BreakPointHit: 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 OperandOutOfRange: public VMException { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; };
diff --git a/libethereum/FeeStructure.cpp b/libethereum/FeeStructure.cpp
new file mode 100644
index 000000000..afcd31751
--- /dev/null
+++ b/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 .
+*/
+/** @file FeeStructure.cpp
+ * @author Gav Wood
+ * @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;
+}
diff --git a/libethereum/FeeStructure.h b/libethereum/FeeStructure.h
new file mode 100644
index 000000000..56b8e9c0b
--- /dev/null
+++ b/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 .
+*/
+/** @file FeeStructure.h
+ * @author Gav Wood
+ * @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;
+};
+
+}
diff --git a/libethereum/State.cpp b/libethereum/State.cpp
index f5a7c92be..b3c16f66a 100644
--- a/libethereum/State.cpp
+++ b/libethereum/State.cpp
@@ -23,19 +23,6 @@
#include
#include
-#if WIN32
-#pragma warning(push)
-#pragma warning(disable:4244)
-#else
-#pragma GCC diagnostic ignored "-Wunused-function"
-#endif
-#include
-#include
-#include
-#if WIN32
-#pragma warning(pop)
-#else
-#endif
#include
#include
#include "BlockChain.h"
@@ -43,17 +30,10 @@
#include "Exceptions.h"
#include "Dagger.h"
#include "Defaults.h"
+#include "VM.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;
-
u256 eth::c_genesisDifficulty = (u256)1 << 22;
std::map const& eth::genesisState()
@@ -138,22 +118,6 @@ State& State::operator=(State const& _s)
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
{
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)
{
- std::vector stack;
-
- // Set up some local functions.
- auto require = [&](u256 _n)
- {
- 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 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(32, s)); // b.size() == 32
- stack.pop_back();
- }
- array final;
- digest.TruncatedFinal(final.data(), 32);
- stack.push_back(fromBigEndian(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(32, s)); // b.size() == 32
- stack.pop_back();
- }
- array 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(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(bytesConstRef(&pub).cropped(1, 32)));
- stack.push_back(fromBigEndian(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(bytesConstRef(&pub).cropped(1, 32)));
- stack.push_back(fromBigEndian(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(bytesConstRef(&sig).cropped(0, 32)));
- stack.push_back(fromBigEndian(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(bytesConstRef(&pubkey[1], 32)));
- stack.push_back(fromBigEndian(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(32, s)); // b.size() == 32
- stack.pop_back();
- }
- array final;
- digest.TruncatedFinal(final.data(), 32);
- stack.push_back(fromBigEndian(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();
- }
- }
+ VM vm;
+ ExtVM evm(*this, _myAddress, _txSender, _txValue, _txData);
+ vm.go(evm);
+ *_totalFee = vm.runFee();
}
diff --git a/libethereum/State.h b/libethereum/State.h
index 54554d1a1..26b3993e5 100644
--- a/libethereum/State.h
+++ b/libethereum/State.h
@@ -32,7 +32,9 @@
#include "AddressState.h"
#include "Transaction.h"
#include "TrieDB.h"
+#include "FeeStructure.h"
#include "Dagger.h"
+#include "ExtVMFace.h"
namespace eth
{
@@ -44,24 +46,12 @@ std::map const& genesisState();
#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 class UnitTest {};
static const std::map EmptyMapU256U256;
+class ExtVM;
+
/**
* @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).
@@ -70,6 +60,8 @@ static const std::map EmptyMapU256U256;
class State
{
template friend class UnitTest;
+ friend class ExtVM;
+
public:
/// Construct state object.
State(Address _coinbaseAddress, Overlay const& _db);
@@ -254,6 +246,66 @@ private:
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* m_store;
+};
+
inline std::ostream& operator<<(std::ostream& _out, State const& _s)
{
_out << "--- " << _s.rootHash() << std::endl;
diff --git a/libethereum/VM.cpp b/libethereum/VM.cpp
new file mode 100644
index 000000000..727559751
--- /dev/null
+++ b/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 .
+*/
+/** @file VM.cpp
+ * @author Gav Wood
+ * @date 2014
+ */
+
+#include "VM.h"
+
+#include
+#include
+#if WIN32
+#pragma warning(push)
+#pragma warning(disable:4244)
+#else
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+#include
+#include
+#include
+#if WIN32
+#pragma warning(pop)
+#else
+#endif
+#include
+#include
+#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;
+}
diff --git a/libethereum/VM.h b/libethereum/VM.h
new file mode 100644
index 000000000..b80c9a0a8
--- /dev/null
+++ b/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 .
+*/
+/** @file VM.h
+ * @author Gav Wood
+ * @date 2014
+ */
+
+#pragma once
+
+#include
+#include
+#if WIN32
+#pragma warning(push)
+#pragma warning(disable:4244)
+#else
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+#include
+#include
+#include
+#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 friend class UnitTest;
+
+public:
+ /// Construct VM object.
+ VM();
+
+ void reset();
+
+ template
+ 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 m_temp;
+ std::vector m_stack;
+ u256 m_runFee = 0;
+};
+
+}
+
+// INLINE:
+template 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(32, s)); // b.size() == 32
+ m_stack.pop_back();
+ }
+ std::array final;
+ digest.TruncatedFinal(final.data(), 32);
+ m_stack.push_back(fromBigEndian(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(32, s)); // b.size() == 32
+ m_stack.pop_back();
+ }
+ std::array 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(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(bytesConstRef(&pub).cropped(1, 32)));
+ m_stack.push_back(fromBigEndian(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(bytesConstRef(&pub).cropped(1, 32)));
+ m_stack.push_back(fromBigEndian(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(bytesConstRef(&sig).cropped(0, 32)));
+ m_stack.push_back(fromBigEndian(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(bytesConstRef(&pubkey[1], 32)));
+ m_stack.push_back(fromBigEndian(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(32, s)); // b.size() == 32
+ m_stack.pop_back();
+ }
+ std::array final;
+ digest.TruncatedFinal(final.data(), 32);
+ m_stack.push_back(fromBigEndian(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();
+}
+