Browse Source

Coding standards doc.

Some repotting.
Documentation.
Some minor API cleanups.
cl-refactor
Gav Wood 11 years ago
parent
commit
ade3d0225d
  1. 131
      CodingStandards.txt
  2. 11
      README.md
  3. 11
      astylerc
  4. 21
      libethereum/AddressState.cpp
  5. 25
      libethereum/AddressState.h
  6. 26
      libethereum/BlockInfo.cpp
  7. 21
      libethereum/BlockInfo.h
  8. 91
      libethereum/Common.cpp
  9. 76
      libethereum/Common.h
  10. 204
      libethereum/RLP.cpp
  11. 376
      libethereum/RLP.h
  12. 120
      libethereum/State.cpp
  13. 90
      libethereum/State.h
  14. 94
      libethereum/Transaction.cpp
  15. 68
      libethereum/Transaction.h
  16. 36
      libethereum/Trie.cpp
  17. 21
      libethereum/Trie.h
  18. 23
      libethereum/sha256.cpp
  19. 23
      libethereum/sha256.h
  20. 56
      libethereum/vector_ref.h
  21. 41
      test/main.cpp

131
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 <cassert>
using namespace std;
tuple<float, float> meanAndSigma(vector<float> const& _v);
(CORRECT)
#include <cassert>
std::tuple<float, float> meanAndSigma(std::vector<float> 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<float> _v, float* _sigma);
(CORRECT)
double const d = 0;
int i;
int j;
char* s;
std::tuple<float, float> meanAndSigma(std::vector<float> 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.

11
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.

11
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

21
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 <http://www.gnu.org/licenses/>.
*/
/** @file AddressState.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Trie.h"
#include "AddressState.h"
using namespace std;

25
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 <http://www.gnu.org/licenses/>.
*/
/** @file AddressState.h
* @author Gav Wood <i@gavwood.com>
* @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 "";
}

26
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 <http://www.gnu.org/licenses/>.
*/
/** @file BlockInfo.cpp
* @author Gav Wood <i@gavwood.com>
* @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;

21
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 <http://www.gnu.org/licenses/>.
*/
/** @file BlockInfo.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include "Common.h"

91
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 <http://www.gnu.org/licenses/>.
*/
/** @file Common.cpp
* @author Gav Wood <i@gavwood.com>
* @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<int>(4, 10)(s_eng), ' ');
char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
std::uniform_int_distribution<int> 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<uint8_t> 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<uint8_t> ret;
ret.reserve(_s.size() * 2);
for (auto i: _s)
{
ret.push_back(i / 16);
ret.push_back(i % 16);
}
return ret;
}

76
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 <http://www.gnu.org/licenses/>.
*/
/** @file Common.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* Shared algorithms and data types.
*/
#pragma once
#include <map>
@ -35,6 +58,8 @@ using HexMap = std::map<bytes, std::string>;
template <class _T> 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 <class _T> inline std::string asHex(_T const& _data, int _w = 2)
{
std::ostringstream ret;
@ -58,41 +83,11 @@ template <class _T, class _U> 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<uint8_t> 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<uint8_t> 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 <class _T, class _Out>
inline void toBigEndian(_T _val, _Out& o_out)
@ -138,17 +133,6 @@ template <class _T, class _U> 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<int>(4, 10)(s_eng), ' ');
char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
std::uniform_int_distribution<int> d(0, sizeof(n) - 2);
for (char& c: ret)
c = n[d(s_eng)];
return ret;
}
template <class _T>
inline u160 low160(_T const& _t)
{

204
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 <http://www.gnu.org/licenses/>.
*/
/** @file RLP.cpp
* @author Gav Wood <i@gavwood.com>
* @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<uint>(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;
}

376
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 <http://www.gnu.org/licenses/>.
*/
/** @file RLP.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*
* RLP (de-)serialisation.
*/
#pragma once
#include <exception>
@ -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<uint>(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 <class _T = uint> _T toInt() const
{
if (!isString() && !isInt())
@ -179,26 +175,29 @@ public:
return ret;
}
/// Converts to eth::uint. @see toInt()
uint toSlimInt() const { return toInt<uint>(); }
/// Converts to eth::u256. @see toInt()
u256 toFatInt() const { return toInt<u256>(); }
/// Converts to eth::bigint. @see toInt()
bigint toBigInt() const { return toInt<bigint>(); }
/// Converts to eth::uint. @throws BadCast if not isInt(). @see toInt()
uint toSlimIntStrict() const { if (!isSlimInt()) throw BadCast(); return toInt<uint>(); }
/// Converts to eth::u256. @throws BadCast if not isInt(). @see toInt()
u256 toFatIntStrict() const { if (!isFatInt() && !isSlimInt()) throw BadCast(); return toInt<u256>(); }
/// Converts to eth::bigint. @throws BadCast if not isInt(). @see toInt()
bigint toBigIntStrict() const { if (!isInt()) throw BadCast(); return toInt<bigint>(); }
/// 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<uint>(); }
/// 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<u256>(); }
/// 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<bigint>(); }
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 <class _T> 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 <class _T> 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 <class _T> static uint bytesRequired(_T _i)
{
_i >>= 8;
@ -369,93 +293,29 @@ private:
return i;
}
/// Our output byte stream.
bytes m_out;
};
template <class _T> void rlpListAux(RLPStream& _out, _T _t)
{
_out << _t;
}
template <class _T> void rlpListAux(RLPStream& _out, _T _t) { _out << _t; }
template <class _T, class ... _Ts> void rlpListAux(RLPStream& _out, _T _t, _Ts ... _ts) { rlpListAux(_out << _t, _ts...); }
template <class _T, class ... _Ts> void rlpListAux(RLPStream& _out, _T _t, _Ts ... _ts)
{
_out << _t;
rlpListAux(_out, _ts...);
}
template <class _T> std::string rlp(_T _t)
{
RLPStream out;
out << _t;
return out.str();
}
template <class _T> bytes rlpBytes(_T _t)
{
RLPStream out;
out << _t;
return out.out();
}
template <class ... _Ts> 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 <class _T> bytes rlp(_T _t) { return (RLPStream() << _t).out(); }
template <class ... _Ts> bytes rlpListBytes(_Ts ... _ts)
/// Export a list of items in RLP format, returning a byte array.
inline bytes rlpList() { return RLPStream(0).out(); }
template <class ... _Ts> 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);

120
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 <http://www.gnu.org/licenses/>.
*/
/** @file State.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <secp256k1.h>
#include <random>
#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<byte>(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<u256>(bytesConstRef(&sig).cropped(0, 32));
vrs.s = fromBigEndian<u256>(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();

90
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 <http://www.gnu.org/licenses/>.
*/
/** @file State.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <array>
#include <map>
#include <unordered_map>
#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<Address, AddressState> 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;
};
}

94
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 <http://www.gnu.org/licenses/>.
*/
/** @file Transaction.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <secp256k1.h>
#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<u256>(bytesConstRef(sig, 32));
vrs.s = fromBigEndian<u256>(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;
}

68
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 <http://www.gnu.org/licenses/>.
*/
/** @file Transaction.h
* @author Gav Wood <i@gavwood.com>
* @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()); }
};
}

36
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 <http://www.gnu.org/licenses/>.
*/
/** @file Trie.cpp
* @author Gav Wood <i@gavwood.com>
* @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;

21
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 <http://www.gnu.org/licenses/>.
*/
/** @file Trie.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <map>

23
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 <http://www.gnu.org/licenses/>.
*/
/** @file sha256.cpp
* @author Gav Wood <i@gavwood.com>
* @author Oliver Gay <olivier.gay@a3.epfl.ch>
* @date 2014
* @note Modified from an original version with BSD licence.
*/
/*
* Updated to C++, zedwood.com 2012
* Based on Olivier Gay's version

23
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 <http://www.gnu.org/licenses/>.
*/
/** @file sha256.h
* @author Gav Wood <i@gavwood.com>
* @author Oliver Gay <olivier.gay@a3.epfl.ch>
* @date 2014
* @note Modified from an original version with BSD licence.
*/
#pragma once
#include <string>

56
libethereum/vector_ref.h

@ -0,0 +1,56 @@
#pragma once
#include <cassert>
#include <vector>
#include <string>
namespace eth
{
template <class _T>
class vector_ref
{
public:
typedef _T value_type;
typedef _T element_type;
vector_ref(): m_data(nullptr), m_count(0) {}
vector_ref(std::vector<typename std::remove_const<_T>::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 <class _T2> 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;
};
}

41
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 <http://www.gnu.org/licenses/>.
*/
/** @file main.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
* Main test functions.
*/
#include <random>
#include <Common.h>
#include <secp256k1.h>
@ -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

Loading…
Cancel
Save