521 lines
12 KiB

#include <secp256k1.h>
#include <random>
#include "sha256.h"
#include "VirtualMachine.h"
using namespace std;
using namespace eth;
u256 const State::c_stepFee = 0;
u256 const State::c_dataFee = 0;
u256 const State::c_memoryFee = 0;
u256 const State::c_extroFee = 0;
u256 const State::c_cryptoFee = 0;
u256 const State::c_newContractFee = 0;
u256 extractSender(u256 _v, u256 _r, u256 _s)
{
// TODO...
return _s;
}
template <class _T>
inline _T low160(_T const& _t)
{
return _t & ((((_T)1) << 160) - 1);
}
bool State::transact(bytes const& _rlp)
{
RLP rlp(_rlp);
if (!rlp.isList())
return false;
RLPs items = rlp.toList();
// if (!items[0].isFixedInt())
// return false;
if (!items[0].isString())
return false;
u256 nonce = items[0].toFatInt();
// if (!(items[1].isEmpty() || items[1].isFixedInt()))
// return false;
if (!items[1].isString())
return false;
u256 address = items[1].toFatInt();
if (!items[2].isFixedInt())
return false;
u256 value = items[2].toFatInt();
if (!items[3].isFixedInt())
return false;
u256 fee = items[3].toFatInt();
if (!items[4].isList())
return false;
u256s data;
data.reserve(items[4].itemCount());
for (auto const& i: items[4].toList())
if (i.isFixedInt())
data.push_back(i.toFatInt());
else
return false;
if (!items[5].isString())
return false;
u256 v = items[5].toFatInt();
if (!items[6].isString())
return false;
u256 r = items[6].toFatInt();
if (!items[7].isString())
return false;
u256 s = items[7].toFatInt();
u256 sender;
try
{
sender = extractSender(v, r, s);
}
catch (...)
{
// Invalid signiture.
// Error reporting?
return false;
}
if (nonce != transactionsFrom(sender))
{
// Nonce is wrong.
// Error reporting?
return false;
}
if (balance(sender) < value + fee)
{
// Sender balance too low.
// Error reporting?
return false;
}
if (address)
{
assert(subBalance(sender, value));
addBalance(address, value);
if (isContractAddress(address))
{
bool ret = true;
u256 minerFee = 0;
try
{
execute(address, sender, value, fee, data, &minerFee);
}
catch (...)
{
// Execution error.
// Error reporting?
ret = false;
}
addBalance(m_minerAddress, minerFee);
return ret;
}
else
return true;
}
else
{
if (fee < data.size() * c_memoryFee + c_newContractFee)
{
// Fee too small.
// Error reporting?
return false;
}
u256 newAddress = low160(sha256(_rlp));
if (isContractAddress(newAddress))
{
// Contract collision.
// Error reporting?
return false;
}
auto& mem = m_contractMemory[newAddress];
for (uint i = 0; i < data.size(); ++i)
mem[i] = data[i];
assert(subBalance(sender, value));
addBalance(newAddress, value);
return true;
}
}
void State::execute(u256 _myAddress, u256 _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee)
{
std::vector<u256> stack;
auto& myMemory = ensureMemory(_myAddress);
auto require = [&](u256 _n)
{
if (stack.size() < _n)
throw StackTooSmall(_n, stack.size());
};
auto mem = [&](u256 _n) -> u256
{
auto i = myMemory.find(_n);
return i == myMemory.end() ? 0 : i->second;
};
auto setMem = [&](u256 _n, u256 _v)
{
if (_v)
myMemory[_n] = _v;
else
myMemory.erase(_n);
};
u256 curPC = 0;
u256 nextPC = 1;
u256 stepCount = 0;
for (bool stopped = false; !stopped; curPC = nextPC, nextPC = curPC + 1)
{
stepCount++;
bigint minerFee = stepCount > 16 ? c_stepFee : 0;
bigint voidFee = 0;
auto rawInst = mem(curPC);
if (rawInst > 0xff)
throw BadInstruction();
Instruction inst = (Instruction)(uint8_t)rawInst;
switch (inst)
{
case Instruction::STORE:
require(2);
if (!mem(stack.back()) && stack[stack.size() - 2])
voidFee += c_memoryFee;
if (mem(stack.back()) && !stack[stack.size() - 2])
voidFee -= c_memoryFee;
// continue on to...
case Instruction::LOAD:
minerFee += c_dataFee;
break;
case Instruction::EXTRO:
case Instruction::BALANCE:
minerFee += c_extroFee;
break;
case Instruction::SHA256:
case Instruction::RIPEMD160:
case Instruction::ECMUL:
case Instruction::ECADD:
case Instruction::ECSIGN:
case Instruction::ECRECOVER:
case Instruction::ECVALID:
minerFee += c_cryptoFee;
break;
default:
break;
}
if (minerFee + voidFee > balance(_myAddress))
return;
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(_myAddress);
break;
case Instruction::TXSENDER:
stack.push_back(_txSender);
break;
case Instruction::TXVALUE:
stack.push_back(_txValue);
break;
case Instruction::TXFEE:
stack.push_back(_txFee);
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(m_currentBlock.coinbase);
break;
case Instruction::BLK_TIMESTAMP:
stack.push_back(m_currentBlock.timestamp);
break;
case Instruction::BLK_NUMBER:
stack.push_back(m_currentBlock.number);
break;
case Instruction::BLK_DIFFICULTY:
stack.push_back(m_currentBlock.difficulty);
break;
case Instruction::SHA256:
case Instruction::RIPEMD160:
{
uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32);
bytes b(s);
uint i = 0;
for (; s; s = (s >= 32 ? s - 32 : 0), i += 32)
{
stack.pop_back();
u256 v = stack.back();
int sz = (int)min<u256>(32, s) - 1; // sz is one fewer than the number of bytes we're interested in.
v >>= ((31 - sz) * 8); // kill unused low-order bytes.
for (int j = 0; j <= sz; ++j, v >>= 8) // cycle through bytes, starting at low-order end.
b[i + sz - j] = (byte)(v & 0xff); // set each 32-byte (256-bit) chunk in reverse - (i.e. we want to put low-order last).
}
if (inst == Instruction::SHA256)
stack.back() = sha256(b);
else
// NOTE: this aligns to right of 256-bit container (low-order bytes). This won't work if they're treated as byte-arrays and thus left-aligned in a 256-bit container.
stack.back() = ripemd160(&b);
break;
}
case Instruction::ECMUL:
case Instruction::ECADD:
case Instruction::ECSIGN:
case Instruction::ECRECOVER:
case Instruction::ECVALID:
// TODO
break;
case Instruction::PUSH:
{
stack.push_back(mem(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 = mem(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 = mem(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::LOAD:
require(1);
stack.back() = mem(stack.back());
break;
case Instruction::STORE:
require(2);
setMem(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();
auto contractAddress = stack.back();
stack.back() = memory(contractAddress, memoryAddress);
break;
}
case Instruction::BALANCE:
{
require(1);
stack.back() = balance(stack.back());
break;
}
case Instruction::MKTX:
{
require(4);
auto dest = stack.back();
stack.pop_back();
auto value = stack.back();
stack.pop_back();
auto fee = stack.back();
stack.pop_back();
auto itemCount = stack.back();
stack.pop_back();
if (stack.size() < itemCount)
throw OperandOutOfRange(0, stack.size(), itemCount);
u256s data;
data.reserve((uint)itemCount);
for (auto i = 0; i < itemCount; ++i)
{
data.push_back(stack.back());
stack.pop_back();
}
u256 nonce = transactionsFrom(_myAddress);
u256 v = 42; // TODO: turn our address into a v/r/s signature?
u256 r = 42;
u256 s = _myAddress;
// v/r/s are required to make the transaction hash (via the RLP serialisation) and thus are required in the creation of a contract.
RLPStream rlp;
if (nonce)
rlp << nonce;
else
rlp << "";
if (dest)
rlp << toBigEndianString(dest);
else
rlp << "";
rlp << value << fee << data << toBigEndianString(v) << toBigEndianString(r) << toBigEndianString(s);
transact(rlp.out());
break;
}
case Instruction::SUICIDE:
{
require(1);
auto dest = stack.back();
u256 minusVoidFee = m_contractMemory[_myAddress].size() * c_memoryFee;
addBalance(dest, balance(_myAddress) + minusVoidFee - _txFee);
m_balance.erase(_myAddress);
m_contractMemory.erase(_myAddress);
// ...follow through to...
}
case Instruction::STOP:
return;
default:
throw BadInstruction();
}
}
}