From 30285da6cb25b34da2d6b0f20bc1003825cf8c59 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 4 Jan 2014 17:06:07 +0000 Subject: [PATCH] Much change - beginning the bigger-picture stuff. --- libethereum/Common.cpp | 2 +- libethereum/Common.h | 31 ++++-- libethereum/RLP.h | 106 +++++++++++++++--- libethereum/Trie.cpp | 85 +++++++------- libethereum/Trie.h | 2 + libethereum/VirtualMachine.cpp | 196 +++++++++++++-------------------- libethereum/VirtualMachine.h | 170 +++++++++++++++++++++++++--- libethereum/foreign.h | 51 --------- libethereum/sha256.cpp | 14 +++ libethereum/sha256.h | 1 + 10 files changed, 405 insertions(+), 253 deletions(-) delete mode 100644 libethereum/foreign.h diff --git a/libethereum/Common.cpp b/libethereum/Common.cpp index 34a7a7601..56576ef2f 100644 --- a/libethereum/Common.cpp +++ b/libethereum/Common.cpp @@ -10,7 +10,7 @@ using namespace eth; ((uint32_t) *((strptr)+1) << 8) | \ ((uint32_t) *(strptr))) -u256 eth::ripemd160(fConstBytes _message) +u256 eth::ripemd160(bytesConstRef _message) /* * returns RMD(message) * message should be a string terminated by '\0' diff --git a/libethereum/Common.h b/libethereum/Common.h index 4d4df6c31..140b4b2d5 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -4,7 +4,7 @@ #include #include #include -#include "foreign.h" +#include "vector_ref.h" namespace eth { @@ -12,15 +12,18 @@ namespace eth using byte = uint8_t; using bytes = std::vector; -using fBytes = foreign; -using fConstBytes = foreign; +using bytesRef = vector_ref; +using bytesConstRef = vector_ref; using bigint = boost::multiprecision::number>; using u256 = boost::multiprecision::number>; using s256 = boost::multiprecision::number>; +using u160 = boost::multiprecision::number>; +using s160 = boost::multiprecision::number>; using uint = uint64_t; using sint = int64_t; using u256s = std::vector; +using u160s = std::vector; template std::string toString(_T const& _t) { std::ostringstream o; o << _t; return o.str(); } @@ -57,15 +60,27 @@ inline bytes toHex(std::string const& _s) return ret; } -inline std::string toBigEndianString(u256 _val) +template +inline std::string toBigEndianString(_T _val, uint _s) { std::string ret; - ret.resize(32); - for (int i = 0; i < 32; ++i, _val >>= 8) - ret[31 - i] = (char)(uint8_t)_val; + ret.resize(_s); + for (uint i = 0; i < _s; ++i, _val >>= 8) + ret[_s - 1 - i] = (char)(uint8_t)_val; return ret; } +inline std::string toBigEndianString(u256 _val) { return toBigEndianString(_val, 32); } +inline std::string toBigEndianString(u160 _val) { return toBigEndianString(_val, 20); } + +template +inline std::string toCompactBigEndianString(_T _val) +{ + int i; + for (i = 0; _val; ++i, _val >>= 8) {} + return toBigEndianString(_val, i); +} + template uint commonPrefix(_T const& _t, _U const& _u) { uint s = std::min(_t.size(), _u.size()); @@ -75,6 +90,6 @@ template uint commonPrefix(_T const& _t, _U const& _u) return s; } -u256 ripemd160(fConstBytes _message); +u256 ripemd160(bytesConstRef _message); } diff --git a/libethereum/RLP.h b/libethereum/RLP.h index 1113fccba..68fc571e0 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -1,8 +1,9 @@ #pragma once +#include #include #include -#include "foreign.h" +#include "vector_ref.h" #include "Common.h" namespace eth @@ -20,20 +21,24 @@ typedef std::vector RLPs; class RLP { public: + class BadCast: public std::exception {}; + /// Construct a null node. RLP() {} /// Construct a node of value given in the bytes. - explicit RLP(fConstBytes _d): m_data(_d) {} + explicit RLP(bytesConstRef _d): m_data(_d) {} /// Construct a node of value given in the bytes. - explicit RLP(bytes const& _d): m_data(const_cast(&_d)) {} // a bit horrible, but we know we won't be altering the data. TODO: allow vector const* to be passed to foreign. + explicit RLP(bytes const& _d): m_data(const_cast(&_d)) {} // a bit horrible, but we know we won't be altering the data. TODO: allow vector const* to be passed to vector_ref. /// Construct a node to read RLP data in the bytes given. - RLP(byte const* _b, uint _s): m_data(fConstBytes(_b, _s)) {} + RLP(byte const* _b, uint _s): m_data(bytesConstRef(_b, _s)) {} /// Construct a node to read RLP data in the string. - explicit RLP(std::string const& _s): m_data(fConstBytes((byte const*)_s.data(), _s.size())) {} + explicit RLP(std::string const& _s): m_data(bytesConstRef((byte const*)_s.data(), _s.size())) {} + + bytesConstRef data() const { return m_data; } /// @returns true if the RLP is non-null. explicit operator bool() const { return !isNull(); } @@ -67,6 +72,7 @@ public: /// @returns the number of items in the list, or zero if it isn't a list. uint itemCount() const { return isList() ? items() : 0; } + uint itemCountStrict() const { if (!isList()) throw BadCast(); return items(); } /// @returns the number of characters in the string, or zero if it isn't a string. uint stringSize() const { return isString() ? items() : 0; } @@ -80,11 +86,69 @@ public: { if (!isList() || itemCount() <= _i) return RLP(); - fConstBytes d = payload(); - for (uint64_t i = 0; i < _i; ++i, d = d.cropped(RLP(d).size())) {} - return RLP(d); + if (_i < m_lastIndex) + { + m_lastEnd = RLP(payload()).actualSize(); + m_lastItem = payload().cropped(m_lastEnd); + m_lastIndex = 0; + } + for (; m_lastIndex < _i; ++m_lastIndex) + { + m_lastItem = payload().cropped(m_lastEnd); + m_lastItem = m_lastItem.cropped(0, RLP(m_lastItem).actualSize()); + m_lastEnd += m_lastItem.size(); + } + return RLP(m_lastItem); } + typedef RLP element_type; + + class iterator + { + friend class RLP; + + public: + typedef RLP value_type; + typedef RLP element_type; + + iterator& operator++() + { + if (m_remaining) + { + m_lastItem.retarget(m_lastItem.next().data(), m_remaining); + m_lastItem = m_lastItem.cropped(0, RLP(m_lastItem).actualSize()); + m_remaining -= std::min(m_remaining, m_lastItem.size()); + } + return *this; + } + iterator operator++(int) { auto ret = *this; operator++(); return ret; } + RLP operator*() const { return RLP(m_lastItem); } + bool operator==(iterator const& _cmp) const { return m_lastItem == _cmp.m_lastItem; } + bool operator!=(iterator const& _cmp) const { return !operator==(_cmp); } + + private: + iterator() {} + iterator(bytesConstRef _payload, bool _begin) + { + if (_begin) + { + m_lastItem = _payload.cropped(RLP(_payload).actualSize()); + m_remaining = _payload.size() - m_lastItem.size(); + } + else + { + m_lastItem = _payload.cropped(m_lastItem.size()); + m_remaining = 0; + } + } + uint m_remaining = 0; + bytesConstRef m_lastItem; + }; + friend class iterator; + + iterator begin() const { return iterator(payload(), true); } + iterator end() const { return iterator(payload(), false); } + explicit operator std::string() const { return toString(); } explicit operator RLPs() const { return toList(); } explicit operator uint() const { return toSlimInt(); } @@ -115,6 +179,12 @@ public: uint toSlimInt() const { return toInt(); } u256 toFatInt() const { return toInt(); } bigint toBigInt() const { return toInt(); } + uint toSlimIntStrict() const { if (!isSlimInt()) throw BadCast(); return toInt(); } + u256 toFatIntStrict() const { if (!isFatInt() && !isSlimInt()) throw BadCast(); return toInt(); } + bigint toBigIntStrict() const { if (!isInt()) throw BadCast(); return toInt(); } + uint toSlimIntFromString() const { if (!isString()) throw BadCast(); return toInt(); } + u256 toFatIntFromString() const { if (!isString()) throw BadCast(); return toInt(); } + bigint toBigIntFromString() const { if (!isString()) throw BadCast(); return toInt(); } RLPs toList() const { @@ -122,9 +192,8 @@ public: if (!isList()) return ret; uint64_t c = items(); - fConstBytes d = payload(); - for (uint64_t i = 0; i < c; ++i, d = d.cropped(RLP(d).size())) - ret.push_back(RLP(d)); + for (uint64_t i = 0; i < c; ++i) + ret.push_back(operator[](i)); return ret; } @@ -144,7 +213,7 @@ private: /// Direct-length list. bool isSmallList() const { assert(!isNull()); return m_data[0] >= 0x80 && m_data[0] < 0xb8; } - uint size() const + uint actualSize() const { if (isNull()) return 0; @@ -154,9 +223,9 @@ private: return payload().data() - m_data.data() + items(); if (isList()) { - fConstBytes d = payload(); + bytesConstRef d = payload(); uint64_t c = items(); - for (uint64_t i = 0; i < c; ++i, d = d.cropped(RLP(d).size())) {} + for (uint64_t i = 0; i < c; ++i, d = d.cropped(RLP(d).actualSize())) {} return d.data() - m_data.data(); } return 0; @@ -176,14 +245,17 @@ private: return ret; } - fConstBytes payload() const + bytesConstRef payload() const { assert(isString() || isList()); auto n = (m_data[0] & 0x3f); return m_data.cropped(1 + (n < 0x38 ? 0 : (n - 0x37))); } - fConstBytes m_data; + bytesConstRef m_data; + mutable uint m_lastIndex = (uint)-1; + mutable uint m_lastEnd; + mutable bytesConstRef m_lastItem; }; struct RLPList { RLPList(uint _count): count(_count) {} uint count; }; @@ -376,7 +448,7 @@ inline std::ostream& operator<<(std::ostream& _out, eth::RLP _d) { _out << "["; int j = 0; - for (auto i: _d.toList()) + for (auto i: _d) _out << (j++ ? ", " : " ") << i; _out << " ]"; } diff --git a/libethereum/Trie.cpp b/libethereum/Trie.cpp index d3995800b..874b18464 100644 --- a/libethereum/Trie.cpp +++ b/libethereum/Trie.cpp @@ -62,7 +62,7 @@ u256 hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i rlp << RLPList(2) << hexPrefixEncode(_begin->first, true, _preLen) << _begin->second; #if ENABLE_DEBUG_PRINT if (g_hashDebug) - std::cerr << s_indent << asHex(fConstBytes(_begin->first.data() + _preLen, _begin->first.size() - _preLen), 1) << ": " << _begin->second << " = " << sha256(rlp.out()) << std::endl; + std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, _begin->first.size() - _preLen), 1) << ": " << _begin->second << " = " << sha256(rlp.out()) << std::endl; #endif } else @@ -83,9 +83,9 @@ u256 hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i // if they all have the same next nibble, we also want a pair. #if ENABLE_DEBUG_PRINT if (g_hashDebug) - std::cerr << s_indent << asHex(fConstBytes(_begin->first.data() + _preLen, sharedPre), 1) << ": " << std::endl; + std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, sharedPre), 1) << ": " << std::endl; #endif - rlp << RLPList(2) << hexPrefixEncode(_begin->first, false, _preLen, sharedPre) << toBigEndianString(hash256aux(_s, _begin, _end, sharedPre)); + rlp << RLPList(2) << hexPrefixEncode(_begin->first, false, _preLen, sharedPre) << toCompactBigEndianString(hash256aux(_s, _begin, _end, sharedPre)); #if ENABLE_DEBUG_PRINT if (g_hashDebug) std::cerr << s_indent << "= " << sha256(rlp.out()) << std::endl; @@ -116,7 +116,7 @@ u256 hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i if (g_hashDebug) std::cerr << s_indent << std::hex << i << ": " << std::endl; #endif - rlp << toBigEndianString(hash256aux(_s, b, n, _preLen + 1)); + rlp << toCompactBigEndianString(hash256aux(_s, b, n, _preLen + 1)); } b = n; } @@ -149,6 +149,17 @@ u256 hash256(StringMap const& _s) return hash256aux(hexMap, hexMap.cbegin(), hexMap.cend(), 0); } +u256 hash256(u256Map const& _s) +{ + // build patricia tree. + if (_s.empty()) + return sha256(RLPNull); + HexMap hexMap; + for (auto i = _s.rbegin(); i != _s.rend(); ++i) + hexMap[toHex(toBigEndianString(i->first))] = rlp(i->second); + return hash256aux(hexMap, hexMap.cbegin(), hexMap.cend(), 0); +} + class TrieNode { @@ -156,9 +167,9 @@ public: TrieNode() {} virtual ~TrieNode() {} - virtual std::string const& at(fConstBytes _key) const = 0; - virtual TrieNode* insert(fConstBytes _key, std::string const& _value) = 0; - virtual TrieNode* remove(fConstBytes _key) = 0; + virtual std::string const& at(bytesConstRef _key) const = 0; + virtual TrieNode* insert(bytesConstRef _key, std::string const& _value) = 0; + virtual TrieNode* remove(bytesConstRef _key) = 0; virtual bytes rlp() const = 0; #if ENABLE_DEBUG_PRINT @@ -173,7 +184,7 @@ protected: virtual void debugPrintBody(std::string const& _indent = "") const = 0; #endif - static TrieNode* newBranch(fConstBytes _k1, std::string const& _v1, fConstBytes _k2, std::string const& _v2); + static TrieNode* newBranch(bytesConstRef _k1, std::string const& _v1, bytesConstRef _k2, std::string const& _v2); private: mutable u256 m_sha256 = 0; @@ -184,7 +195,7 @@ static const std::string c_nullString; class TrieExtNode: public TrieNode { public: - TrieExtNode(fConstBytes _bytes): m_ext(_bytes.begin(), _bytes.end()) {} + TrieExtNode(bytesConstRef _bytes): m_ext(_bytes.begin(), _bytes.end()) {} bytes m_ext; }; @@ -231,9 +242,9 @@ public: } #endif - virtual std::string const& at(fConstBytes _key) const override; - virtual TrieNode* insert(fConstBytes _key, std::string const& _value) override; - virtual TrieNode* remove(fConstBytes _key) override; + virtual std::string const& at(bytesConstRef _key) const override; + virtual TrieNode* insert(bytesConstRef _key, std::string const& _value) override; + virtual TrieNode* remove(bytesConstRef _key) override; virtual bytes rlp() const override; private: @@ -249,7 +260,7 @@ private: class TrieLeafNode: public TrieExtNode { public: - TrieLeafNode(fConstBytes _key, std::string const& _value): TrieExtNode(_key), m_value(_value) {} + TrieLeafNode(bytesConstRef _key, std::string const& _value): TrieExtNode(_key), m_value(_value) {} #if ENABLE_DEBUG_PRINT virtual void debugPrintBody(std::string const& _indent) const @@ -264,13 +275,13 @@ public: } #endif - virtual std::string const& at(fConstBytes _key) const override { return contains(_key) ? m_value : c_nullString; } - virtual TrieNode* insert(fConstBytes _key, std::string const& _value) override; - virtual TrieNode* remove(fConstBytes _key) override; + virtual std::string const& at(bytesConstRef _key) const override { return contains(_key) ? m_value : c_nullString; } + virtual TrieNode* insert(bytesConstRef _key, std::string const& _value) override; + virtual TrieNode* remove(bytesConstRef _key) override; virtual bytes rlp() const override { return rlpListBytes(hexPrefixEncode(m_ext, true), m_value); } private: - bool contains(fConstBytes _key) const { return _key.size() == m_ext.size() && !memcmp(_key.data(), m_ext.data(), _key.size()); } + bool contains(bytesConstRef _key) const { return _key.size() == m_ext.size() && !memcmp(_key.data(), m_ext.data(), _key.size()); } std::string m_value; }; @@ -278,7 +289,7 @@ private: class TrieInfixNode: public TrieExtNode { public: - TrieInfixNode(fConstBytes _key, TrieNode* _next): TrieExtNode(_key), m_next(_next) {} + TrieInfixNode(bytesConstRef _key, TrieNode* _next): TrieExtNode(_key), m_next(_next) {} virtual ~TrieInfixNode() { delete m_next; } #if ENABLE_DEBUG_PRINT @@ -289,18 +300,18 @@ public: } #endif - virtual std::string const& at(fConstBytes _key) const override { assert(m_next); return contains(_key) ? m_next->at(_key.cropped(m_ext.size())) : c_nullString; } - virtual TrieNode* insert(fConstBytes _key, std::string const& _value) override; - virtual TrieNode* remove(fConstBytes _key) override; - virtual bytes rlp() const override { assert(m_next); return rlpListBytes(hexPrefixEncode(m_ext, false), toBigEndianString(m_next->sha256())); } + virtual std::string const& at(bytesConstRef _key) const override { assert(m_next); return contains(_key) ? m_next->at(_key.cropped(m_ext.size())) : c_nullString; } + virtual TrieNode* insert(bytesConstRef _key, std::string const& _value) override; + virtual TrieNode* remove(bytesConstRef _key) override; + virtual bytes rlp() const override { assert(m_next); return rlpListBytes(hexPrefixEncode(m_ext, false), toCompactBigEndianString(m_next->sha256())); } private: - bool contains(fConstBytes _key) const { return _key.size() >= m_ext.size() && !memcmp(_key.data(), m_ext.data(), m_ext.size()); } + bool contains(bytesConstRef _key) const { return _key.size() >= m_ext.size() && !memcmp(_key.data(), m_ext.data(), m_ext.size()); } TrieNode* m_next; }; -TrieNode* TrieNode::newBranch(fConstBytes _k1, std::string const& _v1, fConstBytes _k2, std::string const& _v2) +TrieNode* TrieNode::newBranch(bytesConstRef _k1, std::string const& _v1, bytesConstRef _k2, std::string const& _v2) { uint prefix = commonPrefix(_k1, _k2); @@ -319,7 +330,7 @@ TrieNode* TrieNode::newBranch(fConstBytes _k1, std::string const& _v1, fConstByt return ret; } -std::string const& TrieBranchNode::at(fConstBytes _key) const +std::string const& TrieBranchNode::at(bytesConstRef _key) const { if (_key.empty()) return m_value; @@ -328,7 +339,7 @@ std::string const& TrieBranchNode::at(fConstBytes _key) const return c_nullString; } -TrieNode* TrieBranchNode::insert(fConstBytes _key, std::string const& _value) +TrieNode* TrieBranchNode::insert(bytesConstRef _key, std::string const& _value) { assert(_value.size()); mark(); @@ -342,7 +353,7 @@ TrieNode* TrieBranchNode::insert(fConstBytes _key, std::string const& _value) return this; } -TrieNode* TrieBranchNode::remove(fConstBytes _key) +TrieNode* TrieBranchNode::remove(bytesConstRef _key) { if (_key.empty()) if (m_value.size()) @@ -367,7 +378,7 @@ TrieNode* TrieBranchNode::rejig() if (n == (byte)-1 && m_value.size()) { // switch to leaf - auto r = new TrieLeafNode(fConstBytes(), m_value); + auto r = new TrieLeafNode(bytesConstRef(), m_value); delete this; return r; } @@ -379,7 +390,7 @@ TrieNode* TrieBranchNode::rejig() // switch to infix m_nodes[n] = nullptr; delete this; - return new TrieInfixNode(fConstBytes(&n, 1), b); + return new TrieInfixNode(bytesConstRef(&n, 1), b); } else { @@ -401,7 +412,7 @@ bytes TrieBranchNode::rlp() const RLPStream s; s << RLPList(17); for (auto i: m_nodes) - s << (i ? toBigEndianString(i->sha256()) : ""); + s << (i ? toCompactBigEndianString(i->sha256()) : ""); s << m_value; return s.out(); } @@ -420,7 +431,7 @@ byte TrieBranchNode::activeBranch() const return n; } -TrieNode* TrieInfixNode::insert(fConstBytes _key, std::string const& _value) +TrieNode* TrieInfixNode::insert(bytesConstRef _key, std::string const& _value) { assert(_value.size()); mark(); @@ -458,7 +469,7 @@ TrieNode* TrieInfixNode::insert(fConstBytes _key, std::string const& _value) } } -TrieNode* TrieInfixNode::remove(fConstBytes _key) +TrieNode* TrieInfixNode::remove(bytesConstRef _key) { if (contains(_key)) { @@ -485,7 +496,7 @@ TrieNode* TrieInfixNode::remove(fConstBytes _key) return this; } -TrieNode* TrieLeafNode::insert(fConstBytes _key, std::string const& _value) +TrieNode* TrieLeafNode::insert(bytesConstRef _key, std::string const& _value) { assert(_value.size()); mark(); @@ -497,13 +508,13 @@ TrieNode* TrieLeafNode::insert(fConstBytes _key, std::string const& _value) else { // create new trie. - auto n = TrieNode::newBranch(_key, _value, fConstBytes(&m_ext), m_value); + auto n = TrieNode::newBranch(_key, _value, bytesConstRef(&m_ext), m_value); delete this; return n; } } -TrieNode* TrieLeafNode::remove(fConstBytes _key) +TrieNode* TrieLeafNode::remove(bytesConstRef _key) { if (contains(_key)) { @@ -541,7 +552,7 @@ std::string const& Trie::at(std::string const& _key) const if (!m_root) return c_nullString; auto h = toHex(_key); - return m_root->at(fConstBytes(&h)); + return m_root->at(bytesConstRef(&h)); } void Trie::insert(std::string const& _key, std::string const& _value) @@ -549,7 +560,7 @@ void Trie::insert(std::string const& _key, std::string const& _value) if (_value.empty()) remove(_key); auto h = toHex(_key); - m_root = m_root ? m_root->insert(&h, _value) : new TrieLeafNode(fConstBytes(&h), _value); + m_root = m_root ? m_root->insert(&h, _value) : new TrieLeafNode(bytesConstRef(&h), _value); } void Trie::remove(std::string const& _key) diff --git a/libethereum/Trie.h b/libethereum/Trie.h index c0864a0a3..dcebf97af 100644 --- a/libethereum/Trie.h +++ b/libethereum/Trie.h @@ -8,9 +8,11 @@ namespace eth { using StringMap = std::map; +using u256Map = std::map; using HexMap = std::map; 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); class TrieNode; diff --git a/libethereum/VirtualMachine.cpp b/libethereum/VirtualMachine.cpp index 65e3cddbd..87329e94c 100644 --- a/libethereum/VirtualMachine.cpp +++ b/libethereum/VirtualMachine.cpp @@ -12,130 +12,96 @@ 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) +Transaction::Transaction(bytes const& _rlpData) { - // TODO... - return _s; + RLP rlp(_rlpData); + nonce = rlp[0].toFatIntFromString(); + receiveAddress = as160(rlp[1].toFatIntFromString()); + value = rlp[2].toFatIntStrict(); + fee = rlp[3].toFatIntStrict(); + data.reserve(rlp[4].itemCountStrict()); + for (auto const& i: rlp[4]) + data.push_back(i.toFatIntStrict()); + vrs = Signature{ rlp[5].toFatIntFromString(), rlp[6].toFatIntFromString(), rlp[7].toFatIntFromString() }; } -template -inline _T low160(_T const& _t) +bytes Transaction::rlp() const { - return _t & ((((_T)1) << 160) - 1); + RLPStream rlp; + rlp << RLPList(8); + if (nonce) + rlp << nonce; + else + rlp << ""; + if (receiveAddress) + rlp << toCompactBigEndianString(receiveAddress); + else + rlp << ""; + rlp << value << fee << data << toCompactBigEndianString(vrs.v) << toCompactBigEndianString(vrs.r) << toCompactBigEndianString(vrs.s); + return rlp.out(); } -bool State::transact(bytes const& _rlp) +// Entry point for a user-originated transaction. +bool State::execute(Transaction const& _t) { - 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(); + return execute(_t, _t.vrs.address()); +} - u256 sender; - try - { - sender = extractSender(v, r, s); - } - catch (...) - { - // Invalid signiture. - // Error reporting? - return false; - } +bool State::execute(Transaction const& _t, u160 _sender) +{ + // Entry point for a contract-originated transaction. - if (nonce != transactionsFrom(sender)) + if (_t.nonce != transactionsFrom(_sender)) { // Nonce is wrong. // Error reporting? return false; } - if (balance(sender) < value + fee) + if (balance(_sender) < _t.value + _t.fee) { // Sender balance too low. // Error reporting? return false; } - if (address) + if (_t.receiveAddress) { - assert(subBalance(sender, value)); - addBalance(address, value); + assert(subBalance(_sender, _t.value)); + addBalance(_t.receiveAddress, _t.value); - if (isContractAddress(address)) + if (isContractAddress(_t.receiveAddress)) { - bool ret = true; u256 minerFee = 0; try { - execute(address, sender, value, fee, data, &minerFee); + execute(_t.receiveAddress, _sender, _t.value, _t.fee, _t.data, &minerFee); + addBalance(m_minerAddress, minerFee); + return true; } catch (...) { // Execution error. // Error reporting? - ret = false; + addBalance(m_minerAddress, minerFee); + throw ExecutionException(); } - - addBalance(m_minerAddress, minerFee); - return ret; } else return true; } else { - if (fee < data.size() * c_memoryFee + c_newContractFee) + if (_t.fee < _t.data.size() * c_memoryFee + c_newContractFee) { // Fee too small. // Error reporting? return false; } - u256 newAddress = low160(sha256(_rlp)); + u160 newAddress = low160(_t.sha256()); + if (isContractAddress(newAddress)) { // Contract collision. @@ -143,19 +109,23 @@ bool State::transact(bytes const& _rlp) 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); + // + 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)); + addBalance(newAddress, _t.value); return true; } } -void State::execute(u256 _myAddress, u256 _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee) +void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee) { std::vector stack; - auto& myMemory = ensureMemory(_myAddress); + auto m = m_current.find(_myAddress); + if (m == m_current.end()) + throw NoSuchContract(); + auto& myMemory = m->second.memory(); auto require = [&](u256 _n) { @@ -334,7 +304,7 @@ void State::execute(u256 _myAddress, u256 _txSender, u256 _txValue, u256 _txFee, stack.push_back(m_previousBlock.hash); break; case Instruction::BLK_COINBASE: - stack.push_back(m_currentBlock.coinbase); + stack.push_back(m_currentBlock.coinbaseAddress); break; case Instruction::BLK_TIMESTAMP: stack.push_back(m_currentBlock.timestamp); @@ -363,7 +333,8 @@ void State::execute(u256 _myAddress, u256 _txSender, u256 _txValue, u256 _txFee, 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. + // 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; } @@ -447,69 +418,52 @@ void State::execute(u256 _myAddress, u256 _txSender, u256 _txValue, u256 _txFee, require(2); auto memoryAddress = stack.back(); stack.pop_back(); - auto contractAddress = stack.back(); - stack.back() = memory(contractAddress, memoryAddress); + u160 contractAddress = as160(stack.back()); + stack.back() = contractMemory(contractAddress, memoryAddress); break; } case Instruction::BALANCE: { require(1); - stack.back() = balance(stack.back()); + stack.back() = balance(as160(stack.back())); break; } case Instruction::MKTX: { require(4); - auto dest = stack.back(); - stack.pop_back(); - auto value = stack.back(); + Transaction t; + t.receiveAddress = as160(stack.back()); stack.pop_back(); - - auto fee = stack.back(); + t.value = stack.back(); + stack.pop_back(); + t.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); + t.data.reserve((uint)itemCount); for (auto i = 0; i < itemCount; ++i) { - data.push_back(stack.back()); + t.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()); + t.nonce = transactionsFrom(_myAddress); + execute(t); 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); + u160 dest = as160(stack.back()); + u256 minusVoidFee = m_current[_myAddress].memory().size() * c_memoryFee; + addBalance(dest, balance(_myAddress) + minusVoidFee); + subBalance(dest, _txFee); + m_current.erase(_myAddress); // ...follow through to... } case Instruction::STOP: diff --git a/libethereum/VirtualMachine.h b/libethereum/VirtualMachine.h index d0195b719..153fc64fc 100644 --- a/libethereum/VirtualMachine.h +++ b/libethereum/VirtualMachine.h @@ -4,6 +4,7 @@ #include #include #include +#include "Trie.h" #include "RLP.h" #include "Common.h" @@ -67,14 +68,144 @@ enum class Instruction: uint8_t 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 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 coinbase; + u256 parentHash; + u256 sha256Uncles; + u256 coinbaseAddress; + u256 sha256Transactions; + u256 difficulty; u256 timestamp; + u256 nonce; u256 number; - u256 difficulty; + + 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& memory() { assert(m_type == AddressType::Contract); return m_memory; } + std::map 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 +inline u160 low160(_T const& _t) +{ + return (u160)(_t & ((((_T)1) << 160) - 1)); +} + +template +inline u160 as160(_T const& _t) +{ + return (u160)(_t & ((((_T)1) << 160) - 1)); +} + + +struct Signature +{ + u256 v; + u256 r; + u256 s; + + u160 address() const { return as160(s); } // TODO! +}; + + +// [ nonce, receiving_address, value, fee, [ data item 0, data item 1 ... data item n ], v, r, s ] +struct Transaction +{ + Transaction() {} + Transaction(bytes const& _rlp); + + u256 nonce; + u160 receiveAddress; + u256 value; + u256 fee; + u256s data; + Signature vrs; + + bytes rlp() const; + u256 sha256() const { return eth::sha256(rlp()); } }; class State @@ -82,38 +213,41 @@ class State public: explicit State(u256 _minerAddress): m_minerAddress(_minerAddress) {} - bool transact(bytes const& _rlp); + bool verify(bytes const& _block); + bool execute(bytes const& _rlp) { try { Transaction t(_rlp); return execute(t); } catch (...) { return false; } } private: - bool isContractAddress(u256 _address) const { return m_contractMemory.count(_address); } + bool execute(Transaction const& _t); + bool execute(Transaction const& _t, u160 _sender); + + bool isNormalAddress(u160 _address) const { auto it = m_current.find(_address); return it != m_current.end() && it->second.type() == AddressType::Normal; } + bool isContractAddress(u160 _address) const { auto it = m_current.find(_address); return it != m_current.end() && it->second.type() == AddressType::Contract; } - u256 balance(u256 _id) const { auto it = m_balance.find(_id); return it == m_balance.end() ? 0 : it->second; } - void addBalance(u256 _id, u256 _amount) { auto it = m_balance.find(_id); if (it == m_balance.end()) it->second = _amount; else it->second += _amount; } + u256 balance(u160 _id) const { auto it = m_current.find(_id); return it == m_current.end() ? 0 : it->second.balance(); } + void addBalance(u160 _id, u256 _amount) { auto it = m_current.find(_id); if (it == m_current.end()) it->second.balance() = _amount; else it->second.balance() += _amount; } // bigint as we don't want any accidental problems with -ve numbers. - bool subBalance(u256 _id, bigint _amount) { auto it = m_balance.find(_id); if (it == m_balance.end() || (bigint)it->second < _amount) return false; it->second = (u256)((bigint)it->second - _amount); return true; } + bool subBalance(u160 _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 memory(u256 _contract, u256 _memory) const + u256 contractMemory(u160 _contract, u256 _memory) const { - auto m = m_contractMemory.find(_contract); - if (m == m_contractMemory.end()) + auto m = m_current.find(_contract); + if (m == m_current.end()) return 0; - auto i = m->second.find(_memory); - return i == m->second.end() ? 0 : i->second; + auto i = m->second.memory().find(_memory); + return i == m->second.memory().end() ? 0 : i->second; } - u256 transactionsFrom(u256 _address) { return 0; } // TODO + u256 transactionsFrom(u160 _address) { auto it = m_current.find(_address); return it == m_current.end() ? 0 : it->second.nonce(); } - void execute(u256 _myAddress, u256 _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee); + void execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* o_totalFee); - std::map& ensureMemory(u256 _contract) { return m_contractMemory[_contract]; } + std::map m_current; - std::map> m_contractMemory; - std::map m_balance; // for now - might end up using Trie? BlockInfo m_previousBlock; BlockInfo m_currentBlock; - u256 m_minerAddress; + u160 m_minerAddress; static const u256 c_stepFee; static const u256 c_dataFee; diff --git a/libethereum/foreign.h b/libethereum/foreign.h deleted file mode 100644 index 9b3477a98..000000000 --- a/libethereum/foreign.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace eth -{ - -template -class foreign -{ -public: - typedef _T value_type; - typedef _T element_type; - - foreign(): m_data(nullptr), m_count(0) {} - foreign(std::vector::type>* _data): m_data(_data->data()), m_count(_data->size()) {} - foreign(_T* _data, unsigned _count): m_data(_data), m_count(_count) {} - - explicit operator bool() const { return m_data && m_count; } - - std::vector<_T> toVector() const { return std::vector<_T>(m_data, m_data + m_count); } - std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count); } - template operator foreign<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return foreign<_T2>((_T2*)m_data, m_count * sizeof(_T) / sizeof(_T2)); } - - _T* data() const { return m_data; } - unsigned count() const { return m_count; } - unsigned size() const { return m_count; } - unsigned empty() const { return !m_count; } - foreign<_T> next() const { return foreign<_T>(m_data + m_count, m_count); } - foreign<_T> cropped(unsigned _begin, int _count = -1) const { if (m_data && _begin + std::max(0, _count) <= m_count) return foreign<_T>(m_data + _begin, _count < 0 ? m_count - _begin : _count); else return foreign<_T>(); } - void retarget(_T const* _d, size_t _s) { m_data = _d; m_count = _s; } - void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } - - _T* begin() { return m_data; } - _T* end() { return m_data + m_count; } - _T const* begin() const { return m_data; } - _T const* end() const { return m_data + m_count; } - - _T& operator[](unsigned _i) { assert(m_data); assert(_i < m_count); return m_data[_i]; } - _T const& operator[](unsigned _i) const { assert(m_data); assert(_i < m_count); return m_data[_i]; } - - void reset() { m_data = nullptr; m_count = 0; } - -private: - _T* m_data; - unsigned m_count; -}; - -} diff --git a/libethereum/sha256.cpp b/libethereum/sha256.cpp index a96c34037..a69b5b513 100644 --- a/libethereum/sha256.cpp +++ b/libethereum/sha256.cpp @@ -194,3 +194,17 @@ u256 eth::sha256(bytes const& _input) ret = (ret << 8) | buf[i]; return ret; } + +u256 eth::sha256(bytesConstRef _input) +{ + u256 ret = 0; + + SHA256 ctx = SHA256(); + ctx.init(); + ctx.update(_input.data(), _input.size()); + uint8_t buf[SHA256::DIGEST_SIZE]; + ctx.final(buf); + for (unsigned i = 0; i < 32; ++i) + ret = (ret << 8) | buf[i]; + return ret; +} diff --git a/libethereum/sha256.h b/libethereum/sha256.h index 590e40669..f141426ee 100644 --- a/libethereum/sha256.h +++ b/libethereum/sha256.h @@ -27,6 +27,7 @@ protected: std::string sha256(std::string const& input, bool _hex); u256 sha256(bytes const& input); +u256 sha256(bytesConstRef input); #define SHA2_SHFR(x, n) (x >> n) #define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))