diff --git a/libethereum/Common.h b/libethereum/Common.h index baaaddd65..2ae3746fb 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -98,6 +99,9 @@ inline void toBigEndian(_T _val, _Out& o_out) inline std::string toBigEndianString(u256 _val) { std::string ret(32, '\0'); toBigEndian(_val, ret); return ret; } inline std::string toBigEndianString(u160 _val) { std::string ret(20, '\0'); toBigEndian(_val, ret); return ret; } +inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } +inline bytes toBigEndian(u160 _val) { bytes ret(20); toBigEndian(_val, ret); return ret; } + template inline std::string toCompactBigEndianString(_T _val) { @@ -119,4 +123,25 @@ 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 std::vector<_T>& operator+=(std::vector<_T>& _a, std::vector<_T> const& _b) +{ + auto s = _a.size(); + _a.resize(_a.size() + _b.size()); + memcpy(_a.data() + s, _b.data(), _b.size() * sizeof(_T)); + return _a; + +} +template inline std::vector<_T> operator+(std::vector<_T> const& _a, std::vector<_T> const& _b) { std::vector<_T> ret(_a); return ret += _b; } + } diff --git a/libethereum/VirtualMachine.cpp b/libethereum/VirtualMachine.cpp index a94291cc4..5dffa604d 100644 --- a/libethereum/VirtualMachine.cpp +++ b/libethereum/VirtualMachine.cpp @@ -12,6 +12,23 @@ u256 const State::c_extroFee = 0; u256 const State::c_cryptoFee = 0; u256 const State::c_newContractFee = 0; +u160 Transaction::sender() const +{ + State::ensureCrypto(); + + bytes sig = toBigEndian(vrs.r) + toBigEndian(vrs.s); + assert(sig.size() == 64); + RLPStream rlp; + fillStream(rlp, false); + bytes msg = eth::sha256Bytes(rlp.out()); + + bytes pubkey(65); + int pubkeylen = 65; + if (!secp256k1_ecdsa_recover_compact(msg.data(), msg.size(), sig.data(), pubkey.data(), &pubkeylen, 0, (int)vrs.v - 27)) + throw InvalidSignature(); + return low160(eth::sha256(bytesConstRef(&pubkey).cropped(1))); +} + Transaction::Transaction(bytes const& _rlpData) { RLP rlp(_rlpData); @@ -27,11 +44,16 @@ Transaction::Transaction(bytes const& _rlpData) void Transaction::fillStream(RLPStream& _s, bool _sig) const { - _s << RLPList(8) << nonce << toCompactBigEndianString(receiveAddress) << value << fee << data; + _s << RLPList(_sig ? 8 : 5) << nonce << toCompactBigEndianString(receiveAddress) << value << fee << data; if (_sig) _s << toCompactBigEndianString(vrs.v) << toCompactBigEndianString(vrs.r) << toCompactBigEndianString(vrs.s); } +void State::ensureCrypto() +{ + secp256k1_start(); +} + bool State::execute(Transaction const& _t, u160 _sender) { // Entry point for a contract-originated transaction. diff --git a/libethereum/VirtualMachine.h b/libethereum/VirtualMachine.h index 61c81adcf..fa3b04a25 100644 --- a/libethereum/VirtualMachine.h +++ b/libethereum/VirtualMachine.h @@ -70,6 +70,7 @@ class StackTooSmall: public std::exception { public: StackTooSmall(u256 _req, u2 class OperandOutOfRange: public std::exception { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; class ExecutionException: public std::exception {}; class NoSuchContract: public std::exception {}; +class InvalidSignature: public std::exception {}; class InvalidTransactionFormat: public std::exception {}; class InvalidBlockFormat: public std::exception {}; class InvalidUnclesHash: public std::exception {}; @@ -186,11 +187,6 @@ struct Signature u256 v; u256 r; u256 s; - - u160 address(bytesConstRef _tx) const - { - return as160(s); - } }; @@ -207,6 +203,8 @@ struct Transaction u256s data; Signature vrs; + u160 sender() const; + 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(); } @@ -218,8 +216,10 @@ class State public: explicit State(u256 _minerAddress): m_minerAddress(_minerAddress) {} + static void ensureCrypto(); + bool verify(bytes const& _block); - bool execute(bytes const& _rlp) { try { Transaction t(_rlp); u160 sender = t.vrs.address(bytesConstRef(const_cast(&_rlp))); return execute(t, sender); } catch (...) { return false; } } // remove const_cast once vector_ref can handle const vector* properly. + bool execute(bytes const& _rlp) { try { Transaction t(_rlp); return execute(t, t.sender()); } catch (...) { return false; } } // remove const_cast once vector_ref can handle const vector* properly. private: bool execute(Transaction const& _t, u160 _sender); diff --git a/libethereum/sha256.cpp b/libethereum/sha256.cpp index a69b5b513..e46b7c448 100644 --- a/libethereum/sha256.cpp +++ b/libethereum/sha256.cpp @@ -181,17 +181,13 @@ std::string eth::sha256(std::string const& _input, bool _hex) return std::string(buf); } -u256 eth::sha256(bytes const& _input) +bytes eth::sha256Bytes(bytesConstRef _input) { - u256 ret = 0; - + bytes ret(SHA256::DIGEST_SIZE); SHA256 ctx = SHA256(); ctx.init(); - ctx.update(_input.data(), _input.size()); - uint8_t buf[SHA256::DIGEST_SIZE]; - ctx.final(buf); - for (unsigned i = 0; i < 32; ++i) - ret = (ret << 8) | buf[i]; + ctx.update((byte*)_input.data(), _input.size()); + ctx.final(ret.data()); return ret; } diff --git a/libethereum/sha256.h b/libethereum/sha256.h index f141426ee..953b9920e 100644 --- a/libethereum/sha256.h +++ b/libethereum/sha256.h @@ -25,9 +25,14 @@ protected: uint32_t m_h[8]; }; -std::string sha256(std::string const& input, bool _hex); -u256 sha256(bytes const& input); -u256 sha256(bytesConstRef input); +std::string sha256(std::string const& _input, bool _hex); + +bytes sha256Bytes(bytesConstRef _input); +inline bytes sha256Bytes(std::string const& _input) { return sha256Bytes((std::string*)&_input); } +inline bytes sha256Bytes(bytes const& _input) { return sha256Bytes((bytes*)&_input); } + +u256 sha256(bytesConstRef _input); +inline u256 sha256(bytes const& _input) { return sha256(bytesConstRef((bytes*)&_input)); } #define SHA2_SHFR(x, n) (x >> n) #define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) diff --git a/test/main.cpp b/test/main.cpp index 006814368..537843c3e 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -7,35 +7,15 @@ using namespace std; using namespace eth; -std::string randomWord() -{ - static std::mt19937_64 s_eng(0); - std::string ret(uniform_int_distribution(4, 10)(s_eng), ' '); - char const n[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; - uniform_int_distribution d(0, sizeof(n) - 2); - for (char& c: ret) - c = n[d(s_eng)]; - return ret; -} - int main() { - secp256k1_start(); - - bytes pubkey(65); - int pubkeylen = 65; - { - cout << "SEC: " << asHex(sha256("123", false)) << endl; - int ret = secp256k1_ecdsa_pubkey_create(pubkey.data(), &pubkeylen, (byte const*)sha256("123", false).data(), 1); - pubkey.resize(pubkeylen); - cout << "PUB: " << ret << " " << pubkeylen << " " << asHex(pubkey) << endl; - } - bytes tx = fromUserHex("88005401010101010101010101010101010101010101011f0de0b6b3a76400001ce8d4a5100080181c373130a009ba1f10285d4e659568bfcfec85067855c5a3c150100815dad4ef98fd37cf0593828c89db94bd6c64e210a32ef8956eaa81ea9307194996a3b879441f5d"); cout << "TX: " << RLP(tx) << endl; Transaction t(tx); - std::string sig64 = toBigEndianString(t.vrs.r) + toBigEndianString(t.vrs.s); + cout << "SENDER: " << hex << t.sender() << endl; + + bytes sig64 = toBigEndian(t.vrs.r) + toBigEndian(t.vrs.s); cout << "SIG: " << sig64.size() << " " << asHex(sig64) << " " << t.vrs.v << endl; auto msg = t.rlp(false); @@ -44,30 +24,45 @@ int main() std::string hmsg = sha256(t.rlpString(false), false); cout << "SHA256(RLP(TX w/o SIG)): 0x" << asHex(hmsg) << endl; + bytes privkey = sha256Bytes("123"); + + secp256k1_start(); + + { + bytes pubkey(65); + int pubkeylen = 65; + + int ret = secp256k1_ecdsa_seckey_verify(privkey.data()); + cout << "SEC: " << dec << ret << " " << asHex(privkey) << endl; + + ret = secp256k1_ecdsa_pubkey_create(pubkey.data(), &pubkeylen, privkey.data(), 1); + pubkey.resize(pubkeylen); + int good = secp256k1_ecdsa_pubkey_verify(pubkey.data(), pubkey.size()); + cout << "PUB: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << (good ? " GOOD" : " BAD") << endl; + } + + // Test roundtrip... { bytes sig(64); u256 nonce = 0; int v = 0; - int ret = secp256k1_ecdsa_sign_compact((byte const*)hmsg.data(), hmsg.size(), sig.data(), (byte const*)sha256("123", false).data(), (byte const*)&nonce, &v); + int ret = secp256k1_ecdsa_sign_compact((byte const*)hmsg.data(), hmsg.size(), sig.data(), privkey.data(), (byte const*)&nonce, &v); cout << "MYSIG: " << dec << ret << " " << sig.size() << " " << asHex(sig) << " " << v << endl; - ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), hmsg.size(), (byte const*)sig.data(), pubkey.data(), &pubkeylen, 1, (int)t.vrs.v); + bytes pubkey(65); + int pubkeylen = 65; + ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), hmsg.size(), (byte const*)sig.data(), pubkey.data(), &pubkeylen, 0, v); pubkey.resize(pubkeylen); cout << "MYREC: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << endl; } { - pubkey.resize(65); - int ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), hmsg.size(), (byte const*)sig64.data(), pubkey.data(), &pubkeylen, 1, (int)t.vrs.v); - pubkey.resize(pubkeylen); - cout << "REC: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << endl; - cout << hex << sha256(pubkey) << endl; - - pubkey.resize(65); - ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), hmsg.size(), (byte const*)sig64.data(), pubkey.data(), &pubkeylen, 0, (int)t.vrs.v); + bytes pubkey(65); + int pubkeylen = 65; + int ret = secp256k1_ecdsa_recover_compact((byte const*)hmsg.data(), hmsg.size(), (byte const*)sig64.data(), pubkey.data(), &pubkeylen, 0, (int)t.vrs.v - 27); pubkey.resize(pubkeylen); - cout << "REC+: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << endl; - cout << hex << sha256(pubkey) << endl; + cout << "RECPUB: " << dec << ret << " " << pubkeylen << " " << asHex(pubkey) << endl; + cout << "SENDER: " << hex << low160(eth::sha256(bytesConstRef(&pubkey).cropped(1))) << endl; } {