Browse Source

PoC-3.5. ish.

cl-refactor
Gav Wood 11 years ago
parent
commit
472323115a
  1. 1
      eth/main.cpp
  2. 17
      libethereum/Client.cpp
  3. 10
      libethereum/Client.h
  4. 1
      libethereum/Exceptions.h
  5. 19
      libethereum/ExtVMFace.h
  6. 29
      libethereum/FeeStructure.cpp
  7. 24
      libethereum/FeeStructure.h
  8. 24
      libethereum/Instruction.cpp
  9. 25
      libethereum/Instruction.h
  10. 212
      libethereum/State.cpp
  11. 58
      libethereum/State.h
  12. 30
      libethereum/Transaction.cpp
  13. 36
      libethereum/Transaction.h
  14. 8
      libethereum/VM.cpp
  15. 215
      libethereum/VM.h
  16. 6
      test/vm.cpp

1
eth/main.cpp

@ -286,6 +286,7 @@ int main(int argc, char** argv)
u256 amount;
cin >> rechex >> amount;
Address dest = h160(fromHex(rechex));
c.transact(us.secret(), dest, amount);
}
else if (cmd == "help")

17
libethereum/Client.cpp

@ -131,13 +131,14 @@ void Client::stopMining()
m_doMine = false;
}
void Client::transact(Secret _secret, Address _dest, u256 _amount, u256s _data)
void Client::call(Secret _secret, u256 _amount, u256 _baseFee, Address _dest, u256 _gas, bytes _data)
{
lock_guard<recursive_mutex> l(m_lock);
Transaction t;
t.nonce = m_postMine.transactionsFrom(toAddress(_secret));
t.receiveAddress = _dest;
t.value = _amount;
t.gas = _gas;
t.data = _data;
t.sign(_secret);
cnote << "New transaction " << t;
@ -145,6 +146,20 @@ void Client::transact(Secret _secret, Address _dest, u256 _amount, u256s _data)
m_changed = true;
}
void Client::create(Secret _secret, u256 _endowment, u256 _baseFee, u256s _storage)
{
lock_guard<recursive_mutex> l(m_lock);
Transaction t;
t.nonce = m_postMine.transactionsFrom(toAddress(_secret));
t.value = _endowment;
t.gasPrice = _baseFee;
t.storage = _storage;
t.sign(_secret);
cnote << "New transaction " << t;
m_tq.attemptImport(t.rlp());
m_changed = true;
}
void Client::work()
{
bool changed = false;

10
libethereum/Client.h

@ -83,8 +83,14 @@ public:
/// Destructor.
~Client();
/// Executes the given transaction.
void transact(Secret _secret, Address _dest, u256 _amount, u256s _data = u256s());
/// Submits the given transaction.
void transact(Secret _secret, u256 _amount, u256 _baseFee, Address _dest, u256 _gas, bytes _data = bytes());
/// Makes the given call. Nothing is recorded into the state. TODO
// bytes call(Secret _secret, u256 _amount, u256 _baseFee, Address _dest, u256 _gas, bytes _data = bytes());
/// Submits a new contract.
void create(Secret _secret, u256 _endowment, u256 _baseFee, u256s _storage = u256s());
/// Requires transactions involving this address be queued for inspection.
void setInterest(Address _dest);

1
libethereum/Exceptions.h

@ -26,6 +26,7 @@ class VMException: public Exception {};
class StepsDone: public VMException {};
class BreakPointHit: public VMException {};
class BadInstruction: public VMException {};
class OutOfGas: 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; };

19
libethereum/ExtVMFace.h

@ -35,19 +35,18 @@ class ExtVMFace
public:
ExtVMFace() {}
ExtVMFace(FeeStructure const& _fees, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
fees(_fees),
ExtVMFace(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
previousBlock(_previousBlock),
currentBlock(_currentBlock),
currentNumber(_currentNumber)
{}
ExtVMFace(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, FeeStructure const& _fees, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
ExtVMFace(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
myAddress(_myAddress),
txSender(_txSender),
txValue(_txValue),
gasPrice(_gasPrice),
txData(_txData),
fees(_fees),
previousBlock(_previousBlock),
currentBlock(_currentBlock),
currentNumber(_currentNumber)
@ -61,21 +60,21 @@ public:
u256 store(u256 _n) { return 0; }
void setStore(u256 _n, u256 _v) {}
void mktx(Transaction& _t) {}
u256 balance(Address _a) { return 0; }
void payFee(bigint _fee) {}
void subBalance(u256 _a) {}
u256 txCount(Address _a) { return 0; }
u256 extro(Address _a, u256 _pos) { return 0; }
u256 extroPrice(Address _a) { return 0; }
void suicide(Address _a) {}
h160 create(Address _txSender, u256 _endowment, vector_ref<h256 const> _storage) { return h160(); }
bool call(Address _myAddress, Address _txSender, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _tx) { return false; }
#pragma GCC diagnostic pop
#pragma warning(pop)
Address myAddress;
Address txSender;
u256 txValue;
u256s txData;
FeeStructure fees;
u256 gasPrice;
bytesConstRef txData;
BlockInfo previousBlock; ///< The current block's information.
BlockInfo currentBlock; ///< The current block's information.
uint currentNumber;

29
libethereum/FeeStructure.cpp

@ -24,26 +24,11 @@
using namespace std;
using namespace eth;
u256 const c_stepFee = 1;
u256 const c_dataFee = 20;
u256 const c_memoryFee = 5;
u256 const c_stepGas = 1;
u256 const c_ioGas = 20;
u256 const c_memoryGas = 5;
u256 const c_newMemoryFee = 5;
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;
}
u256 const c_balanceGas = 40;
u256 const c_createGas = 100;
u256 const c_callGas = 100;

24
libethereum/FeeStructure.h

@ -26,20 +26,14 @@
namespace eth
{
/**
* The collection of fee amounts that makes up the fee structure.
*/
struct FeeStructure
{
void setMultiplier(u256 _x); ///< Set the current block multiplier.
u256 multiplier() const; ///< @returns the current block multiplier.
u256 m_stepFee;
u256 m_dataFee;
u256 m_memoryFee;
u256 m_extroFee;
u256 m_cryptoFee;
u256 m_newContractFee;
u256 m_txFee;
};
extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL.
extern u256 const c_balanceGas; ///< Once per BALANCE operation.
extern u256 const c_sha3Gas; ///< Once per SHA3 operation.
extern u256 const c_sloadGas; ///< Once per SLOAD operation.
extern u256 const c_sstoreGas; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once).
extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction.
extern u256 const c_callGas; ///< Once per CALL operation & message call transaction.
extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
extern u256 const c_txDataGas; ///< Per byte of data attached to a message-call transaction. NOTE: Not payable on data of calls between transactions.
}

24
libethereum/Instruction.cpp

@ -56,7 +56,7 @@ const std::map<std::string, Instruction> eth::c_instructions =
{ "CALLVALUE", Instruction::CALLVALUE },
{ "CALLDATA", Instruction::CALLDATA },
{ "CALLDATASIZE", Instruction::CALLDATASIZE },
{ "BASEFEE", Instruction::BASEFEE },
{ "BASEFEE", Instruction::GASPRICE },
{ "PREVHASH", Instruction::PREVHASH },
{ "PREVNONCE", Instruction::PREVNONCE },
{ "COINBASE", Instruction::COINBASE },
@ -75,6 +75,7 @@ const std::map<std::string, Instruction> eth::c_instructions =
{ "JUMP", Instruction::JUMP },
{ "JUMPI", Instruction::JUMPI },
{ "PC", Instruction::PC },
{ "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL },
{ "RETURN", Instruction::RETURN },
{ "SUICIDE", Instruction::SUICIDE }
@ -108,7 +109,7 @@ const std::map<Instruction, InstructionInfo> eth::c_instructionInfo =
{ Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1 } },
{ Instruction::CALLDATA, { "CALLDATA", 0, 0, 1 } },
{ Instruction::CALLDATASIZE, { "CALLDATASIZE", 0, 1, 1 } },
{ Instruction::BASEFEE, { "BASEFEE", 0, 0, 1 } },
{ Instruction::GASPRICE, { "BASEFEE", 0, 0, 1 } },
{ Instruction::PREVHASH, { "PREVHASH", 0, 0, 1 } },
{ Instruction::PREVNONCE, { "PREVNONCE", 0, 0, 1 } },
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1 } },
@ -127,7 +128,8 @@ const std::map<Instruction, InstructionInfo> eth::c_instructionInfo =
{ Instruction::JUMP, { "JUMP", 0, 1, 0 } },
{ Instruction::JUMPI, { "JUMPI", 0, 2, 0 } },
{ Instruction::PC, { "PC", 0, 0, 1 } },
{ Instruction::CALL, { "CALL", 0, 6, 1 } },
{ Instruction::CREATE, { "CREATE", 0, 3, 1 } }, // endowment, first word in memory, data words
{ Instruction::CALL, { "CALL", 0, 7, 1 } },
{ Instruction::RETURN, { "RETURN", 0, 2, 0 } },
{ Instruction::SUICIDE, { "SUICIDE", 0, 1, 0} }
};
@ -360,7 +362,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
appendCode(o_code, o_locs, codes[0], locs[0]);
// Jump to positive if true.
o_code.push_back(Instruction::JMPI);
o_code.push_back(Instruction::JUMPI);
// Second fragment - negative.
appendCode(o_code, o_locs, codes[2], locs[2]);
@ -370,7 +372,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
unsigned endLocation = (unsigned)o_code.size();
o_locs.push_back(endLocation);
o_code.push_back(0);
o_code.push_back(Instruction::JMP);
o_code.push_back(Instruction::JUMP);
// Third fragment - positive.
o_code[posLocation] = o_code.size();
@ -409,7 +411,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
// Jump to end...
if (t == "WHEN")
o_code.push_back(Instruction::NOT);
o_code.push_back(Instruction::JMPI);
o_code.push_back(Instruction::JUMPI);
// Second fragment - negative.
appendCode(o_code, o_locs, codes[1], locs[1]);
@ -448,7 +450,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
// Jump to positive if true.
o_code.push_back(Instruction::NOT);
o_code.push_back(Instruction::JMPI);
o_code.push_back(Instruction::JUMPI);
// Second fragment - negative.
appendCode(o_code, o_locs, codes[1], locs[1]);
@ -457,7 +459,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
o_code.push_back(Instruction::PUSH);
o_locs.push_back((unsigned)o_code.size());
o_code.push_back(startLocation);
o_code.push_back(Instruction::JMP);
o_code.push_back(Instruction::JUMP);
// At end now.
o_code[endInsertion] = o_code.size();
@ -512,7 +514,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
o_code.push_back(datan);
}
}
o_code.push_back(Instruction::MKTX);
o_code.push_back(Instruction::CALL);
outs = 0;
}
}
@ -577,7 +579,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
// Jump to end...
o_code.push_back(Instruction::NOT);
o_code.push_back(Instruction::JMPI);
o_code.push_back(Instruction::JUMPI);
}
o_code.push_back(Instruction::POP);
}
@ -630,7 +632,7 @@ static int compileLispFragment(char const*& d, char const* e, bool _quiet, u256s
appendCode(o_code, o_locs, codes[i - 1], locs[i - 1]);
// Jump to end...
o_code.push_back(Instruction::JMPI);
o_code.push_back(Instruction::JUMPI);
}
o_code.push_back(Instruction::POP);
}

25
libethereum/Instruction.h

@ -50,6 +50,17 @@ enum class Instruction: uint8_t
OR,
XOR,
SHA3,
PUSH,
POP,
DUP,
SWAP,
MLOAD,
MSTORE,
MSTORE8,
SLOAD,
SSTORE,
JUMP,
JUMPI, //0x1d
ADDRESS = 0x20, ///< pushes the transaction sender
BALANCE,
ORIGIN, ///< pushes the transaction sender
@ -57,7 +68,7 @@ enum class Instruction: uint8_t
CALLVALUE, ///< pushes the transaction value
CALLDATA, ///< pushes the transaction value
CALLDATASIZE, ///< pushes the transaction value
BASEFEE,
GASPRICE,
MEMSIZE,
PREVHASH, ///< pushes the hash of the previous block (NOT the current one since that's impossible!)
PREVNONCE,
@ -65,18 +76,8 @@ enum class Instruction: uint8_t
TIMESTAMP, ///< pushes the timestamp of the current block
NUMBER, ///< pushes the current block number
DIFFICULTY, ///< pushes the difficulty of the current block
PUSH = 0x30,
POP,
DUP,
SWAP,
MLOAD,
MSTORE,
MSTORE8,
SLOAD,
SSTORE,
JUMP,
JUMPI,
PC,
CREATE = 0x30,
CALL,
RETURN,
SUICIDE = 0x3f

212
libethereum/State.cpp

@ -72,7 +72,6 @@ State::State(Address _coinbaseAddress, Overlay const& _db):
m_ourAddress(_coinbaseAddress)
{
m_blockReward = 1500 * finney;
m_fees.setMultiplier(100 * szabo);
secp256k1_start();
@ -98,7 +97,6 @@ State::State(State const& _s):
m_currentBlock(_s.m_currentBlock),
m_currentNumber(_s.m_currentNumber),
m_ourAddress(_s.m_ourAddress),
m_fees(_s.m_fees),
m_blockReward(_s.m_blockReward)
{
}
@ -114,15 +112,19 @@ State& State::operator=(State const& _s)
m_currentBlock = _s.m_currentBlock;
m_currentNumber = _s.m_currentNumber;
m_ourAddress = _s.m_ourAddress;
m_fees = _s.m_fees;
m_blockReward = _s.m_blockReward;
return *this;
}
void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const
{
auto it = m_cache.find(_a);
if (it == m_cache.end())
ensureCached(m_cache, _a, _requireMemory, _forceCreate);
}
void State::ensureCached(std::map<Address, AddressState>& _cache, Address _a, bool _requireMemory, bool _forceCreate) const
{
auto it = _cache.find(_a);
if (it == _cache.end())
{
// populate basic info.
string stateBack = m_state.at(_a);
@ -137,7 +139,7 @@ void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) con
else
s = AddressState(state[0].toInt<u256>(), state[1].toInt<u256>(), state[2].toHash<h256>());
bool ok;
tie(it, ok) = m_cache.insert(make_pair(_a, s));
tie(it, ok) = _cache.insert(make_pair(_a, s));
}
if (_requireMemory && !it->second.haveMemory())
{
@ -595,85 +597,97 @@ void State::execute(bytesConstRef _rlp)
{
// Entry point for a user-executed transaction.
Transaction t(_rlp);
executeBare(t, t.sender());
// Add to the user-originated transactions that we've executed.
// NOTE: Here, contract-originated transactions will not get added to the transaction list.
// If this is wrong, move this line into execute(Transaction const& _t, Address _sender) and
// don't forget to allow unsigned transactions in the tx list if they concur with the script execution.
m_transactions.push_back(t);
m_transactionSet.insert(t.sha3());
}
void State::applyRewards(Addresses const& _uncleAddresses)
{
u256 r = m_blockReward;
for (auto const& i: _uncleAddresses)
{
addBalance(i, m_blockReward * 3 / 4);
r += m_blockReward / 8;
}
addBalance(m_currentBlock.coinbaseAddress, r);
}
auto sender = t.sender();
void State::unapplyRewards(Addresses const& _uncleAddresses)
{
u256 r = m_blockReward;
for (auto const& i: _uncleAddresses)
// Avoid invalid transactions.
auto nonceReq = transactionsFrom(sender);
if (t.nonce != nonceReq)
{
subBalance(i, m_blockReward * 3 / 4);
r += m_blockReward / 8;
clog(StateChat) << "Invalid Nonce.";
throw InvalidNonce(nonceReq, t.nonce);
}
subBalance(m_currentBlock.coinbaseAddress, r);
}
void State::executeBare(Transaction const& _t, Address _sender)
{
#if ETH_DEBUG
commit();
clog(StateChat) << "State:" << rootHash();
clog(StateChat) << "Executing TX:" << _t;
#endif
// Entry point for a contract-originated transaction.
// Ignore invalid transactions.
auto nonceReq = transactionsFrom(_sender);
if (_t.nonce != nonceReq)
u256 gasCost;
if (t.isCreation)
{
clog(StateChat) << "Invalid Nonce.";
throw InvalidNonce(nonceReq, _t.nonce);
}
unsigned nonZeroData = 0;
for (auto i: _t.data)
unsigned nonZero = 0;
for (auto i: t.storage)
if (i)
nonZeroData++;
u256 fee = _t.receiveAddress ? m_fees.m_txFee : (nonZeroData * m_fees.m_memoryFee + m_fees.m_newContractFee);
nonZero++;
gasCost = nonZero * c_sstoreGas + c_createGas;
}
else
gasCost = t.data.size() * c_txDataGas + c_callGas + t.gas;
u256 cost = t.value + gasCost * t.gasPrice;
// Not considered invalid - just pointless.
if (balance(_sender) < _t.value + fee)
// Avoid unaffordable transactions.
if (balance(sender) < cost)
{
clog(StateChat) << "Not enough cash.";
throw NotEnoughCash();
}
if (_t.receiveAddress)
if (t.isCreation)
{
Address newAddress = right160(t.sha3());
while (isContractAddress(newAddress) || isNormalAddress(newAddress))
newAddress = (u160)newAddress + 1;
// Increment associated nonce for sender.
noteSending(_sender);
noteSending(sender);
// Pay...
subBalance(_sender, _t.value + fee);
addBalance(_t.receiveAddress, _t.value);
// Pay out of sender...
subBalance(sender, cost);
if (isContractAddress(_t.receiveAddress))
// Set up new account...
m_cache[newAddress] = AddressState(t.value, 0, AddressType::Contract);
auto& mem = m_cache[newAddress].memory();
for (uint i = 0; i < t.data.size(); ++i)
mem[i] = t.storage[i];
}
else
{
// Increment associated nonce for sender.
noteSending(sender);
// Pay...
subBalance(sender, cost);
addBalance(t.receiveAddress, t.value);
u256 gas = t.gas;
if (isContractAddress(t.receiveAddress))
// Once we get here, there's no going back.
call(t.receiveAddress, sender, t.value, t.gasPrice, bytesConstRef(&t.data), &gas, bytesRef());
addBalance(t.receiveAddress, gas * t.gasPrice);
}
// Add to the user-originated transactions that we've executed.
// NOTE: Here, contract-originated transactions will not get added to the transaction list.
// If this is wrong, move this line into execute(Transaction const& _t, Address _sender) and
// don't forget to allow unsigned transactions in the tx list if they concur with the script execution.
m_transactions.push_back(t);
m_transactionSet.insert(t.sha3());
}
bool State::call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out)
{
VM vm(*_gas);
ExtVM evm(*this, _myAddress, _txSender, _txValue, _gasPrice, _txData);
bool revert = false;
try
{
MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction.
execute(_t.receiveAddress, _sender, _t.value, _t.data, &feeAdder.fee);
auto out = vm.go(evm);
memcpy(_out.data(), out.data(), std::min(out.size(), _out.size()));
}
catch (OutOfGas const& _e)
{
clog(StateChat) << "Out of Gas! Reverting.";
revert = true;
}
catch (VMException const& _e)
{
@ -687,41 +701,59 @@ void State::executeBare(Transaction const& _t, Address _sender)
{
clog(StateChat) << "std::exception in VM: " << _e.what();
}
}
}
else
{
Address newAddress = right160(_t.sha3());
if (isContractAddress(newAddress) || isNormalAddress(newAddress))
{
clog(StateChat) << "Contract address collision.";
throw ContractAddressCollision();
}
// Write state out only in the case of a non-excepted transaction.
if (revert)
evm.revert();
// Increment associated nonce for sender.
noteSending(_sender);
*_gas = vm.gas();
// Pay out of sender...
subBalance(_sender, _t.value + fee);
return !revert;
}
h160 State::create(Address _txSender, u256 _endowment, u256 _gasPrice, vector_ref<h256 const> _storage)
{
Transaction t;
for (auto const& i: _storage)
t.storage.push_back((u256)i);
t.value = _endowment;
t.gasPrice = _gasPrice;
t.nonce = transactionsFrom(_txSender);
Address newAddress = right160(t.sha3());
while (isContractAddress(newAddress) || isNormalAddress(newAddress))
newAddress = (u160)newAddress + 1;
// Increment associated nonce for sender.
noteSending(_txSender);
// Set up new account...
m_cache[newAddress] = AddressState(_t.value, 0, AddressType::Contract);
m_cache[newAddress] = AddressState(_endowment, 0, AddressType::Contract);
auto& mem = m_cache[newAddress].memory();
for (uint i = 0; i < _t.data.size(); ++i)
mem[i] = _t.data[i];
}
for (uint i = 0; i < _storage.size(); ++i)
mem[i] = (u256)_storage[i];
#if ETH_DEBUG
commit();
clog(StateChat) << "New state:" << rootHash();
#endif
return newAddress;
}
void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* _totalFee)
void State::applyRewards(Addresses const& _uncleAddresses)
{
VM vm;
ExtVM evm(*this, _myAddress, _txSender, _txValue, _txData);
vm.go(evm);
*_totalFee = vm.runFee();
u256 r = m_blockReward;
for (auto const& i: _uncleAddresses)
{
addBalance(i, m_blockReward * 3 / 4);
r += m_blockReward / 8;
}
addBalance(m_currentBlock.coinbaseAddress, r);
}
void State::unapplyRewards(Addresses const& _uncleAddresses)
{
u256 r = m_blockReward;
for (auto const& i: _uncleAddresses)
{
subBalance(i, m_blockReward * 3 / 4);
r += m_blockReward / 8;
}
subBalance(m_currentBlock.coinbaseAddress, r);
}

58
libethereum/State.h

@ -119,6 +119,7 @@ public:
bool cull(TransactionQueue& _tq) const;
/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.
void execute(bytes const& _rlp) { return execute(&_rlp); }
void execute(bytesConstRef _rlp);
@ -170,26 +171,21 @@ public:
u256 playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit);
/// Get the fee associated for a contract created with the given data.
u256 fee(uint _dataCount) const { return m_fees.m_memoryFee * _dataCount + m_fees.m_newContractFee; }
u256 createGas(uint _nonZeroStorageCount) const { return c_sstoreGas * _nonZeroStorageCount + c_createGas; }
/// Get the fee associated for a normal transaction.
u256 fee() const { return m_fees.m_txFee; }
u256 callGas(uint _dataCount, u256 _gas = 0) const { return c_txDataGas * _dataCount + c_callGas + _gas; }
private:
/// Fee-adder on destruction RAII class.
struct MinerFeeAdder
{
~MinerFeeAdder() { /*state->addBalance(state->m_currentBlock.coinbaseAddress, fee);*/ } // No fees paid now.
State* state;
u256 fee;
};
/// Retrieve all information about a given address into the cache.
/// If _requireMemory is true, grab the full memory should it be a contract item.
/// If _forceCreate is true, then insert a default item into the cache, in the case it doesn't
/// exist in the DB.
void ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const;
/// Retrieve all information about a given address into a cache.
void ensureCached(std::map<Address, AddressState>& _cache, Address _a, bool _requireMemory, bool _forceCreate) const;
/// Commit all changes waiting in the address cache to the DB.
void commit();
@ -201,12 +197,16 @@ private:
/// Throws on failure.
u256 playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit);
/// Execute a decoded transaction object, given a sender.
/// This will append @a _t to the transaction list and change the state accordingly.
void executeBare(Transaction const& _t, Address _sender);
// Two priviledged entry points for transaction processing used by the VM (these don't get added to the Transaction lists):
// We assume all instrinsic fees are paid up before this point.
/// Execute a contract transaction.
void execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* o_totalFee);
/// Execute a contract-creation transaction.
h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, vector_ref<h256 const> _storage);
/// Execute a call.
/// @a _gas points to the amount of gas to use for the call, and will lower it accordingly.
/// @returns false if the call ran out of gas before completion. true otherwise.
bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out);
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();
@ -236,7 +236,6 @@ private:
Dagger m_dagger;
FeeStructure m_fees;
u256 m_blockReward;
static std::string c_defaultPath;
@ -247,8 +246,8 @@ private:
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)
ExtVM(State& _s, Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData):
ExtVMFace(_myAddress, _txSender, _txValue, _gasPrice, _txData, _s.m_previousBlock, _s.m_currentBlock, _s.m_currentNumber), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
m_store = &(m_s.m_cache[_myAddress].memory());
@ -267,30 +266,33 @@ public:
m_store->erase(_n);
}
void payFee(bigint _f)
h160 create(Address _txSender, u256 _endowment, vector_ref<h256 const> _storage)
{
if (_f > m_s.balance(myAddress))
throw NotEnoughCash();
m_s.subBalance(myAddress, _f);
return m_s.create(_txSender, _endowment, gasPrice, _storage);
}
void mktx(Transaction& _t)
bool call(Address _myAddress, Address _txSender, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out)
{
_t.nonce = m_s.transactionsFrom(myAddress);
m_s.executeBare(_t, myAddress);
return m_s.call(_myAddress, _txSender, _txValue, gasPrice, _txData, _gas, _out);
}
u256 balance(Address _a) { return m_s.balance(_a); }
void subBalance(u256 _a) { m_s.subBalance(myAddress, _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.addBalance(_a, m_s.balance(myAddress));
m_s.m_cache[myAddress].kill();
}
void revert()
{
m_s.m_cache = m_origCache;
}
private:
State& m_s;
std::map<Address, AddressState> m_origCache;
std::map<u256, u256>* m_store;
};

30
libethereum/Transaction.cpp

@ -35,12 +35,24 @@ Transaction::Transaction(bytesConstRef _rlpData)
try
{
nonce = rlp[field = 0].toInt<u256>();
receiveAddress = rlp[field = 1].toHash<Address>();
value = rlp[field = 2].toInt<u256>();
data.reserve(rlp[field = 3].itemCountStrict());
value = rlp[field = 1].toInt<u256>();
gasPrice = rlp[field = 2].toInt<u256>();
isCreation = rlp[field = 3].isList();
if (isCreation)
{
storage.reserve(rlp[field = 3].itemCountStrict());
for (auto const& i: rlp[3])
data.push_back(i.toInt<u256>());
vrs = Signature{ rlp[field = 4].toInt<byte>(), rlp[field = 5].toInt<u256>(), rlp[field = 6].toInt<u256>() };
storage.push_back(i.toInt<u256>());
}
else
{
receiveAddress = rlp[3].toHash<Address>();
gas = rlp[field = 4].toInt<u256>();
data.reserve(rlp[field = 5].itemCountStrict());
for (auto const& i: rlp[5])
data.push_back(i.toInt<byte>());
}
vrs = Signature{ rlp[field = 6].toInt<byte>(), rlp[field = 7].toInt<u256>(), rlp[field = 8].toInt<u256>() };
}
catch (RLPException const&)
{
@ -112,8 +124,12 @@ void Transaction::sign(Secret _priv)
void Transaction::fillStream(RLPStream& _s, bool _sig) const
{
_s.appendList(_sig ? 7 : 4);
_s << nonce << receiveAddress << value << data;
_s.appendList((_sig ? 3 : 0) + (isCreation ? 4 : 6));
_s << nonce << value << gasPrice;
if (isCreation)
_s << storage;
else
_s << receiveAddress << gas << data;
if (_sig)
_s << vrs.v << vrs.r << vrs.s;
}

36
libethereum/Transaction.h

@ -34,7 +34,9 @@ struct Signature
u256 s;
};
// [ nonce, receiving_address, value, [ data item 0, data item 1 ... data item n ], v, r, s ]
// [ nonce, value, baseFee, receiving_address, gas_deposit, [ data byte 0, data byte 1, ... ], v, r, s ]
// or
// [ nonce, endowment, baseFee, [ storage code 1, storage code 2, ... ], v, r, s ]
struct Transaction
{
Transaction() {}
@ -44,15 +46,23 @@ struct Transaction
bool operator==(Transaction const& _c) const { return receiveAddress == _c.receiveAddress && value == _c.value && data == _c.data; }
bool operator!=(Transaction const& _c) const { return !operator==(_c); }
u256 nonce;
Address receiveAddress;
u256 value;
u256s data;
Signature vrs;
u256 nonce; ///< The transaction-count of the sender.
bool isCreation; ///< Is this a contract-creation transaction?
u256 value; ///< The amount of ETH to be transferred by this transaction. Called 'endowment' for contract-creation transactions.
u256 gasPrice; ///< The base fee and thus the implied exchange rate of ETH to GAS.
Signature vrs; ///< The signature of the transaction. Encodes the sender.
Address safeSender() const noexcept;
Address sender() const;
void sign(Secret _priv);
// if isCreation:
u256s storage; ///< The initial storage of the contract.
// if !isCreation:
u256 gas; ///< The total gas to convert, paid for from sender's account. Any unused gas gets refunded once the contract is ended.
Address receiveAddress; ///< The receiving address of the transaction.
bytes data; ///< The data associated with the transaction.
Address safeSender() const noexcept; ///< Like sender() but will never throw.
Address sender() const; ///< Determine the sender of the transaction from the signature (and hash).
void sign(Secret _priv); ///< Sign the transaction.
static h256 kFromMessage(h256 _msg, h256 _priv);
@ -68,12 +78,12 @@ using Transactions = std::vector<Transaction>;
inline std::ostream& operator<<(std::ostream& _out, Transaction const& _t)
{
_out << "{";
if (_t.receiveAddress)
_out << _t.receiveAddress.abridged();
else
if (_t.isCreation)
_out << "[CREATE]";
else
_out << _t.receiveAddress.abridged();
_out << "/" << _t.nonce << "*" << _t.value;
_out << "/" << _t.nonce << "$" << _t.value << "+" << _t.gas << "@" << _t.gasPrice;
Address s;
try
{

8
libethereum/VM.cpp

@ -24,13 +24,9 @@
using namespace std;
using namespace eth;
VM::VM()
{
reset();
}
void VM::reset()
void VM::reset(u256 _gas)
{
m_gas = _gas;
m_curPC = 0;
m_nextPC = 1;
m_stepCount = 0;

215
libethereum/VM.h

@ -22,8 +22,7 @@
#pragma once
#include <unordered_map>
#include "CryptoHeaders.h"
#include "Common.h"
#include "CommonEth.h"
#include "Exceptions.h"
#include "FeeStructure.h"
#include "Instruction.h"
@ -53,21 +52,24 @@ class VM
public:
/// Construct VM object.
VM();
explicit VM(u256 _gas = 0) { reset(_gas); }
void reset();
void reset(u256 _gas = 0);
template <class Ext>
void go(Ext& _ext, uint64_t _steps = (uint64_t)-1);
bytesConstRef go(Ext& _ext, uint64_t _steps = (uint64_t)-1);
void require(u256 _n) { if (m_stack.size() < _n) throw StackTooSmall(_n, m_stack.size()); }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 runFee() const { return m_runFee; }
u256 gas() const { return m_gas; }
private:
u256 m_gas = 0;
u256 m_curPC = 0;
u256 m_nextPC = 1;
uint64_t m_stepCount = 0;
std::map<u256, u256> m_temp;
bytes m_temp;
std::vector<u256> m_stack;
u256 m_runFee = 0;
};
@ -75,7 +77,7 @@ private:
}
// INLINE:
template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
template <class Ext> eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps)
{
for (bool stopped = false; !stopped && _steps--; m_curPC = m_nextPC, m_nextPC = m_curPC + 1)
{
@ -88,35 +90,98 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
Instruction inst = (Instruction)(uint8_t)rawInst;
// FEES...
bigint runFee = m_stepCount > 16 ? _ext.fees.m_stepFee : 0;
bigint storeCostDelta = 0;
bigint runGas = c_stepGas;
unsigned newTempSize = m_temp.size();
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...
runGas = c_sstoreGas * 2;
else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2])
runGas = 0;
else
runGas = c_sstoreGas;
break;
case Instruction::SLOAD:
runFee += _ext.fees.m_dataFee;
runGas += c_sloadGas;
break;
// These all operate on memory and therefore potentially expand it:
case Instruction::MSTORE:
require(2);
newTempSize = (unsigned)m_stack.back() + 32;
break;
case Instruction::MSTORE8:
require(2);
newTempSize = (unsigned)m_stack.back() + 1;
break;
case Instruction::MLOAD:
require(1);
newTempSize = (unsigned)m_stack.back() + 32;
break;
case Instruction::RETURN:
require(2);
newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 2];
break;
case Instruction::SHA3:
require(2);
runGas = c_sha3Gas;
newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 2];
break;
case Instruction::CALLDATA:
require(2);
newTempSize = (unsigned)m_stack.back() + (unsigned)m_stack[m_stack.size() - 2];
break;
case Instruction::BALANCE:
runFee += _ext.fees.m_extroFee;
runGas = c_balanceGas;
break;
case Instruction::CALL:
runFee += _ext.fees.m_txFee;
require(7);
runGas = c_callGas + (unsigned)m_stack[m_stack.size() - 3];
newTempSize = std::max((unsigned)m_stack[m_stack.size() - 6] + (unsigned)m_stack[m_stack.size() - 7], (unsigned)m_stack[m_stack.size() - 4] + (unsigned)m_stack[m_stack.size() - 5]);
break;
case Instruction::CREATE:
{
require(3);
u256 gas = (unsigned)m_stack[m_stack.size() - 1];
unsigned inOff = (unsigned)m_stack[m_stack.size() - 2];
unsigned inSize = (unsigned)m_stack[m_stack.size() - 3];
newTempSize = inOff + inSize;
unsigned wc = std::min<unsigned>(inSize / 32 * 32 + inOff, m_temp.size());
unsigned nonZero = 0;
for (unsigned i = inOff; i < wc; i += 32)
if (!!*(h256*)(m_temp.data() + inOff))
nonZero++;
runGas += c_createGas + nonZero * c_sstoreGas + gas;
break;
}
default:
break;
}
// TODO: payFee should deduct from origin.
_ext.payFee(runFee + storeCostDelta);
m_runFee += (u256)runFee;
newTempSize = (newTempSize + 31) / 32 * 32;
if (newTempSize > m_temp.size())
runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32;
if (m_gas < runGas)
{
// Out of gas!
m_gas = 0;
throw OutOfGas();
}
m_gas = (u256)((bigint)m_gas - runGas);
m_temp.resize(newTempSize);
// EXECUTE...
switch (inst)
@ -141,28 +206,28 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
case Instruction::DIV:
require(2);
if (!m_stack[m_stack.size() - 2])
return;
return bytesConstRef();
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);
if (!m_stack[m_stack.size() - 2])
return;
return bytesConstRef();
(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);
if (!m_stack[m_stack.size() - 2])
return;
return bytesConstRef();
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);
if (!m_stack[m_stack.size() - 2])
return;
return bytesConstRef();
(s256&)m_stack[m_stack.size() - 2] = (s256&)m_stack.back() % (s256&)m_stack[m_stack.size() - 2];
m_stack.pop_back();
break;
@ -203,21 +268,12 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
break;
case Instruction::SHA3:
{
require(1);
uint s = (uint)std::min(m_stack.back(), (u256)(m_stack.size() - 1) * 32);
require(2);
unsigned inOff = (unsigned)m_stack.back();
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
unsigned inSize = (unsigned)m_stack.back();
m_stack.pop_back();
}
std::array<byte, 32> final;
digest.TruncatedFinal(final.data(), 32);
m_stack.push_back(fromBigEndian<u256>(final));
m_stack.push_back(sha3(bytesConstRef(m_temp.data() + inOff, inSize)));
break;
}
case Instruction::ADDRESS:
@ -240,13 +296,19 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
m_stack.push_back(_ext.txValue);
break;
case Instruction::CALLDATA:
// TODO: write data from ext into memory.
{
unsigned inOff = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned inSize = (unsigned)m_stack.back();
m_stack.pop_back();
memcpy(m_temp.data() + inOff, _ext.txData.data(), std::min<unsigned>(_ext.txData.size(), inSize));
break;
}
case Instruction::CALLDATASIZE:
m_stack.push_back(_ext.txData.size());
break;
case Instruction::BASEFEE:
m_stack.push_back(_ext.fees.multiplier());
case Instruction::GASPRICE:
m_stack.push_back(_ext.gasPrice);
break;
case Instruction::PREVHASH:
m_stack.push_back(_ext.previousBlock.hash);
@ -312,13 +374,13 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
case Instruction::MLOAD:
{
require(1);
m_stack.back() = m_temp[m_stack.back()];
m_stack.back() = (u256)*(h256 const*)(m_temp.data() + (unsigned)m_stack.back());
break;
}
case Instruction::MSTORE:
{
require(2);
m_temp[m_stack.back()] = m_stack[m_stack.size() - 2];
*(h256*)&m_temp[(unsigned)m_stack.back()] = (h256)m_stack[m_stack.size() - 2];
m_stack.pop_back();
m_stack.pop_back();
break;
@ -326,7 +388,7 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
case Instruction::MSTORE8:
{
require(2);
m_temp[m_stack.back()] = m_stack[m_stack.size() - 2];
m_temp[(unsigned)m_stack.back()] = (byte)(m_stack[m_stack.size() - 2] & 0xff);
m_stack.pop_back();
m_stack.pop_back();
break;
@ -356,34 +418,72 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
case Instruction::PC:
m_stack.push_back(m_curPC);
break;
case Instruction::CREATE:
{
require(3);
u256 endowment = m_stack.back();
m_stack.pop_back();
unsigned inOff = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned inSize = (unsigned)m_stack.back();
m_stack.pop_back();
if (_ext.balance(_ext.myAddress) >= endowment)
{
_ext.subBalance(endowment);
m_stack.push_back((u160)_ext.create(_ext.myAddress, endowment, vector_ref<h256 const>((h256 const*)(m_temp.data() + inOff), inSize / 32)));
}
else
m_stack.push_back(0);
break;
}
case Instruction::CALL:
{
require(6);
require(7);
Transaction t;
t.receiveAddress = asAddress(m_stack.back());
u256 gas = m_stack.back();
if (!gas)
{
gas = m_gas;
m_gas = 0;
}
m_stack.pop_back();
t.value = m_stack.back();
u160 receiveAddress = asAddress(m_stack.back());
m_stack.pop_back();
u256 value = m_stack.back();
m_stack.pop_back();
auto itemCount = m_stack.back();
unsigned inOff = (unsigned)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());
unsigned inSize = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned outOff = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned outSize = (unsigned)m_stack.back();
m_stack.pop_back();
if (_ext.balance(_ext.myAddress) >= value)
{
_ext.subBalance(value);
m_stack.push_back(_ext.call(receiveAddress, _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize)));
}
_ext.mktx(t);
m_gas += gas;
break;
}
case Instruction::RETURN:
{
require(2);
// TODO: write data from memory into ext.
return;
unsigned b = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned s = (unsigned)m_stack.back();
m_stack.pop_back();
return bytesConstRef(m_temp.data() + b, s);
}
case Instruction::SUICIDE:
{
require(1);
@ -392,12 +492,13 @@ template <class Ext> void eth::VM::go(Ext& _ext, uint64_t _steps)
// ...follow through to...
}
case Instruction::STOP:
return;
return bytesConstRef();
default:
throw BadInstruction();
}
}
if (_steps == (unsigned)-1)
throw StepsDone();
return true;
}

6
test/vm.cpp

@ -39,7 +39,7 @@ class FakeExtVM: public ExtVMFace
public:
FakeExtVM()
{}
FakeExtVM(FeeStructure const& _fees, BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
FakeExtVM(BlockInfo const& _previousBlock, BlockInfo const& _currentBlock, uint _currentNumber):
ExtVMFace(Address(), Address(), 0, u256s(), _fees, _previousBlock, _currentBlock, _currentNumber)
{}
@ -51,7 +51,7 @@ public:
{
get<3>(addresses[myAddress])[_n] = _v;
}
void mktx(Transaction& _t)
void transact(Transaction& _t)
{
if (get<0>(addresses[myAddress]) >= _t.value)
{
@ -359,8 +359,6 @@ public:
cb.difficulty = 256;
cb.timestamp = 1;
cb.coinbaseAddress = toAddress(sha3("coinbase"));
FeeStructure fees;
fees.setMultiplier(1);
FakeExtVM fev(fees, pb, cb, 0);
fev.setContract(toAddress(sha3("contract")), ether, 0, compileLisp("(suicide (txsender))"));
o["env"] = fev.exportEnv();

Loading…
Cancel
Save