Browse Source

More crypto additions.

cl-refactor
Gav Wood 11 years ago
parent
commit
00da157812
  1. 9
      libethereum/Common.h
  2. 198
      libethereum/State.cpp
  3. 41
      libethereum/State.h
  4. 2
      test/main.cpp

9
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; o_out[s - 1 - i] = (typename _Out::value_type)(uint8_t)_val;
} }
template <class _T, class _In>
inline _T fromBigEndian(_In const& _bytes)
{
_T ret = 0;
for (auto i: _bytes)
ret = (ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i;
return ret;
}
inline std::string toBigEndianString(u256 _val) { std::string ret(32, '\0'); toBigEndian(_val, ret); 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; } inline std::string toBigEndianString(u160 _val) { std::string ret(20, '\0'); toBigEndian(_val, ret); return ret; }

198
libethereum/VirtualMachine.cpp → libethereum/State.cpp

@ -1,7 +1,7 @@
#include <secp256k1.h> #include <secp256k1.h>
#include <random> #include <random>
#include "sha256.h" #include "sha256.h"
#include "VirtualMachine.h" #include "State.h"
using namespace std; using namespace std;
using namespace eth; using namespace eth;
@ -11,22 +11,56 @@ u256 const State::c_memoryFee = 0;
u256 const State::c_extroFee = 0; u256 const State::c_extroFee = 0;
u256 const State::c_cryptoFee = 0; u256 const State::c_cryptoFee = 0;
u256 const State::c_newContractFee = 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(); State::ensureCrypto();
bytes sig = toBigEndian(vrs.r) + toBigEndian(vrs.s); bytes sig = toBigEndian(vrs.r) + toBigEndian(vrs.s);
assert(sig.size() == 64); assert(sig.size() == 64);
RLPStream rlp; bytes msg = sha256Bytes(false);
fillStream(rlp, false);
bytes msg = eth::sha256Bytes(rlp.out());
bytes pubkey(65); byte pubkey[65];
int pubkeylen = 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(); 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<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));
} }
Transaction::Transaction(bytes const& _rlpData) Transaction::Transaction(bytes const& _rlpData)
@ -39,7 +73,7 @@ Transaction::Transaction(bytes const& _rlpData)
data.reserve(rlp[4].itemCountStrict()); data.reserve(rlp[4].itemCountStrict());
for (auto const& i: rlp[4]) for (auto const& i: rlp[4])
data.push_back(i.toFatIntStrict()); 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 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); _s << toCompactBigEndianString(vrs.v) << toCompactBigEndianString(vrs.r) << toCompactBigEndianString(vrs.s);
} }
State::State(Address _minerAddress): m_minerAddress(_minerAddress)
{
ensureCrypto();
}
void State::ensureCrypto() void State::ensureCrypto()
{ {
secp256k1_start(); 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. // Entry point for a contract-originated transaction.
@ -107,7 +153,7 @@ bool State::execute(Transaction const& _t, u160 _sender)
return false; return false;
} }
u160 newAddress = low160(_t.sha256()); Address newAddress = low160(_t.sha256());
if (isContractAddress(newAddress)) 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<u256> stack; std::vector<u256> stack;
auto m = m_current.find(_myAddress); auto m = m_current.find(_myAddress);
@ -152,6 +198,12 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee,
myMemory.erase(_n); myMemory.erase(_n);
}; };
if (balance(_myAddress) < _txFee)
throw NotEnoughCash();
subBalance(_myAddress, _txFee);
*_totalFee += _txFee;
u256 curPC = 0; u256 curPC = 0;
u256 nextPC = 1; u256 nextPC = 1;
u256 stepCount = 0; u256 stepCount = 0;
@ -185,6 +237,10 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee,
minerFee += c_extroFee; minerFee += c_extroFee;
break; break;
case Instruction::MKTX:
minerFee += c_txFee;
break;
case Instruction::SHA256: case Instruction::SHA256:
case Instruction::RIPEMD160: case Instruction::RIPEMD160:
case Instruction::ECMUL: case Instruction::ECMUL:
@ -199,7 +255,7 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee,
} }
if (minerFee + voidFee > balance(_myAddress)) if (minerFee + voidFee > balance(_myAddress))
return; throw NotEnoughCash();
subBalance(_myAddress, minerFee + voidFee); subBalance(_myAddress, minerFee + voidFee);
*_totalFee += (u256)minerFee; *_totalFee += (u256)minerFee;
@ -346,13 +402,122 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee,
break; break;
} }
case Instruction::ECMUL: 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<u256>(bytesConstRef(&pub).cropped(1, 32)));
stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(33, 32)));
}
else
{
stack.push_back(0);
stack.push_back(0);
}
break;
}
case Instruction::ECADD: 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<u256>(bytesConstRef(&pub).cropped(1, 32)));
stack.push_back(fromBigEndian<u256>(bytesConstRef(&pub).cropped(33, 32)));
}
else
{
stack.push_back(0);
stack.push_back(0);
}
break;
}
case Instruction::ECSIGN: 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<u256>(bytesConstRef(&sig).cropped(0, 32)));
stack.push_back(fromBigEndian<u256>(bytesConstRef(&sig).cropped(32)));
break;
}
case Instruction::ECRECOVER: 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<u256>(bytesConstRef(&pubkey[1], 32)));
stack.push_back(fromBigEndian<u256>(bytesConstRef(&pubkey[33], 32)));
}
break;
}
case Instruction::ECVALID: 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; break;
}
case Instruction::PUSH: case Instruction::PUSH:
{ {
stack.push_back(mem(curPC + 1)); stack.push_back(mem(curPC + 1));
@ -426,7 +591,7 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee,
require(2); require(2);
auto memoryAddress = stack.back(); auto memoryAddress = stack.back();
stack.pop_back(); stack.pop_back();
u160 contractAddress = as160(stack.back()); Address contractAddress = as160(stack.back());
stack.back() = contractMemory(contractAddress, memoryAddress); stack.back() = contractMemory(contractAddress, memoryAddress);
break; break;
} }
@ -467,10 +632,9 @@ void State::execute(u160 _myAddress, u160 _txSender, u256 _txValue, u256 _txFee,
case Instruction::SUICIDE: case Instruction::SUICIDE:
{ {
require(1); require(1);
u160 dest = as160(stack.back()); Address dest = as160(stack.back());
u256 minusVoidFee = m_current[_myAddress].memory().size() * c_memoryFee; u256 minusVoidFee = m_current[_myAddress].memory().size() * c_memoryFee;
addBalance(dest, balance(_myAddress) + minusVoidFee); addBalance(dest, balance(_myAddress) + minusVoidFee);
subBalance(dest, _txFee);
m_current.erase(_myAddress); m_current.erase(_myAddress);
// ...follow through to... // ...follow through to...
} }

41
libethereum/VirtualMachine.h → 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 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 BadInstruction: public std::exception {};
class StackTooSmall: public std::exception { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; }; class StackTooSmall: public std::exception { public: StackTooSmall(u256 _req, u256 _got): req(_req), got(_got) {} u256 req; u256 got; };
class OperandOutOfRange: public std::exception { public: OperandOutOfRange(u256 _min, u256 _max, u256 _got): mn(_min), mx(_max), got(_got) {} u256 mn; u256 mx; u256 got; }; class 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 struct Signature
{ {
u256 v; byte v;
u256 r; u256 r;
u256 s; 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 ] // [ nonce, receiving_address, value, fee, [ data item 0, data item 1 ... data item n ], v, r, s ]
struct Transaction struct Transaction
@ -197,42 +200,45 @@ struct Transaction
Transaction(bytes const& _rlp); Transaction(bytes const& _rlp);
u256 nonce; u256 nonce;
u160 receiveAddress; Address receiveAddress;
u256 value; u256 value;
u256 fee; u256 fee;
u256s data; u256s data;
Signature vrs; Signature vrs;
u160 sender() const; Address sender() const;
void sign(PrivateKey _priv);
void fillStream(RLPStream& _s, bool _sig = true) const; void fillStream(RLPStream& _s, bool _sig = true) const;
bytes rlp(bool _sig = true) const { RLPStream s; fillStream(s, _sig); return s.out(); } 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(); } 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()); } 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 class State
{ {
public: public:
explicit State(u256 _minerAddress): m_minerAddress(_minerAddress) {} explicit State(Address _minerAddress);
static void ensureCrypto(); static void ensureCrypto();
static std::mt19937_64& engine();
bool verify(bytes const& _block); 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. 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: 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 isNormalAddress(Address _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 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(); } u256 balance(Address _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; } 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. // 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); auto m = m_current.find(_contract);
if (m == m_current.end()) if (m == m_current.end())
@ -241,17 +247,15 @@ private:
return i == m->second.memory().end() ? 0 : i->second; 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(); } u256 transactionsFrom(Address _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<u160, AddressState> m_current;
void execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* o_totalFee);
std::map<Address, AddressState> m_current;
BlockInfo m_previousBlock; BlockInfo m_previousBlock;
BlockInfo m_currentBlock; BlockInfo m_currentBlock;
u160 m_minerAddress; Address m_minerAddress;
static const u256 c_stepFee; static const u256 c_stepFee;
static const u256 c_dataFee; static const u256 c_dataFee;
@ -259,6 +263,9 @@ private:
static const u256 c_extroFee; static const u256 c_extroFee;
static const u256 c_cryptoFee; static const u256 c_cryptoFee;
static const u256 c_newContractFee; static const u256 c_newContractFee;
static const u256 c_txFee;
static std::mt19937_64* s_engine;
}; };
} }

2
test/main.cpp

@ -3,7 +3,7 @@
#include <secp256k1.h> #include <secp256k1.h>
#include "RLP.h" #include "RLP.h"
#include "Trie.h" #include "Trie.h"
#include "VirtualMachine.h" #include "State.h"
using namespace std; using namespace std;
using namespace eth; using namespace eth;

Loading…
Cancel
Save