diff --git a/libethereum/Common.h b/libethereum/Common.h index 2ae3746fb..e85320ee9 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -96,6 +96,15 @@ inline void toBigEndian(_T _val, _Out& o_out) o_out[s - 1 - i] = (typename _Out::value_type)(uint8_t)_val; } +template +inline _T fromBigEndian(_In const& _bytes) +{ + _T ret = 0; + for (auto i: _bytes) + ret = (ret << 8) | (byte)(typename std::make_unsigned::type)i; + return ret; +} + 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; } diff --git a/libethereum/VirtualMachine.cpp b/libethereum/State.cpp similarity index 67% rename from libethereum/VirtualMachine.cpp rename to libethereum/State.cpp index 5dffa604d..85b953b28 100644 --- a/libethereum/VirtualMachine.cpp +++ b/libethereum/State.cpp @@ -1,7 +1,7 @@ #include #include #include "sha256.h" -#include "VirtualMachine.h" +#include "State.h" using namespace std; using namespace eth; @@ -11,22 +11,56 @@ u256 const State::c_memoryFee = 0; u256 const State::c_extroFee = 0; u256 const State::c_cryptoFee = 0; u256 const State::c_newContractFee = 0; +u256 const State::c_txFee = 0; -u160 Transaction::sender() const +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::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 msg = sha256Bytes(false); - bytes pubkey(65); + byte pubkey[65]; int pubkeylen = 65; - if (!secp256k1_ecdsa_recover_compact(msg.data(), msg.size(), sig.data(), pubkey.data(), &pubkeylen, 0, (int)vrs.v - 27)) + 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).cropped(1))); + return low160(eth::sha256(bytesConstRef(&pubkey[1], 64))); +} + +void Transaction::sign(PrivateKey _priv) +{ + 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)); } Transaction::Transaction(bytes const& _rlpData) @@ -39,7 +73,7 @@ Transaction::Transaction(bytes const& _rlpData) data.reserve(rlp[4].itemCountStrict()); for (auto const& i: rlp[4]) data.push_back(i.toFatIntStrict()); - vrs = Signature{ rlp[5].toFatIntStrict(), rlp[6].toFatIntStrict(), rlp[7].toFatIntStrict() }; + vrs = Signature{ (byte)rlp[5].toSlimIntStrict(), rlp[6].toFatIntStrict(), rlp[7].toFatIntStrict() }; } void Transaction::fillStream(RLPStream& _s, bool _sig) const @@ -49,12 +83,24 @@ void Transaction::fillStream(RLPStream& _s, bool _sig) const _s << toCompactBigEndianString(vrs.v) << toCompactBigEndianString(vrs.r) << toCompactBigEndianString(vrs.s); } +State::State(Address _minerAddress): m_minerAddress(_minerAddress) +{ + ensureCrypto(); +} + void State::ensureCrypto() { secp256k1_start(); } -bool State::execute(Transaction const& _t, u160 _sender) +mt19937_64& State::engine() +{ + if (!s_engine) + s_engine = new mt19937_64(random_device()()); + return *s_engine; +} + +bool State::execute(Transaction const& _t, Address _sender) { // Entry point for a contract-originated transaction. @@ -107,7 +153,7 @@ bool State::execute(Transaction const& _t, u160 _sender) return false; } - u160 newAddress = low160(_t.sha256()); + Address newAddress = low160(_t.sha256()); if (isContractAddress(newAddress)) { @@ -126,7 +172,7 @@ bool State::execute(Transaction const& _t, u160 _sender) } } -void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee) +void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee) { std::vector stack; auto m = m_current.find(_myAddress); @@ -152,6 +198,12 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, myMemory.erase(_n); }; + if (balance(_myAddress) < _txFee) + throw NotEnoughCash(); + + subBalance(_myAddress, _txFee); + *_totalFee += _txFee; + u256 curPC = 0; u256 nextPC = 1; u256 stepCount = 0; @@ -185,6 +237,10 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, minerFee += c_extroFee; break; + case Instruction::MKTX: + minerFee += c_txFee; + break; + case Instruction::SHA256: case Instruction::RIPEMD160: case Instruction::ECMUL: @@ -199,7 +255,7 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, } if (minerFee + voidFee > balance(_myAddress)) - return; + throw NotEnoughCash(); subBalance(_myAddress, minerFee + voidFee); *_totalFee += (u256)minerFee; @@ -346,13 +402,122 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, break; } case Instruction::ECMUL: + { + // ECMUL - pops three items. + // If (S[-2],S[-1]) are a valid point in secp256k1, including both coordinates being less than P, pushes (S[-1],S[-2]) * S[-3], using (0,0) as the point at infinity. + // Otherwise, pushes (0,0). + require(3); + + bytes pub(1, 4); + pub += toBigEndian(stack[stack.size() - 2]); + pub += toBigEndian(stack.back()); + stack.pop_back(); + stack.pop_back(); + bytes x = toBigEndian(stack.back()); + stack.pop_back(); + + if (secp256k1_ecdsa_pubkey_verify(pub.data(), pub.size())) // TODO: Check both are less than P. + { + secp256k1_ecdsa_pubkey_tweak_mul(pub.data(), pub.size(), x.data()); + stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); + stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); + } + else + { + stack.push_back(0); + stack.push_back(0); + } + break; + } case Instruction::ECADD: + { + // ECADD - pops four items and pushes (S[-4],S[-3]) + (S[-2],S[-1]) if both points are valid, otherwise (0,0). + require(4); + + bytes pub(1, 4); + pub += toBigEndian(stack[stack.size() - 2]); + pub += toBigEndian(stack.back()); + stack.pop_back(); + stack.pop_back(); + + bytes tweak(1, 4); + tweak += toBigEndian(stack[stack.size() - 2]); + tweak += toBigEndian(stack.back()); + stack.pop_back(); + stack.pop_back(); + + if (secp256k1_ecdsa_pubkey_verify(pub.data(), pub.size()) && secp256k1_ecdsa_pubkey_verify(tweak.data(), tweak.size())) + { + secp256k1_ecdsa_pubkey_tweak_add(pub.data(), pub.size(), tweak.data()); + stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); + stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); + } + else + { + stack.push_back(0); + stack.push_back(0); + } + break; + } case Instruction::ECSIGN: + { + require(2); + bytes sig(64); + int v = 0; + + u256 msg = stack.back(); + stack.pop_back(); + u256 priv = stack.back(); + stack.pop_back(); + bytes nonce = toBigEndian(kFromMessage(msg, priv)); + + if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v)) + throw InvalidSignature(); + + stack.push_back(v + 27); + stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(0, 32))); + stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(32))); + break; + } case Instruction::ECRECOVER: + { + require(4); + + bytes sig = toBigEndian(stack[stack.size() - 2]) + toBigEndian(stack.back()); + stack.pop_back(); + stack.pop_back(); + int v = (int)stack.back(); + stack.pop_back(); + bytes msg = toBigEndian(stack.back()); + stack.pop_back(); + + byte pubkey[65]; + int pubkeylen = 65; + if (secp256k1_ecdsa_recover_compact(msg.data(), msg.size(), sig.data(), pubkey, &pubkeylen, 0, v - 27)) + { + stack.push_back(0); + stack.push_back(0); + } + else + { + stack.push_back(fromBigEndian(bytesConstRef(&pubkey[1], 32))); + stack.push_back(fromBigEndian(bytesConstRef(&pubkey[33], 32))); + } + break; + } case Instruction::ECVALID: - // TODO + { + require(2); + bytes pub(1, 4); + pub += toBigEndian(stack[stack.size() - 2]); + pub += toBigEndian(stack.back()); + stack.pop_back(); + stack.pop_back(); + + stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), pub.size()) ? 1 : 0; break; + } case Instruction::PUSH: { stack.push_back(mem(curPC + 1)); @@ -426,7 +591,7 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, require(2); auto memoryAddress = stack.back(); stack.pop_back(); - u160 contractAddress = as160(stack.back()); + Address contractAddress = as160(stack.back()); stack.back() = contractMemory(contractAddress, memoryAddress); break; } @@ -467,10 +632,9 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, case Instruction::SUICIDE: { require(1); - u160 dest = as160(stack.back()); + Address dest = as160(stack.back()); u256 minusVoidFee = m_current[_myAddress].memory().size() * c_memoryFee; addBalance(dest, balance(_myAddress) + minusVoidFee); - subBalance(dest, _txFee); m_current.erase(_myAddress); // ...follow through to... } diff --git a/libethereum/VirtualMachine.h b/libethereum/State.h similarity index 82% rename from libethereum/VirtualMachine.h rename to libethereum/State.h index fa3b04a25..a47cfe85a 100644 --- a/libethereum/VirtualMachine.h +++ b/libethereum/State.h @@ -65,6 +65,7 @@ enum class Instruction: uint8_t SUICIDE = 0xff ///< Rx - destroys the contract and clears all memory, sending the entire balance plus the negative fee from clearing memory minus TXFEE to the address }; +class NotEnoughCash: public std::exception {}; class BadInstruction: public std::exception {}; class StackTooSmall: public std::exception { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; class OperandOutOfRange: public std::exception { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; @@ -184,11 +185,13 @@ inline u160 as160(_T const& _t) struct Signature { - u256 v; + 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 @@ -197,42 +200,45 @@ struct Transaction Transaction(bytes const& _rlp); u256 nonce; - u160 receiveAddress; + Address receiveAddress; u256 value; u256 fee; u256s data; Signature vrs; - u160 sender() const; + 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(u256 _minerAddress): m_minerAddress(_minerAddress) {} + 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); 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); + bool execute(Transaction const& _t, Address _sender); - bool isNormalAddress(u160 _address) const { auto it = m_current.find(_address); return it != m_current.end() && it->second.type() == AddressType::Normal; } - bool isContractAddress(u160 _address) const { auto it = m_current.find(_address); return it != m_current.end() && it->second.type() == AddressType::Contract; } + 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(u160 _id) const { auto it = m_current.find(_id); return it == m_current.end() ? 0 : it->second.balance(); } - void addBalance(u160 _id, u256 _amount) { auto it = m_current.find(_id); if (it == m_current.end()) it->second.balance() = _amount; else it->second.balance() += _amount; } + 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(u160 _id, bigint _amount) { auto it = m_current.find(_id); if (it == m_current.end() || (bigint)it->second.balance() < _amount) return false; it->second.balance() = (u256)((bigint)it->second.balance() - _amount); return true; } + 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(u160 _contract, u256 _memory) const + u256 contractMemory(Address _contract, u256 _memory) const { auto m = m_current.find(_contract); if (m == m_current.end()) @@ -241,17 +247,15 @@ private: return i == m->second.memory().end() ? 0 : i->second; } - u256 transactionsFrom(u160 _address) { auto it = m_current.find(_address); return it == m_current.end() ? 0 : it->second.nonce(); } - - void execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* o_totalFee); - - std::map m_current; + 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; BlockInfo m_previousBlock; BlockInfo m_currentBlock; - u160 m_minerAddress; + Address m_minerAddress; static const u256 c_stepFee; static const u256 c_dataFee; @@ -259,6 +263,9 @@ private: static const u256 c_extroFee; static const u256 c_cryptoFee; static const u256 c_newContractFee; + static const u256 c_txFee; + + static std::mt19937_64* s_engine; }; } diff --git a/test/main.cpp b/test/main.cpp index 537843c3e..9ed0bfc4b 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,7 +3,7 @@ #include #include "RLP.h" #include "Trie.h" -#include "VirtualMachine.h" +#include "State.h" using namespace std; using namespace eth;