Browse Source

Some repotting.

cl-refactor
Gav Wood 11 years ago
parent
commit
0349f7bf05
  1. 10
      libethereum/AddressState.cpp
  2. 48
      libethereum/AddressState.h
  3. 39
      libethereum/BlockInfo.cpp
  4. 26
      libethereum/BlockInfo.h
  5. 18
      libethereum/Common.h
  6. 27
      libethereum/Exceptions.h
  7. 62
      libethereum/Instruction.h
  8. 71
      libethereum/State.cpp
  9. 197
      libethereum/State.h
  10. 4
      libethereum/Trie.h

10
libethereum/AddressState.cpp

@ -0,0 +1,10 @@
#include "Trie.h"
#include "AddressState.h"
using namespace std;
using namespace eth;
u256 AddressState::memoryHash() const
{
return hash256(m_memory);
}

48
libethereum/AddressState.h

@ -0,0 +1,48 @@
#pragma once
#include "Common.h"
#include "RLP.h"
namespace eth
{
enum class AddressType
{
Normal,
Contract
};
class AddressState
{
public:
AddressState(AddressType _type = AddressType::Normal): m_type(_type), m_balance(0), m_nonce(0) {}
AddressType type() const { return m_type; }
u256& balance() { return m_balance; }
u256 const& balance() const { return m_balance; }
u256& nonce() { return m_nonce; }
u256 const& nonce() const { return m_nonce; }
std::map<u256, u256>& memory() { assert(m_type == AddressType::Contract); return m_memory; }
std::map<u256, u256> const& memory() const { assert(m_type == AddressType::Contract); return m_memory; }
u256 memoryHash() const;
std::string toString() const
{
if (m_type == AddressType::Normal)
return rlpList(m_balance, toCompactBigEndianString(m_nonce));
if (m_type == AddressType::Contract)
return rlpList(m_balance, toCompactBigEndianString(m_nonce), toCompactBigEndianString(memoryHash()));
return "";
}
private:
AddressType m_type;
u256 m_balance;
u256 m_nonce;
u256Map m_memory;
};
}

39
libethereum/BlockInfo.cpp

@ -0,0 +1,39 @@
#include "RLP.h"
#include "BlockInfo.h"
using namespace std;
using namespace eth;
void populateAndVerify(bytesConstRef _block, u256 _number)
{
number = _number;
RLP root(_block);
try
{
RLP header = root[0];
hash = eth::sha256(_block);
parentHash = header[0].toFatInt();
sha256Uncles = header[1].toFatInt();
coinbaseAddress = header[2].toFatInt();
sha256Transactions = header[3].toFatInt();
difficulty = header[4].toFatInt();
timestamp = header[5].toFatInt();
nonce = header[6].toFatInt();
}
catch (RLP::BadCast)
{
throw InvalidBlockFormat();
}
if (sha256Transactions != sha256(root[1].data()))
throw InvalidTransactionsHash();
if (sha256Uncles != sha256(root[2].data()))
throw InvalidUnclesHash();
// TODO: check timestamp.
// TODO: check difficulty against timestamp.
// TODO: check proof of work.
// TODO: check each transaction.
}

26
libethereum/BlockInfo.h

@ -0,0 +1,26 @@
#pragma once
#include "Common.h"
namespace eth
{
struct BlockInfo
{
public:
u256 hash;
u256 parentHash;
u256 sha256Uncles;
u256 coinbaseAddress;
u256 sha256Transactions;
u256 difficulty;
u256 timestamp;
u256 nonce;
u256 number;
void populateAndVerify(bytesConstRef _block, u256 _number);
};
}

18
libethereum/Common.h

@ -1,5 +1,7 @@
#pragma once
#include <map>
#include <string>
#include <cassert>
#include <random>
#include <sstream>
@ -27,6 +29,10 @@ using sint = int64_t;
using u256s = std::vector<u256>;
using u160s = std::vector<u160>;
using StringMap = std::map<std::string, std::string>;
using u256Map = std::map<u256, u256>;
using HexMap = std::map<bytes, std::string>;
template <class _T> std::string toString(_T const& _t) { std::ostringstream o; o << _t; return o.str(); }
template <class _T> inline std::string asHex(_T const& _data, int _w = 2)
@ -143,6 +149,18 @@ inline std::string randomWord()
return ret;
}
template <class _T>
inline u160 low160(_T const& _t)
{
return (u160)(_t & ((((_T)1) << 160) - 1));
}
template <class _T>
inline u160 as160(_T const& _t)
{
return (u160)(_t & ((((_T)1) << 160) - 1));
}
template <class _T> inline std::vector<_T>& operator+=(std::vector<_T>& _a, std::vector<_T> const& _b)
{
auto s = _a.size();

27
libethereum/Exceptions.h

@ -0,0 +1,27 @@
#pragma once
#include <exception>
#include "Common.h"
namespace eth
{
class NotEnoughCash: public std::exception {};
class BadInstruction: public std::exception {};
class StackTooSmall: public std::exception { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
class OperandOutOfRange: public std::exception { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; };
class ExecutionException: public std::exception {};
class NoSuchContract: public std::exception {};
class ContractAddressCollision: public std::exception {};
class FeeTooSmall: public std::exception {};
class InvalidSignature: public std::exception {};
class InvalidTransactionFormat: public std::exception {};
class InvalidBlockFormat: public std::exception {};
class InvalidUnclesHash: public std::exception {};
class InvalidTransactionsHash: public std::exception {};
class InvalidTransaction: public std::exception {};
class InvalidDifficulty: public std::exception {};
class InvalidTimestamp: public std::exception {};
class InvalidNonce: public std::exception {};
}

62
libethereum/Instruction.h

@ -0,0 +1,62 @@
#pragma once
namespace eth
{
// TODO: Update comments.
/// Virtual machine bytecode instruction.
enum class Instruction: uint8_t
{
STOP = 0x00, ///< halts execution
ADD, ///< Rx Ry Rz - sets Rz <- Rx + Ry mod 2^256
SUB, ///< Rx Ry Rz - sets Rz <- Rx - Ry mod 2^256
MUL, ///< Rx Ry Rz - sets Rz <- Rx * Ry mod 2^256
DIV, ///< Rx Ry Rz - sets Rz <- floor(Rx / Ry)
SDIV, ///< Rx Ry Rz - like DIV, except it treats values above 2^255 as negative (ie. 2^256 - x -> -x)
MOD, ///< Rx Ry Rz - sets Rz <- Rx mod Ry
SMOD, ///< Rx Ry Rz - like MOD, but for signed values just like SDIV (using Python's convention with negative numbers)
EXP, ///< Rx Ry Rz - sets Rz <- Rx ^ Ry mod 2^256
NEG, ///< Rx Ry - sets Ry <- 2^256 - Rx
LT, ///< Rx Ry Rz - sets Rz <- 1 if Rx < Ry else 0
LE, ///< Rx Ry Rz - sets Rz <- 1 if Rx <= Ry else 0
GT, ///< Rx Ry Rz - sets Rz <- 1 if Rx > Ry else 0
GE, ///< Rx Ry Rz - sets Rz <- 1 if Rx >= Ry else 0
EQ, ///< Rx Ry Rz - sets Rz <- 1 if Rx = Ry else 0
NOT, ///< Rx Ry - sets Ry <- 1 if Rx = 0 else 0
MYADDRESS = 0x10, ///< Rx - sets Rx to the contract's own address
TXSENDER, ///< pushes the transaction sender
TXVALUE , ///< pushes the transaction value
TXFEE, ///< pushes the transaction fee
TXDATAN, ///< pushes the number of data items
TXDATA, ///< pops one item and pushes data item S[-1], or zero if index out of range
BLK_PREVHASH, ///< pushes the hash of the previous block (NOT the current one since that's impossible!)
BLK_COINBASE, ///< pushes the coinbase of the current block
BLK_TIMESTAMP, ///< pushes the timestamp of the current block
BLK_NUMBER, ///< pushes the current block number
BLK_DIFFICULTY, ///< pushes the difficulty of the current block
SHA256 = 0x20, ///< sets Ry <- SHA256(Rx)
RIPEMD160, ///< Rx Ry - sets Ry <- RIPEMD160(Rx)
ECMUL, ///< Rx Ry Rz Ra Rb - sets (Ra, Rb) = Rz * (Rx, Ry) in secp256k1, using (0,0) for the point at infinity
ECADD, ///< Rx Ry Rz Ra Rb Rc - sets (Rb, Rc) = (Rx, Ry) + (Ra, Rb)
ECSIGN, ///< Rx Ry Rz Ra Rb - sets(Rz, Ra, Rb)as the(r,s,prefix)values of an Electrum-style RFC6979 deterministic signature ofRxwith private keyRy`
ECRECOVER, ///< Rx Ry Rz Ra Rb Rc - sets(Rb, Rc)as the public key from the signature(Ry, Rz, Ra)of the message hashRx`
ECVALID, ///< Rx Ry Rz Ra Rb Rc - sets(Rb, Rc)as the public key from the signature(Ry, Rz, Ra)of the message hashRx`
PUSH = 0x30,
POP,
DUP,
DUPN,
SWAP,
SWAPN,
LOAD,
STORE,
JMP = 0x40, ///< Rx - sets the index pointer to the value at Rx
JMPI, ///< Rx Ry - if Rx != 0, sets the index pointer to Ry
IND, ///< pushes the index pointer.
EXTRO = 0x50, ///< Rx Ry Rz - looks at the contract at address Rx and its memory state Ry, and outputs the result to Rz
BALANCE, ///< Rx - returns the ether balance of address Rx
MKTX = 0x60, ///< Rx Ry Rz Rw Rv - sends Ry ether to Rx plus Rz fee with Rw data items starting from memory index Rv (and then reading to (Rv + 1), (Rv + 2) etc). Note that if Rx = 0 then this creates a new contract.
SUICIDE = 0xff ///< Rx - destroys the contract and clears all memory, sending the entire balance plus the negative fee from clearing memory minus TXFEE to the address
};
}

71
libethereum/State.cpp

@ -1,5 +1,8 @@
#include <secp256k1.h>
#include <random>
#include "Trie.h"
#include "Instruction.h"
#include "Exceptions.h"
#include "sha256.h"
#include "State.h"
using namespace std;
@ -100,75 +103,53 @@ mt19937_64& State::engine()
return *s_engine;
}
bool State::execute(Transaction const& _t, Address _sender)
u256 State::contractMemory(Address _contract, u256 _memory) const
{
auto m = m_current.find(_contract);
if (m == m_current.end())
return 0;
auto i = m->second.memory().find(_memory);
return i == m->second.memory().end() ? 0 : i->second;
}
void State::execute(Transaction const& _t, Address _sender)
{
// Entry point for a contract-originated transaction.
if (_t.nonce != transactionsFrom(_sender))
{
// Nonce is wrong.
// Error reporting?
return false;
}
throw InvalidNonce();
if (balance(_sender) < _t.value + _t.fee)
{
// Sender balance too low.
// Error reporting?
return false;
}
throw NotEnoughCash();
if (_t.receiveAddress)
{
assert(subBalance(_sender, _t.value));
assert(subBalance(_sender, _t.value + _t.fee));
addBalance(_t.receiveAddress, _t.value);
addBalance(m_minerAddress, _t.fee);
if (isContractAddress(_t.receiveAddress))
{
u256 minerFee = 0;
try
{
execute(_t.receiveAddress, _sender, _t.value, _t.fee, _t.data, &minerFee);
addBalance(m_minerAddress, minerFee);
return true;
}
catch (...)
{
// Execution error.
// Error reporting?
addBalance(m_minerAddress, minerFee);
throw ExecutionException();
}
MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction.
execute(_t.receiveAddress, _sender, _t.value, _t.fee, _t.data, &feeAdder.fee);
}
else
return true;
}
else
{
if (_t.fee < _t.data.size() * c_memoryFee + c_newContractFee)
{
// Fee too small.
// Error reporting?
return false;
}
throw FeeTooSmall();
Address newAddress = low160(_t.sha256());
if (isContractAddress(newAddress))
{
// Contract collision.
// Error reporting?
return false;
}
throw ContractAddressCollision();
//
auto& mem = m_current[newAddress].memory();
for (uint i = 0; i < _t.data.size(); ++i)
mem[i] = _t.data[i];
assert(subBalance(_sender, _t.value));
assert(subBalance(_sender, _t.value + _t.fee));
addBalance(newAddress, _t.value);
return true;
addBalance(m_minerAddress, _t.fee);
}
}
@ -198,12 +179,6 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _
myMemory.erase(_n);
};
if (balance(_myAddress) < _txFee)
throw NotEnoughCash();
subBalance(_myAddress, _txFee);
*_totalFee += _txFee;
u256 curPC = 0;
u256 nextPC = 1;
u256 stepCount = 0;

197
libethereum/State.h

@ -1,188 +1,17 @@
#pragma once
#include <exception>
#include <array>
#include <map>
#include <unordered_map>
#include "Trie.h"
#include "Exceptions.h"
#include "AddressState.h"
#include "BlockInfo.h"
#include "RLP.h"
#include "Common.h"
namespace eth
{
/// Virtual machine bytecode instruction.
enum class Instruction: uint8_t
{
STOP = 0x00, ///< halts execution
ADD, ///< Rx Ry Rz - sets Rz <- Rx + Ry mod 2^256
SUB, ///< Rx Ry Rz - sets Rz <- Rx - Ry mod 2^256
MUL, ///< Rx Ry Rz - sets Rz <- Rx * Ry mod 2^256
DIV, ///< Rx Ry Rz - sets Rz <- floor(Rx / Ry)
SDIV, ///< Rx Ry Rz - like DIV, except it treats values above 2^255 as negative (ie. 2^256 - x -> -x)
MOD, ///< Rx Ry Rz - sets Rz <- Rx mod Ry
SMOD, ///< Rx Ry Rz - like MOD, but for signed values just like SDIV (using Python's convention with negative numbers)
EXP, ///< Rx Ry Rz - sets Rz <- Rx ^ Ry mod 2^256
NEG, ///< Rx Ry - sets Ry <- 2^256 - Rx
LT, ///< Rx Ry Rz - sets Rz <- 1 if Rx < Ry else 0
LE, ///< Rx Ry Rz - sets Rz <- 1 if Rx <= Ry else 0
GT, ///< Rx Ry Rz - sets Rz <- 1 if Rx > Ry else 0
GE, ///< Rx Ry Rz - sets Rz <- 1 if Rx >= Ry else 0
EQ, ///< Rx Ry Rz - sets Rz <- 1 if Rx = Ry else 0
NOT, ///< Rx Ry - sets Ry <- 1 if Rx = 0 else 0
MYADDRESS = 0x10, ///< Rx - sets Rx to the contract's own address
TXSENDER, ///< pushes the transaction sender
TXVALUE , ///< pushes the transaction value
TXFEE, ///< pushes the transaction fee
TXDATAN, ///< pushes the number of data items
TXDATA, ///< pops one item and pushes data item S[-1], or zero if index out of range
BLK_PREVHASH, ///< pushes the hash of the previous block (NOT the current one since that's impossible!)
BLK_COINBASE, ///< pushes the coinbase of the current block
BLK_TIMESTAMP, ///< pushes the timestamp of the current block
BLK_NUMBER, ///< pushes the current block number
BLK_DIFFICULTY, ///< pushes the difficulty of the current block
SHA256 = 0x20, ///< sets Ry <- SHA256(Rx)
RIPEMD160, ///< Rx Ry - sets Ry <- RIPEMD160(Rx)
ECMUL, ///< Rx Ry Rz Ra Rb - sets (Ra, Rb) = Rz * (Rx, Ry) in secp256k1, using (0,0) for the point at infinity
ECADD, ///< Rx Ry Rz Ra Rb Rc - sets (Rb, Rc) = (Rx, Ry) + (Ra, Rb)
ECSIGN, ///< Rx Ry Rz Ra Rb - sets(Rz, Ra, Rb)as the(r,s,prefix)values of an Electrum-style RFC6979 deterministic signature ofRxwith private keyRy`
ECRECOVER, ///< Rx Ry Rz Ra Rb Rc - sets(Rb, Rc)as the public key from the signature(Ry, Rz, Ra)of the message hashRx`
ECVALID, ///< Rx Ry Rz Ra Rb Rc - sets(Rb, Rc)as the public key from the signature(Ry, Rz, Ra)of the message hashRx`
PUSH = 0x30,
POP,
DUP,
DUPN,
SWAP,
SWAPN,
LOAD,
STORE,
JMP = 0x40, ///< Rx - sets the index pointer to the value at Rx
JMPI, ///< Rx Ry - if Rx != 0, sets the index pointer to Ry
IND, ///< pushes the index pointer.
EXTRO = 0x50, ///< Rx Ry Rz - looks at the contract at address Rx and its memory state Ry, and outputs the result to Rz
BALANCE, ///< Rx - returns the ether balance of address Rx
MKTX = 0x60, ///< Rx Ry Rz Rw Rv - sends Ry ether to Rx plus Rz fee with Rw data items starting from memory index Rv (and then reading to (Rv + 1), (Rv + 2) etc). Note that if Rx = 0 then this creates a new contract.
SUICIDE = 0xff ///< Rx - destroys the contract and clears all memory, sending the entire balance plus the negative fee from clearing memory minus TXFEE to the address
};
class NotEnoughCash: public std::exception {};
class BadInstruction: public std::exception {};
class StackTooSmall: public std::exception { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
class OperandOutOfRange: public std::exception { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; };
class ExecutionException: public std::exception {};
class NoSuchContract: public std::exception {};
class InvalidSignature: public std::exception {};
class InvalidTransactionFormat: public std::exception {};
class InvalidBlockFormat: public std::exception {};
class InvalidUnclesHash: public std::exception {};
class InvalidTransactionsHash: public std::exception {};
class InvalidTransaction: public std::exception {};
class InvalidDifficulty: public std::exception {};
class InvalidTimestamp: public std::exception {};
class InvalidNonce: public std::exception {};
struct BlockInfo
{
public:
u256 hash;
u256 parentHash;
u256 sha256Uncles;
u256 coinbaseAddress;
u256 sha256Transactions;
u256 difficulty;
u256 timestamp;
u256 nonce;
u256 number;
void populateAndVerify(bytesConstRef _block, u256 _number)
{
number = _number;
RLP root(_block);
try
{
RLP header = root[0];
hash = eth::sha256(_block);
parentHash = header[0].toFatInt();
sha256Uncles = header[1].toFatInt();
coinbaseAddress = header[2].toFatInt();
sha256Transactions = header[3].toFatInt();
difficulty = header[4].toFatInt();
timestamp = header[5].toFatInt();
nonce = header[6].toFatInt();
}
catch (RLP::BadCast)
{
throw InvalidBlockFormat();
}
if (sha256Transactions != sha256(root[1].data()))
throw InvalidTransactionsHash();
if (sha256Uncles != sha256(root[2].data()))
throw InvalidUnclesHash();
// TODO: check timestamp.
// TODO: check difficulty against timestamp.
// TODO: check proof of work.
// TODO: check each transaction.
}
};
enum class AddressType
{
Normal,
Contract
};
class AddressState
{
public:
AddressState(AddressType _type = AddressType::Normal): m_type(_type), m_balance(0), m_nonce(0) {}
AddressType type() const { return m_type; }
u256& balance() { return m_balance; }
u256 const& balance() const { return m_balance; }
u256& nonce() { return m_nonce; }
u256 const& nonce() const { return m_nonce; }
std::map<u256, u256>& memory() { assert(m_type == AddressType::Contract); return m_memory; }
std::map<u256, u256> const& memory() const { assert(m_type == AddressType::Contract); return m_memory; }
u256 memoryHash() const
{
return hash256(m_memory);
}
std::string toString() const
{
if (m_type == AddressType::Normal)
return rlpList(m_balance, toCompactBigEndianString(m_nonce));
if (m_type == AddressType::Contract)
return rlpList(m_balance, toCompactBigEndianString(m_nonce), toCompactBigEndianString(memoryHash()));
return "";
}
private:
AddressType m_type;
u256 m_balance;
u256 m_nonce;
u256Map m_memory;
};
template <class _T>
inline u160 low160(_T const& _t)
{
return (u160)(_t & ((((_T)1) << 160) - 1));
}
template <class _T>
inline u160 as160(_T const& _t)
{
return (u160)(_t & ((((_T)1) << 160) - 1));
}
struct Signature
{
byte v;
@ -225,10 +54,17 @@ public:
static std::mt19937_64& engine();
bool verify(bytes const& _block);
bool execute(bytes const& _rlp) { try { Transaction t(_rlp); return execute(t, t.sender()); } catch (...) { return false; } } // remove const_cast once vector_ref can handle const vector* properly.
bool execute(bytes const& _rlp) { try { Transaction t(_rlp); execute(t, t.sender()); } catch (...) { return false; } }
private:
bool execute(Transaction const& _t, Address _sender);
struct MinerFeeAdder
{
~MinerFeeAdder() { state->addBalance(state->m_minerAddress, fee); }
State* state;
u256 fee;
};
void execute(Transaction const& _t, Address _sender);
bool isNormalAddress(Address _address) const { auto it = m_current.find(_address); return it != m_current.end() && it->second.type() == AddressType::Normal; }
bool isContractAddress(Address _address) const { auto it = m_current.find(_address); return it != m_current.end() && it->second.type() == AddressType::Contract; }
@ -238,14 +74,7 @@ private:
// bigint as we don't want any accidental problems with -ve numbers.
bool subBalance(Address _id, bigint _amount) { auto it = m_current.find(_id); if (it == m_current.end() || (bigint)it->second.balance() < _amount) return false; it->second.balance() = (u256)((bigint)it->second.balance() - _amount); return true; }
u256 contractMemory(Address _contract, u256 _memory) const
{
auto m = m_current.find(_contract);
if (m == m_current.end())
return 0;
auto i = m->second.memory().find(_memory);
return i == m->second.memory().end() ? 0 : i->second;
}
u256 contractMemory(Address _contract, u256 _memory) const;
u256 transactionsFrom(Address _address) { auto it = m_current.find(_address); return it == m_current.end() ? 0 : it->second.nonce(); }

4
libethereum/Trie.h

@ -7,10 +7,6 @@
namespace eth
{
using StringMap = std::map<std::string, std::string>;
using u256Map = std::map<u256, u256>;
using HexMap = std::map<bytes, std::string>;
u256 hash256(StringMap const& _s);
u256 hash256(u256Map const& _s);
std::string hexPrefixEncode(bytes const& _hexVector, bool _terminated = false, int _begin = 0, int _end = -1);

Loading…
Cancel
Save