diff --git a/CodingStandards.txt b/CodingStandards.txt new file mode 100644 index 000000000..afe4db0e9 --- /dev/null +++ b/CodingStandards.txt @@ -0,0 +1,131 @@ +0. Formatting + +a. Use tabs for indentation; 4 spaces wide. One indentation level -> exactly one byte (i.e. a tab character) in the source file. +b. Don't worry about having lines > 80-char wide. We're not editing on terminals anymore. +c. Don't use braces for condition-body one-liners. +d. Never place condition bodies on same line as condition. +e. Space between first paren and keyword, but *not* following first paren or preceeding final paren. +f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity. +g. No spaces for subscripting. +h. Space all other operators. +i. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. + +(WRONG) +if( a==b[ i ] ) { printf ("Hello\n"); } + +(RIGHT) +if (a == b[i]) + printf("Hello\n"); // NOTE spaces used instead of tab here for clarify - first byte should be '\t'. + + + +1. Namespaces; + +a. No "using" declarations in header files. +b. All symbols should be declared in a namespace except for final applications. +c. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore. + +(WRONG) +#include +using namespace std; +tuple meanAndSigma(vector const& _v); + +(CORRECT) +#include +std::tuple meanAndSigma(std::vector const& _v); + + + +2. Preprocessor; + +a. File comment is always at top, and includes: +- Original author, date. +- Later maintainers (not contributors - they can be seen through VCS log). +- Copyright. +- Licence (e.g. see COPYING). +b. Never use #ifdef/#define/#endif file guards. Prefer #pragma once as first line below file comment. +c. Prefer static const variable to value macros. +d. Prefer inline constexpr functions to function macros. +e. Split complex macro on multiple lines with '\'. + + + +3. Capitalization; + +a. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions. +b. The following entities' first alpha is upper case: +- Type names. +- Template parameters. +- Enum members. +- static const variables that form an external API. +c. All preprocessor symbols (macros, macro argments) in full uppercase with underscore word separation. + +All other entities' first alpha is lower case. + + + +4. Variable prefixes: + +a. Leading underscore "_" to parameter names (both normal and template). +- Exception: "o_parameterName" when it is used exclusively for output. See 7(f). +- Exception: "io_parameterName" when it is used for both input and output. See 7(f). +b. Leading "c_" to const variables (unless part of an external API). +c. Leading "g_" to global (non-const) variables. +d. Leading "s_" to static (non-const, non-global) variables. + + + +5. Error reporting: + +- Prefer exception to bool/int return type. + + + +6. Declarations: + +a. {Typename} + {qualifiers} + {name}. +b. Only one per line. +c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). +d. Favour declarations close to use; don't habitually declare at top of scope ala C. +e. Always pass non-trivial parameters with a const& suffix. +f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. +g. Never use a macro where adequate non-preprocessor C++ can be written. + +(WRONG) +const double d = 0; +int i, j; +char *s; +float meanAndSigma(std::vector _v, float* _sigma); + +(CORRECT) +double const d = 0; +int i; +int j; +char* s; +std::tuple meanAndSigma(std::vector const& _v); + + + +7. Structs & classes + +a. Structs to be used when all members public and no virtual functions. +b. Classes to be used in all other circumstances. + + + +8. Members: + +a. One member per line only. +b. Private, non-static, non-const fields prefixed with m_. +c. Avoid public fields, except in structs. +d. Use override, final and const judiciously. +e. No implementations with the class declaration, except: +- template or force-inline method (though prefer implementation at bottom of header file). +- one-line implementation (in which case include it in same line as declaration). + + + +9. Commenting + +a. Comments should be doxygen-compilable, using @notation rather than \notation. + diff --git a/README.md b/README.md index 89e9b5169..1d7980df3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,18 @@ Ethereum C++ Client. +Gav Wood, 2014. + ## Dependencies secp256k1 implementation: https://github.com/sipa/secp256k1.git Expects secp256k1 directory to be in same path as cpp-ethereum. + +## Contributing + +Read CodingStandards.txt thoroughly, before making alterations to the code base. + +Do *NOT* use an editor that automatically reformats whitespace away from astylerc or +the formating guidelines as describled in CodingStandards.txt. Your contributions will be +refused and your commits will be reversed. + diff --git a/astylerc b/astylerc new file mode 100644 index 000000000..0b407b9fe --- /dev/null +++ b/astylerc @@ -0,0 +1,11 @@ +style=allman +indent=force-tab=4 +convert-tabs +indent-preprocessor +min-conditional-indent=1 +pad-oper +pad-header +unpad-paren +delete-empty-lines +align-pointer=type + diff --git a/libethereum/AddressState.cpp b/libethereum/AddressState.cpp index 612312a7f..1e0408c13 100644 --- a/libethereum/AddressState.cpp +++ b/libethereum/AddressState.cpp @@ -1,3 +1,24 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file AddressState.cpp + * @author Gav Wood + * @date 2014 + */ + #include "Trie.h" #include "AddressState.h" using namespace std; diff --git a/libethereum/AddressState.h b/libethereum/AddressState.h index 621e12d70..b779e2759 100644 --- a/libethereum/AddressState.h +++ b/libethereum/AddressState.h @@ -1,3 +1,24 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file AddressState.h + * @author Gav Wood + * @date 2014 + */ + #pragma once #include "Common.h" @@ -30,9 +51,9 @@ public: std::string toString() const { if (m_type == AddressType::Normal) - return rlpList(m_balance, toCompactBigEndianString(m_nonce)); + return asString(rlpList(m_balance, toCompactBigEndianString(m_nonce))); if (m_type == AddressType::Contract) - return rlpList(m_balance, toCompactBigEndianString(m_nonce), toCompactBigEndianString(memoryHash())); + return asString(rlpList(m_balance, toCompactBigEndianString(m_nonce), toCompactBigEndianString(memoryHash()))); return ""; } diff --git a/libethereum/BlockInfo.cpp b/libethereum/BlockInfo.cpp index 5231027fe..a1a9781d1 100644 --- a/libethereum/BlockInfo.cpp +++ b/libethereum/BlockInfo.cpp @@ -1,9 +1,33 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file BlockInfo.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Common.h" +#include "sha256.h" +#include "Exceptions.h" #include "RLP.h" #include "BlockInfo.h" using namespace std; using namespace eth; -void populateAndVerify(bytesConstRef _block, u256 _number) +void BlockInfo::populateAndVerify(bytesConstRef _block, u256 _number) { number = _number; diff --git a/libethereum/BlockInfo.h b/libethereum/BlockInfo.h index 39d15b089..815079cea 100644 --- a/libethereum/BlockInfo.h +++ b/libethereum/BlockInfo.h @@ -1,3 +1,24 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file BlockInfo.h + * @author Gav Wood + * @date 2014 + */ + #pragma once #include "Common.h" diff --git a/libethereum/Common.cpp b/libethereum/Common.cpp index 56576ef2f..a33d650e2 100644 --- a/libethereum/Common.cpp +++ b/libethereum/Common.cpp @@ -1,3 +1,24 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file Common.cpp + * @author Gav Wood + * @date 2014 + */ + #include "Common.h" #include "rmd160.h" using namespace std; @@ -53,3 +74,73 @@ u256 eth::ripemd160(bytesConstRef _message) ret = (ret << 8) | hashcode[i]; return ret; } + +std::string eth::escaped(std::string const& _s, bool _all) +{ + std::string ret; + ret.reserve(_s.size()); + ret.push_back('"'); + for (auto i: _s) + if (i == '"' && !_all) + ret += "\\\""; + else if (i == '\\' && !_all) + ret += "\\\\"; + else if (i < ' ' || i > 127 || _all) + { + ret += "\\x"; + ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); + ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); + } + else + ret.push_back(i); + ret.push_back('"'); + return ret; +} + +std::string eth::randomWord() +{ + static std::mt19937_64 s_eng(0); + std::string ret(std::uniform_int_distribution(4, 10)(s_eng), ' '); + char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; + std::uniform_int_distribution d(0, sizeof(n) - 2); + for (char& c: ret) + c = n[d(s_eng)]; + return ret; +} + + +int eth::fromHex(char _i) +{ + if (_i >= '0' && _i <= '9') + return _i - '0'; + if (_i >= 'a' && _i <= 'f') + return _i - 'a' + 10; + if (_i >= 'A' && _i <= 'F') + return _i - 'A' + 10; + throw BadHexCharacter(); +} + +bytes eth::fromUserHex(std::string const& _s) +{ + assert(_s.size() % 2 == 0); + if (_s.size() < 2) + return bytes(); + uint s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; + std::vector ret; + ret.reserve((_s.size() - s) / 2); + for (uint i = s; i < _s.size(); i += 2) + ret.push_back(fromHex(_s[i]) * 16 + fromHex(_s[i + 1])); + return ret; +} + +bytes eth::toHex(std::string const& _s) +{ + std::vector ret; + ret.reserve(_s.size() * 2); + for (auto i: _s) + { + ret.push_back(i / 16); + ret.push_back(i % 16); + } + return ret; +} diff --git a/libethereum/Common.h b/libethereum/Common.h index a2460da53..b05df6d72 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -1,3 +1,26 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file Common.h + * @author Gav Wood + * @date 2014 + * + * Shared algorithms and data types. + */ + #pragma once #include @@ -35,6 +58,8 @@ using HexMap = std::map; template std::string toString(_T const& _t) { std::ostringstream o; o << _t; return o.str(); } +inline std::string asString(bytes const& _b) { return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); } + template inline std::string asHex(_T const& _data, int _w = 2) { std::ostringstream ret; @@ -58,41 +83,11 @@ template void pushFront(_T& _t, _U _e) class BadHexCharacter: public std::exception {}; -inline int fromHex(char _i) -{ - if (_i >= '0' && _i <= '9') - return _i - '0'; - if (_i >= 'a' && _i <= 'f') - return _i - 'a' + 10; - if (_i >= 'A' && _i <= 'F') - return _i - 'A' + 10; - throw BadHexCharacter(); -} - -inline bytes fromUserHex(std::string const& _s) -{ - assert(_s.size() % 2 == 0); - if (_s.size() < 2) - return bytes(); - uint s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; - std::vector ret; - ret.reserve((_s.size() - s) / 2); - for (uint i = s; i < _s.size(); i += 2) - ret.push_back(fromHex(_s[i]) * 16 + fromHex(_s[i + 1])); - return ret; -} - -inline bytes toHex(std::string const& _s) -{ - std::vector ret; - ret.reserve(_s.size() * 2); - for (auto i: _s) - { - ret.push_back(i / 16); - ret.push_back(i % 16); - } - return ret; -} +std::string randomWord(); +std::string escaped(std::string const& _s, bool _all = true); +int fromHex(char _i); +bytes fromUserHex(std::string const& _s); +bytes toHex(std::string const& _s); template inline void toBigEndian(_T _val, _Out& o_out) @@ -138,17 +133,6 @@ template uint commonPrefix(_T const& _t, _U const& _u) u256 ripemd160(bytesConstRef _message); -inline std::string randomWord() -{ - static std::mt19937_64 s_eng(0); - std::string ret(std::uniform_int_distribution(4, 10)(s_eng), ' '); - char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; - std::uniform_int_distribution d(0, sizeof(n) - 2); - for (char& c: ret) - c = n[d(s_eng)]; - return ret; -} - template inline u160 low160(_T const& _t) { diff --git a/libethereum/RLP.cpp b/libethereum/RLP.cpp index 5c48020c7..e56b68c50 100644 --- a/libethereum/RLP.cpp +++ b/libethereum/RLP.cpp @@ -1,5 +1,207 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file RLP.cpp + * @author Gav Wood + * @date 2014 + */ + #include "RLP.h" using namespace std; using namespace eth; -bytes eth::RLPNull = rlpBytes(""); +bytes eth::RLPNull = rlp(""); + +RLP::iterator& RLP::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()); + } + else + m_lastItem.retarget(m_lastItem.next().data(), 0); + return *this; +} + +RLP::iterator::iterator(RLP const& _parent, bool _begin) +{ + if (_begin && _parent.isList()) + { + auto pl = _parent.payload(); + m_lastItem = pl.cropped(0, RLP(pl).actualSize()); + m_remaining = pl.size() - m_lastItem.size(); + } + else + { + m_lastItem = _parent.data().cropped(_parent.data().size()); + m_remaining = 0; + } +} + +RLP RLP::operator[](uint _i) const +{ + if (!isList() || itemCount() <= _i) + return RLP(); + if (_i < m_lastIndex) + { + m_lastEnd = RLP(payload()).actualSize(); + m_lastItem = payload().cropped(0, 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); +} + +RLPs RLP::toList() const +{ + RLPs ret; + if (!isList()) + return ret; + uint64_t c = items(); + for (uint64_t i = 0; i < c; ++i) + ret.push_back(operator[](i)); + return ret; +} + +eth::uint RLP::actualSize() const +{ + if (isNull()) + return 0; + if (isInt()) + return 1 + intSize(); + if (isString()) + return payload().data() - m_data.data() + items(); + if (isList()) + { + bytesConstRef d = payload(); + uint64_t c = items(); + for (uint64_t i = 0; i < c; ++i, d = d.cropped(RLP(d).actualSize())) {} + return d.data() - m_data.data(); + } + return 0; +} + +eth::uint RLP::items() const +{ + auto n = (m_data[0] & 0x3f); + if (n < 0x38) + return n; + uint ret = 0; + for (int i = 0; i < n - 0x37; ++i) + ret = (ret << 8) | m_data[i + 1]; + return ret; +} + +RLPStream& RLPStream::append(std::string const& _s) +{ + if (_s.size() < 0x38) + m_out.push_back(_s.size() | 0x40); + else + pushCount(_s.size(), 0x40); + uint os = m_out.size(); + m_out.resize(os + _s.size()); + memcpy(m_out.data() + os, _s.data(), _s.size()); + return *this; +} + +RLPStream& RLPStream::appendList(uint _count) +{ + if (_count < 0x38) + m_out.push_back(_count | 0x80); + else + pushCount(_count, 0x80); + return *this; +} + +RLPStream& RLPStream::append(uint _i) +{ + if (_i < 0x18) + m_out.push_back(_i); + else + { + auto br = bytesRequired(_i); + m_out.push_back(br + 0x17); // max 8 bytes. + pushInt(_i, br); + } + return *this; +} + +RLPStream& RLPStream::append(u256 _i) +{ + if (_i < 0x18) + m_out.push_back((byte)_i); + else + { + auto br = bytesRequired(_i); + m_out.push_back(br + 0x17); // max 8 bytes. + pushInt(_i, br); + } + return *this; +} + +RLPStream& RLPStream::append(bigint _i) +{ + if (_i < 0x18) + m_out.push_back((byte)_i); + else + { + uint br = bytesRequired(_i); + if (br <= 32) + m_out.push_back(bytesRequired(_i) + 0x17); // max 32 bytes. + else + { + auto brbr = bytesRequired(br); + m_out.push_back(0x37 + brbr); + pushInt(br, brbr); + } + pushInt(_i, br); + } + return *this; +} + +void RLPStream::pushCount(uint _count, byte _base) +{ + auto br = bytesRequired(_count); + m_out.push_back(br + 0x37 + _base); // max 8 bytes. + pushInt(_count, br); +} + +std::ostream& operator<<(std::ostream& _out, eth::RLP _d) +{ + if (_d.isNull()) + _out << "null"; + else if (_d.isInt()) + _out << std::showbase << std::hex << std::nouppercase << _d.toBigInt(); + else if (_d.isString()) + _out << eth::escaped(_d.toString(), true); + else if (_d.isList()) + { + _out << "["; + int j = 0; + for (auto i: _d) + _out << (j++ ? ", " : " ") << i; + _out << " ]"; + } + + return _out; +} diff --git a/libethereum/RLP.h b/libethereum/RLP.h index d51c25294..b01fbccdc 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -1,3 +1,26 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file RLP.h + * @author Gav Wood + * @date 2014 + * + * RLP (de-)serialisation. + */ + #pragma once #include @@ -55,13 +78,13 @@ public: /// List value. bool isList() const { assert(!isNull()); return m_data[0] >= 0x80 && m_data[0] < 0xc0; } - /// Integer value. Either isSlimInt() or isBigInt(). + /// Integer value. Either isSlimInt(), isFatInt() or isBigInt(). bool isInt() const { assert(!isNull()); return m_data[0] < 0x40; } - /// Fits into eth::uint type. Can use toInt() to read (as well as toBigInt() or toHugeInt() ). + /// Fits into eth::uint type. Can use toSlimInt() to read (as well as toFatInt() or toBigInt() ). bool isSlimInt() const { assert(!isNull()); return m_data[0] < 0x20; } - /// Fits only into eth::u256 type. Use only toFatInt() or toBigInt() to read. + /// Fits into eth::u256 or eth::bigint type. Use only toFatInt() or toBigInt() to read. bool isFatInt() const { assert(!isNull()); return m_data[0] >= 0x20 && m_data[0] < 0x38; } /// Fits into eth::u256 type, though might fit into eth::uint type. @@ -77,32 +100,26 @@ public: /// @returns the number of characters in the string, or zero if it isn't a string. uint stringSize() const { return isString() ? items() : 0; } + /// Equality operators; does best-effort conversion and checks for equality. bool operator==(char const* _s) const { return isString() && toString() == _s; } + bool operator!=(char const* _s) const { return isString() && toString() != _s; } bool operator==(std::string const& _s) const { return isString() && toString() == _s; } - bool operator==(uint const& _i) const { return toSlimInt() == _i; } - bool operator==(u256 const& _i) const { return toFatInt() == _i; } - bool operator==(bigint const& _i) const { return toBigInt() == _i; } - RLP operator[](uint _i) const - { - if (!isList() || itemCount() <= _i) - return RLP(); - if (_i < m_lastIndex) - { - m_lastEnd = RLP(payload()).actualSize(); - m_lastItem = payload().cropped(0, 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); - } + bool operator!=(std::string const& _s) const { return isString() && toString() != _s; } + bool operator==(uint const& _i) const { return (isInt() || isString()) && toSlimInt() == _i; } + bool operator!=(uint const& _i) const { return (isInt() || isString()) && toSlimInt() != _i; } + bool operator==(u256 const& _i) const { return (isInt() || isString()) && toFatInt() == _i; } + bool operator!=(u256 const& _i) const { return (isInt() || isString()) && toFatInt() != _i; } + bool operator==(bigint const& _i) const { return (isInt() || isString()) && toBigInt() == _i; } + bool operator!=(bigint const& _i) const { return (isInt() || isString()) && toBigInt() != _i; } + + /// Subscript operator. + /// @returns the list item @a _i if isList() and @a _i < listItems(), or RLP() otherwise. + /// @note if used to access items in ascending order, this is efficient. + RLP operator[](uint _i) const; typedef RLP element_type; + /// @brief Iterator class for iterating through items of RLP list. class iterator { friend class RLP; @@ -111,18 +128,7 @@ 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()); - } - else - m_lastItem.retarget(m_lastItem.next().data(), 0); - return *this; - } + iterator& operator++(); 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; } @@ -130,41 +136,31 @@ public: private: iterator() {} - iterator(RLP const& _parent, bool _begin) - { - if (_begin) - { - auto pl = _parent.payload(); - m_lastItem = pl.cropped(0, RLP(pl).actualSize()); - m_remaining = pl.size() - m_lastItem.size(); - } - else - { - m_lastItem = _parent.data().cropped(_parent.data().size()); - m_remaining = 0; - } - } + iterator(RLP const& _parent, bool _begin); + uint m_remaining = 0; bytesConstRef m_lastItem; }; - friend class iterator; + /// @brief Iterator into beginning of sub-item list (valid only if we are a list). iterator begin() const { return iterator(*this, true); } + + /// @brief Iterator into end of sub-item list (valid only if we are a list). iterator end() const { return iterator(*this, false); } + /// Best-effort conversion operators. explicit operator std::string() const { return toString(); } explicit operator RLPs() const { return toList(); } explicit operator uint() const { return toSlimInt(); } explicit operator u256() const { return toFatInt(); } explicit operator bigint() const { return toBigInt(); } - std::string toString() const - { - if (!isString()) - return std::string(); - return payload().cropped(0, items()).toString(); - } + /// Converts to string. @returns the empty string if not a string. + std::string toString() const { if (!isString()) return std::string(); return payload().cropped(0, items()).toString(); } + /// Converts to string. @throws BadCast if not a string. + std::string toStringStrict() const { if (!isString()) throw BadCast(); return payload().cropped(0, items()).toString(); } + /// Converts to int of type given; if isString(), decodes as big-endian bytestream. @returns 0 if not an int or string. template _T toInt() const { if (!isString() && !isInt()) @@ -179,26 +175,29 @@ public: return ret; } + /// Converts to eth::uint. @see toInt() uint toSlimInt() const { return toInt(); } + /// Converts to eth::u256. @see toInt() u256 toFatInt() const { return toInt(); } + /// Converts to eth::bigint. @see toInt() bigint toBigInt() const { return toInt(); } + + /// Converts to eth::uint. @throws BadCast if not isInt(). @see toInt() uint toSlimIntStrict() const { if (!isSlimInt()) throw BadCast(); return toInt(); } + /// Converts to eth::u256. @throws BadCast if not isInt(). @see toInt() u256 toFatIntStrict() const { if (!isFatInt() && !isSlimInt()) throw BadCast(); return toInt(); } + /// Converts to eth::bigint. @throws BadCast if not isInt(). @see toInt() bigint toBigIntStrict() const { if (!isInt()) throw BadCast(); return toInt(); } + + /// Converts to eth::uint using the toString() as a big-endian bytestream. @throws BadCast if not isString(). @see toInt() uint toSlimIntFromString() const { if (!isString()) throw BadCast(); return toInt(); } + /// Converts to eth::u256 using the toString() as a big-endian bytestream. @throws BadCast if not isString(). @see toInt() u256 toFatIntFromString() const { if (!isString()) throw BadCast(); return toInt(); } + /// Converts to eth::bigint using the toString() as a big-endian bytestream. @throws BadCast if not isString(). @see toInt() bigint toBigIntFromString() const { if (!isString()) throw BadCast(); return toInt(); } - RLPs toList() const - { - RLPs ret; - if (!isList()) - return ret; - uint64_t c = items(); - for (uint64_t i = 0; i < c; ++i) - ret.push_back(operator[](i)); - return ret; - } + /// Converts to RLPs collection object. Useful if you need random access to sub items or will iterate over multiple times. + RLPs toList() const; private: /// Direct value integer. @@ -216,136 +215,67 @@ private: /// Direct-length list. bool isSmallList() const { assert(!isNull()); return m_data[0] >= 0x80 && m_data[0] < 0xb8; } - uint actualSize() const - { - if (isNull()) - return 0; - if (isInt()) - return 1 + intSize(); - if (isString()) - return payload().data() - m_data.data() + items(); - if (isList()) - { - bytesConstRef d = payload(); - uint64_t c = items(); - for (uint64_t i = 0; i < c; ++i, d = d.cropped(RLP(d).actualSize())) {} - return d.data() - m_data.data(); - } - return 0; - } + /// @returns the theoretical size of this item; if it's a list, will require a deep traversal which could take a while. + /// @note Under normal circumstances, is equivalent to m_data.size() - use that unless you know it won't work. + uint actualSize() const; + /// @returns the total additional bytes used to encode the integer. Includes the data-size and potentially the length-size. Returns 0 if not isInt(). uint intSize() const { return (!isInt() || isDirectValueInt()) ? 0 : isIndirectAddressedInt() ? lengthSize() + items() : (m_data[0] - 0x17); } + /// @returns the bytes used to encode the length of the data. Valid for all types. uint lengthSize() const { auto n = (m_data[0] & 0x3f); return n > 0x37 ? n - 0x37 : 0; } - uint items() const - { - auto n = (m_data[0] & 0x3f); - if (n < 0x38) - return n; - uint ret = 0; - for (int i = 0; i < n - 0x37; ++i) - ret = (ret << 8) | m_data[i + 1]; - return ret; - } - bytesConstRef payload() const - { - assert(isString() || isList()); - auto n = (m_data[0] & 0x3f); - return m_data.cropped(1 + (n < 0x38 ? 0 : (n - 0x37))); - } + /// @returns the number of data items (bytes in the case of strings & ints, items in the case of lists). Valid for all types. + uint items() const; + + /// @returns the data payload. Valid for all types. + bytesConstRef payload() const { auto n = (m_data[0] & 0x3f); return m_data.cropped(1 + (n < 0x38 ? 0 : (n - 0x37))); } + /// Our byte data. bytesConstRef m_data; + + /// The list-indexing cache. mutable uint m_lastIndex = (uint)-1; - mutable uint m_lastEnd; + mutable uint m_lastEnd = 0; mutable bytesConstRef m_lastItem; }; -struct RLPList { RLPList(uint _count): count(_count) {} uint count; }; - +/** + * @brief Class for writing to an RLP bytestream. + */ class RLPStream { public: + /// Initializes empty RLPStream. RLPStream() {} - void append(uint _s) { appendNumeric(_s); } - void append(u256 _s) { appendNumeric(_s); } - void append(bigint _s) { appendNumeric(_s); } - - void append(std::string const& _s) - { - if (_s.size() < 0x38) - m_out.push_back(_s.size() | 0x40); - else - pushCount(_s.size(), 0x40); - uint os = m_out.size(); - m_out.resize(os + _s.size()); - memcpy(m_out.data() + os, _s.data(), _s.size()); - } - - void appendList(uint _count) - { - if (_count < 0x38) - m_out.push_back(_count | 0x80); - else - pushCount(_count, 0x80); - } - - RLPStream& operator<<(uint _i) { append(_i); return *this; } - RLPStream& operator<<(u256 _i) { append(_i); return *this; } - RLPStream& operator<<(bigint _i) { append(_i); return *this; } - RLPStream& operator<<(char const* _s) { append(std::string(_s)); return *this; } - RLPStream& operator<<(std::string const& _s) { append(_s); return *this; } - RLPStream& operator<<(RLPList _l) { appendList(_l.count); return *this; } + /// Initializes the RLPStream as a list of @a _listItems items. + explicit RLPStream(uint _listItems) { appendList(_listItems); } + + /// Append given data to the byte stream. + RLPStream& append(uint _s); + RLPStream& append(u256 _s); + RLPStream& append(bigint _s); + RLPStream& append(std::string const& _s); + RLPStream& appendList(uint _count); + + /// Shift operators for appending data items. + RLPStream& operator<<(uint _i) { return append(_i); } + RLPStream& operator<<(u256 _i) { return append(_i); } + RLPStream& operator<<(bigint _i) { return append(_i); } + RLPStream& operator<<(char const* _s) { return append(std::string(_s)); } + RLPStream& operator<<(std::string const& _s) { return append(_s); } template RLPStream& operator<<(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } + /// Read the byte stream. bytes const& out() const { return m_out; } - std::string str() const { return std::string((char const*)m_out.data(), (char const*)(m_out.data() + m_out.size())); } private: - void appendNumeric(uint _i) - { - if (_i < 0x18) - m_out.push_back(_i); - else - { - auto br = bytesRequired(_i); - m_out.push_back(br + 0x17); // max 8 bytes. - pushInt(_i, br); - } - } - - void appendNumeric(u256 _i) - { - if (_i < 0x18) - m_out.push_back((byte)_i); - else - { - auto br = bytesRequired(_i); - m_out.push_back(br + 0x17); // max 8 bytes. - pushInt(_i, br); - } - } - - void appendNumeric(bigint _i) - { - if (_i < 0x18) - m_out.push_back((byte)_i); - else - { - uint br = bytesRequired(_i); - if (br <= 32) - m_out.push_back(bytesRequired(_i) + 0x17); // max 32 bytes. - else - { - auto brbr = bytesRequired(br); - m_out.push_back(0x37 + brbr); - pushInt(br, brbr); - } - pushInt(_i, br); - } - } + /// Push the node-type byte (using @a _base) along with the item count @a _count. + /// @arg _count is number of characters for strings, data-bytes for ints, or items for lists. + void pushCount(uint _count, byte _base); + /// Push an integer as a raw big-endian byte-stream. template void pushInt(_T _i, uint _br) { m_out.resize(m_out.size() + _br); @@ -354,13 +284,7 @@ private: *(b--) = (byte)_i; } - void pushCount(uint _count, byte _base) - { - auto br = bytesRequired(_count); - m_out.push_back(br + 0x37 + _base); // max 8 bytes. - pushInt(_count, br); - } - + /// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero. template static uint bytesRequired(_T _i) { _i >>= 8; @@ -369,93 +293,29 @@ private: return i; } + /// Our output byte stream. bytes m_out; }; -template void rlpListAux(RLPStream& _out, _T _t) -{ - _out << _t; -} - -template void rlpListAux(RLPStream& _out, _T _t, _Ts ... _ts) -{ - _out << _t; - rlpListAux(_out, _ts...); -} - -template std::string rlp(_T _t) -{ - RLPStream out; - out << _t; - return out.str(); -} - -template bytes rlpBytes(_T _t) -{ - RLPStream out; - out << _t; - return out.out(); -} +template void rlpListAux(RLPStream& _out, _T _t) { _out << _t; } +template void rlpListAux(RLPStream& _out, _T _t, _Ts ... _ts) { rlpListAux(_out << _t, _ts...); } -template std::string rlpList(_Ts ... _ts) -{ - RLPStream out; - out << RLPList(sizeof ...(_Ts)); - rlpListAux(out, _ts...); - return out.str(); -} +/// Export a single item in RLP format, returning a byte array. +template bytes rlp(_T _t) { return (RLPStream() << _t).out(); } -template bytes rlpListBytes(_Ts ... _ts) +/// Export a list of items in RLP format, returning a byte array. +inline bytes rlpList() { return RLPStream(0).out(); } +template bytes rlpList(_Ts ... _ts) { - RLPStream out; - out << RLPList(sizeof ...(_Ts)); + RLPStream out(sizeof ...(_Ts)); rlpListAux(out, _ts...); return out.out(); } +/// The empty string in RLP format. extern bytes RLPNull; } -inline std::string escaped(std::string const& _s, bool _all = true) -{ - std::string ret; - ret.reserve(_s.size()); - ret.push_back('"'); - for (auto i: _s) - if (i == '"' && !_all) - ret += "\\\""; - else if (i == '\\' && !_all) - ret += "\\\\"; - else if (i < ' ' || i > 127 || _all) - { - ret += "\\x"; - ret.push_back("0123456789abcdef"[(uint8_t)i / 16]); - ret.push_back("0123456789abcdef"[(uint8_t)i % 16]); - } - else - ret.push_back(i); - ret.push_back('"'); - return ret; -} - -inline std::ostream& operator<<(std::ostream& _out, eth::RLP _d) -{ - if (_d.isNull()) - _out << "null"; - else if (_d.isInt()) - _out << std::showbase << std::hex << std::nouppercase << _d.toBigInt(); - else if (_d.isString()) - _out << escaped(_d.toString(), true); - else if (_d.isList()) - { - _out << "["; - int j = 0; - for (auto i: _d) - _out << (j++ ? ", " : " ") << i; - _out << " ]"; - } - - return _out; -} - +/// Human readable version of RLP. +std::ostream& operator<<(std::ostream& _out, eth::RLP _d); diff --git a/libethereum/State.cpp b/libethereum/State.cpp index ee963a57e..ee93f2cdf 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1,3 +1,24 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file State.cpp + * @author Gav Wood + * @date 2014 + */ + #include #include #include "Trie.h" @@ -16,91 +37,51 @@ u256 const State::c_cryptoFee = 0; u256 const State::c_newContractFee = 0; u256 const State::c_txFee = 0; -std::mt19937_64* State::s_engine = nullptr; - -u256 kFromMessage(u256 _msg, u256 _priv) -{ - /* - v = '\x01' * 32 - k = '\x00' * 32 - priv = encode_privkey(priv,'bin') - msghash = encode(hash_to_int(msghash),256,32) - k = hmac.new(k, v+'\x00'+priv+msghash, hashlib.sha256).digest() - v = hmac.new(k, v, hashlib.sha256).digest() - k = hmac.new(k, v+'\x01'+priv+msghash, hashlib.sha256).digest() - v = hmac.new(k, v, hashlib.sha256).digest() - return decode(hmac.new(k, v, hashlib.sha256).digest(),256) - */ - return 0; -} - -Address Transaction::sender() const +State::State(Address _minerAddress): m_minerAddress(_minerAddress) { - State::ensureCrypto(); - - bytes sig = toBigEndian(vrs.r) + toBigEndian(vrs.s); - assert(sig.size() == 64); - bytes msg = sha256Bytes(false); - - byte pubkey[65]; - int pubkeylen = 65; - if (!secp256k1_ecdsa_recover_compact(msg.data(), msg.size(), sig.data(), pubkey, &pubkeylen, 0, (int)vrs.v - 27)) - throw InvalidSignature(); - return low160(eth::sha256(bytesConstRef(&pubkey[1], 64))); + secp256k1_start(); } -void Transaction::sign(PrivateKey _priv) +bool State::isNormalAddress(Address _address) const { - bytes sig(64); - bytes nonce(32); - for (auto& i: nonce) - i = std::uniform_int_distribution(0, 255)(State::engine()); - int v = 0; - - bytes msg = sha256Bytes(false); - if (!secp256k1_ecdsa_sign_compact(msg.data(), msg.size(), sig.data(), toBigEndian(_priv).data(), nonce.data(), &v)) - throw InvalidSignature(); - - vrs.v = v + 27; - vrs.r = fromBigEndian(bytesConstRef(&sig).cropped(0, 32)); - vrs.s = fromBigEndian(bytesConstRef(&sig).cropped(32)); + auto it = m_current.find(_address); + return it != m_current.end() && it->second.type() == AddressType::Normal; } -Transaction::Transaction(bytes const& _rlpData) +bool State::isContractAddress(Address _address) const { - RLP rlp(_rlpData); - nonce = rlp[0].toFatIntStrict(); - 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{ (byte)rlp[5].toSlimIntStrict(), rlp[6].toFatIntStrict(), rlp[7].toFatIntStrict() }; + auto it = m_current.find(_address); + return it != m_current.end() && it->second.type() == AddressType::Contract; } -void Transaction::fillStream(RLPStream& _s, bool _sig) const +u256 State::balance(Address _id) const { - _s << RLPList(_sig ? 8 : 5) << nonce << toCompactBigEndianString(receiveAddress) << value << fee << data; - if (_sig) - _s << toCompactBigEndianString(vrs.v) << toCompactBigEndianString(vrs.r) << toCompactBigEndianString(vrs.s); + auto it = m_current.find(_id); + return it == m_current.end() ? 0 : it->second.balance(); } -State::State(Address _minerAddress): m_minerAddress(_minerAddress) +void State::addBalance(Address _id, u256 _amount) { - ensureCrypto(); + auto it = m_current.find(_id); + if (it == m_current.end()) + it->second.balance() = _amount; + else + it->second.balance() += _amount; } -void State::ensureCrypto() +bool State::subBalance(Address _id, bigint _amount) { - secp256k1_start(); + 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; } -mt19937_64& State::engine() +u256 State::transactionsFrom(Address _address) const { - if (!s_engine) - s_engine = new mt19937_64(random_device()()); - return *s_engine; + auto it = m_current.find(_address); + return it == m_current.end() ? 0 : it->second.nonce(); } u256 State::contractMemory(Address _contract, u256 _memory) const @@ -112,6 +93,11 @@ u256 State::contractMemory(Address _contract, u256 _memory) const return i == m->second.memory().end() ? 0 : i->second; } +bool State::verify(bytes const& _block) +{ + return true; +} + void State::execute(Transaction const& _t, Address _sender) { // Entry point for a contract-originated transaction. @@ -445,7 +431,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ stack.pop_back(); u256 priv = stack.back(); stack.pop_back(); - bytes nonce = toBigEndian(kFromMessage(msg, priv)); + bytes nonce = toBigEndian(Transaction::kFromMessage(msg, priv)); if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v)) throw InvalidSignature(); diff --git a/libethereum/State.h b/libethereum/State.h index def1f7607..3c9b828ae 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -1,61 +1,58 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file State.h + * @author Gav Wood + * @date 2014 + */ + #pragma once #include #include #include +#include "Common.h" +#include "RLP.h" #include "Exceptions.h" -#include "AddressState.h" #include "BlockInfo.h" -#include "RLP.h" -#include "Common.h" +#include "AddressState.h" +#include "Transaction.h" namespace eth { -struct Signature -{ - byte v; - u256 r; - u256 s; -}; - -using PrivateKey = u256; -using Address = u160; - -// [ 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; - Address receiveAddress; - u256 value; - u256 fee; - u256s data; - Signature vrs; - - Address sender() const; - void sign(PrivateKey _priv); - - void fillStream(RLPStream& _s, bool _sig = true) const; - bytes rlp(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return s.out(); } - std::string rlpString(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return s.str(); } - u256 sha256(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return eth::sha256(s.out()); } - bytes sha256Bytes(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return eth::sha256Bytes(s.out()); } -}; - class State { public: explicit State(Address _minerAddress); - static void ensureCrypto(); - static std::mt19937_64& engine(); - bool verify(bytes const& _block); bool execute(bytes const& _rlp) { try { Transaction t(_rlp); execute(t, t.sender()); } catch (...) { return false; } } + bool isNormalAddress(Address _address) const; + bool isContractAddress(Address _address) const; + + u256 balance(Address _id) const; + void addBalance(Address _id, u256 _amount); + // bigint as we don't want any accidental problems with -ve numbers. + bool subBalance(Address _id, bigint _amount); + + u256 contractMemory(Address _contract, u256 _memory) const; + u256 transactionsFrom(Address _address) const; + private: struct MinerFeeAdder { @@ -65,19 +62,6 @@ private: }; 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; } - - u256 balance(Address _id) const { auto it = m_current.find(_id); return it == m_current.end() ? 0 : it->second.balance(); } - void addBalance(Address _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(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; - - u256 transactionsFrom(Address _address) { auto it = m_current.find(_address); return it == m_current.end() ? 0 : it->second.nonce(); } - void execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* o_totalFee); std::map m_current; @@ -93,8 +77,6 @@ private: static const u256 c_cryptoFee; static const u256 c_newContractFee; static const u256 c_txFee; - - static std::mt19937_64* s_engine; }; } diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp new file mode 100644 index 000000000..b78ec6ee0 --- /dev/null +++ b/libethereum/Transaction.cpp @@ -0,0 +1,94 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file Transaction.cpp + * @author Gav Wood + * @date 2014 + */ + +#include +#include "Exceptions.h" +#include "Transaction.h" +using namespace std; +using namespace eth; + +Transaction::Transaction(bytes const& _rlpData) +{ + RLP rlp(_rlpData); + nonce = rlp[0].toFatIntStrict(); + 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{ (byte)rlp[5].toSlimIntStrict(), rlp[6].toFatIntStrict(), rlp[7].toFatIntStrict() }; +} + +Address Transaction::sender() const +{ + secp256k1_start(); + + bytes sig = toBigEndian(vrs.r) + toBigEndian(vrs.s); + assert(sig.size() == 64); + bytes msg = sha256Bytes(false); + + byte pubkey[65]; + int pubkeylen = 65; + if (!secp256k1_ecdsa_recover_compact(msg.data(), msg.size(), sig.data(), pubkey, &pubkeylen, 0, (int)vrs.v - 27)) + throw InvalidSignature(); + return low160(eth::sha256(bytesConstRef(&(pubkey[1]), 64))); +} + +void Transaction::sign(PrivateKey _priv) +{ + int v = 0; + + u256 msg = sha256(false); + byte sig[64]; + if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 32, sig, toBigEndian(_priv).data(), toBigEndian(kFromMessage(msg, _priv)).data(), &v)) + throw InvalidSignature(); + + vrs.v = v + 27; + vrs.r = fromBigEndian(bytesConstRef(sig, 32)); + vrs.s = fromBigEndian(bytesConstRef(&(sig[32]), 32)); +} + +void Transaction::fillStream(RLPStream& _s, bool _sig) const +{ + _s.appendList(_sig ? 8 : 5); + _s << nonce << toCompactBigEndianString(receiveAddress) << value << fee << data; + if (_sig) + _s << toCompactBigEndianString(vrs.v) << toCompactBigEndianString(vrs.r) << toCompactBigEndianString(vrs.s); +} + +u256 Transaction::kFromMessage(u256 _msg, u256 _priv) +{ + // TODO! + /* + v = '\x01' * 32 + k = '\x00' * 32 + priv = encode_privkey(priv,'bin') + msghash = encode(hash_to_int(msghash),256,32) + k = hmac.new(k, v+'\x00'+priv+msghash, hashlib.sha256).digest() + v = hmac.new(k, v, hashlib.sha256).digest() + k = hmac.new(k, v+'\x01'+priv+msghash, hashlib.sha256).digest() + v = hmac.new(k, v, hashlib.sha256).digest() + return decode(hmac.new(k, v, hashlib.sha256).digest(),256) + */ + return 0; +} + diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h new file mode 100644 index 000000000..a7297eee8 --- /dev/null +++ b/libethereum/Transaction.h @@ -0,0 +1,68 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file Transaction.h + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include "Common.h" +#include "sha256.h" +#include "RLP.h" + +namespace eth +{ + +using PrivateKey = u256; +using Address = u160; + +struct Signature +{ + byte v; + u256 r; + u256 s; +}; + +// [ 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; + Address receiveAddress; + u256 value; + u256 fee; + u256s data; + Signature vrs; + + Address sender() const; + void sign(PrivateKey _priv); + + static u256 kFromMessage(u256 _msg, u256 _priv); + + void fillStream(RLPStream& _s, bool _sig = true) const; + bytes rlp(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return s.out(); } + std::string rlpString(bool _sig = true) const { return asString(rlp()); } + u256 sha256(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return eth::sha256(s.out()); } + bytes sha256Bytes(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return eth::sha256Bytes(s.out()); } +}; + +} + + diff --git a/libethereum/Trie.cpp b/libethereum/Trie.cpp index 874b18464..7ff4339d7 100644 --- a/libethereum/Trie.cpp +++ b/libethereum/Trie.cpp @@ -1,3 +1,24 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file Trie.cpp + * @author Gav Wood + * @date 2014 + */ + #include "Common.h" #include "Trie.h" using namespace std; @@ -59,7 +80,7 @@ u256 hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i else if (std::next(_begin) == _end) { // only one left - terminate with the pair. - rlp << RLPList(2) << hexPrefixEncode(_begin->first, true, _preLen) << _begin->second; + rlp.appendList(2) << hexPrefixEncode(_begin->first, true, _preLen) << _begin->second; #if ENABLE_DEBUG_PRINT if (g_hashDebug) std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, _begin->first.size() - _preLen), 1) << ": " << _begin->second << " = " << sha256(rlp.out()) << std::endl; @@ -85,7 +106,7 @@ u256 hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i if (g_hashDebug) std::cerr << s_indent << asHex(bytesConstRef(_begin->first.data() + _preLen, sharedPre), 1) << ": " << std::endl; #endif - rlp << RLPList(2) << hexPrefixEncode(_begin->first, false, _preLen, sharedPre) << toCompactBigEndianString(hash256aux(_s, _begin, _end, sharedPre)); + rlp.appendList(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; @@ -94,7 +115,7 @@ u256 hash256aux(HexMap const& _s, HexMap::const_iterator _begin, HexMap::const_i else { // otherwise enumerate all 16+1 entries. - rlp << RLPList(17); + rlp.appendList(17); auto b = _begin; if (_preLen == b->first.size()) { @@ -156,7 +177,7 @@ u256 hash256(u256Map const& _s) return sha256(RLPNull); HexMap hexMap; for (auto i = _s.rbegin(); i != _s.rend(); ++i) - hexMap[toHex(toBigEndianString(i->first))] = rlp(i->second); + hexMap[toHex(toBigEndianString(i->first))] = asString(rlp(i->second)); return hash256aux(hexMap, hexMap.cbegin(), hexMap.cend(), 0); } @@ -278,7 +299,7 @@ public: 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); } + virtual bytes rlp() const override { return rlpList(hexPrefixEncode(m_ext, true), m_value); } private: bool contains(bytesConstRef _key) const { return _key.size() == m_ext.size() && !memcmp(_key.data(), m_ext.data(), _key.size()); } @@ -303,7 +324,7 @@ public: 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())); } + virtual bytes rlp() const override { assert(m_next); return rlpList(hexPrefixEncode(m_ext, false), toCompactBigEndianString(m_next->sha256())); } private: bool contains(bytesConstRef _key) const { return _key.size() >= m_ext.size() && !memcmp(_key.data(), m_ext.data(), m_ext.size()); } @@ -409,8 +430,7 @@ TrieNode* TrieBranchNode::rejig() bytes TrieBranchNode::rlp() const { - RLPStream s; - s << RLPList(17); + RLPStream s(17); for (auto i: m_nodes) s << (i ? toCompactBigEndianString(i->sha256()) : ""); s << m_value; diff --git a/libethereum/Trie.h b/libethereum/Trie.h index 4941197e5..10bb94816 100644 --- a/libethereum/Trie.h +++ b/libethereum/Trie.h @@ -1,3 +1,24 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file Trie.h + * @author Gav Wood + * @date 2014 + */ + #pragma once #include diff --git a/libethereum/sha256.cpp b/libethereum/sha256.cpp index e46b7c448..9bed1f66e 100644 --- a/libethereum/sha256.cpp +++ b/libethereum/sha256.cpp @@ -1,3 +1,26 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file sha256.cpp + * @author Gav Wood + * @author Oliver Gay + * @date 2014 + * @note Modified from an original version with BSD licence. + */ + /* * Updated to C++, zedwood.com 2012 * Based on Olivier Gay's version diff --git a/libethereum/sha256.h b/libethereum/sha256.h index 953b9920e..68eec4e53 100644 --- a/libethereum/sha256.h +++ b/libethereum/sha256.h @@ -1,3 +1,26 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file sha256.h + * @author Gav Wood + * @author Oliver Gay + * @date 2014 + * @note Modified from an original version with BSD licence. + */ + #pragma once #include diff --git a/libethereum/vector_ref.h b/libethereum/vector_ref.h new file mode 100644 index 000000000..61bebca17 --- /dev/null +++ b/libethereum/vector_ref.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +namespace eth +{ + +template +class vector_ref +{ +public: + typedef _T value_type; + typedef _T element_type; + + vector_ref(): m_data(nullptr), m_count(0) {} + vector_ref(std::vector::type>* _data): m_data(_data->data()), m_count(_data->size()) {} + vector_ref(_T* _data, unsigned _count): m_data(_data), m_count(_count) {} + vector_ref(std::string* _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} + // TODO: const variants enabled only if is_const<_T> + + 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 vector_ref<_T2>() const { assert(m_count * sizeof(_T) / sizeof(_T2) * sizeof(_T2) / sizeof(_T) == m_count); return vector_ref<_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; } + vector_ref<_T> next() const { return vector_ref<_T>(m_data + m_count, m_count); } + vector_ref<_T> cropped(unsigned _begin, int _count = -1) const { if (m_data && _begin + std::max(0, _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count < 0 ? m_count - _begin : _count); else return vector_ref<_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]; } + + bool operator==(vector_ref<_T> const& _cmp) const { return m_data == _cmp.m_data && m_count == _cmp.m_count; } + bool operator!=(vector_ref<_T> const& _cmp) const { return !operator==(); } + + void reset() { m_data = nullptr; m_count = 0; } + +private: + _T* m_data; + unsigned m_count; +}; + +} diff --git a/test/main.cpp b/test/main.cpp index 9ed0bfc4b..02ad7761a 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ +/** @file main.cpp + * @author Gav Wood + * @date 2014 + * Main test functions. + */ + #include #include #include @@ -7,8 +29,11 @@ using namespace std; using namespace eth; +// TODO: utilise the shared testdata. + int main() { + // Test transaction. bytes tx = fromUserHex("88005401010101010101010101010101010101010101011f0de0b6b3a76400001ce8d4a5100080181c373130a009ba1f10285d4e659568bfcfec85067855c5a3c150100815dad4ef98fd37cf0593828c89db94bd6c64e210a32ef8956eaa81ea9307194996a3b879441f5d"); cout << "TX: " << RLP(tx) << endl; @@ -123,38 +148,38 @@ int main() // int of value 15 assert(RLP("\x0f") == 15); - assert(rlp(15) == "\x0f"); + assert(asString(rlp(15)) == "\x0f"); // 3-character string assert(RLP("\x43""dog") == "dog"); - assert(rlp("dog") == "\x43""dog"); + assert(asString(rlp("dog")) == "\x43""dog"); // 2-item list RLP twoItemList("\x82\x0f\x43""dog"); assert(twoItemList.itemCount() == 2); assert(twoItemList[0] == 15); assert(twoItemList[1] == "dog"); - assert(rlpList(15, "dog") == "\x82\x0f\x43""dog"); + assert(asString(rlpList(15, "dog")) == "\x82\x0f\x43""dog"); // 1-byte (8-bit) int assert(RLP("\x18\x45") == 69); - assert(rlp(69) == "\x18\x45"); + assert(asString(rlp(69)) == "\x18\x45"); // 2-byte (16-bit) int assert(RLP("\x19\x01\x01") == 257); - assert(rlp(257) == "\x19\x01\x01"); + assert(asString(rlp(257)) == "\x19\x01\x01"); // 32-byte (256-bit) int assert(RLP("\x37\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f") == bigint("0x100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")); - assert(rlp(bigint("0x100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")) == "\x37\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"); + assert(asString(rlp(bigint("0x100102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"))) == "\x37\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"); // 33-byte (264-bit) int assert(RLP("\x38\x21\x20\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f") == bigint("0x20100102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")); - assert(rlp(bigint("0x20100102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")) == "\x38\x21\x20\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"); + assert(asString(rlp(bigint("0x20100102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"))) == "\x38\x21\x20\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"); // 56-character string. assert(RLP("\x78\x38""Lorem ipsum dolor sit amet, consectetur adipisicing elit") == "Lorem ipsum dolor sit amet, consectetur adipisicing elit"); - assert(rlp("Lorem ipsum dolor sit amet, consectetur adipisicing elit") == "\x78\x38""Lorem ipsum dolor sit amet, consectetur adipisicing elit"); + assert(asString(rlp("Lorem ipsum dolor sit amet, consectetur adipisicing elit")) == "\x78\x38""Lorem ipsum dolor sit amet, consectetur adipisicing elit"); /* * Hex-prefix Notation. First nibble has flags: oddness = 2^0 & termination = 2^1