Gav Wood
11 years ago
7 changed files with 817 additions and 599 deletions
@ -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; |
||||
|
} |
@ -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; |
||||
|
}; |
||||
|
|
||||
|
} |
@ -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; |
||||
|
} |
@ -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…
Reference in new issue