From 4347cfc4fc00bc614a3d3631681aa7bc30d181d4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 20 Jan 2014 14:53:02 +0000 Subject: [PATCH] Various fixes. --- CMakeLists.txt | 1 - eth/CMakeLists.txt | 2 + eth/main.cpp | 2 +- libethereum/BlockChain.cpp | 14 +++++-- libethereum/BlockChain.h | 2 +- libethereum/BlockInfo.cpp | 2 +- libethereum/CMakeLists.txt | 2 + libethereum/Common.cpp | 2 +- libethereum/Common.h | 29 +++++++++++-- libethereum/State.cpp | 84 +++++++++++++++++++++++++------------ libethereum/State.h | 9 ++-- libethereum/Transaction.cpp | 9 ++-- libethereum/Transaction.h | 2 +- libethereum/TrieDB.h | 8 ++-- test/CMakeLists.txt | 2 + test/crypto.cpp | 17 ++++++-- test/main.cpp | 2 +- test/state.cpp | 31 +++++++------- 18 files changed, 150 insertions(+), 70 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00af76d2d..73321e0ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,4 +26,3 @@ endif () add_subdirectory(libethereum) add_subdirectory(test) add_subdirectory(eth) - diff --git a/eth/CMakeLists.txt b/eth/CMakeLists.txt index b1cbfe931..05e8a4894 100644 --- a/eth/CMakeLists.txt +++ b/eth/CMakeLists.txt @@ -18,3 +18,5 @@ target_link_libraries(eth leveldb) target_link_libraries(eth secp256k1) target_link_libraries(eth cryptopp) target_link_libraries(eth gmp) +target_link_libraries(eth boost_system) +target_link_libraries(eth boost_filesystem) diff --git a/eth/main.cpp b/eth/main.cpp index 012b43670..222859b35 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -30,7 +30,7 @@ int main() { // Our address. h256 privkey = sha3("123"); - Address us = toPublic(privkey); // TODO: should be loaded from config file/set at command-line. + Address us = toAddress(privkey); // TODO: should be loaded from config file/set at command-line. BlockChain bc; // Maintains block database. TransactionQueue tq; // Maintains list of incoming transactions not yet on the block chain. diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 3593d3f26..63abf3fc3 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -19,6 +19,7 @@ * @date 2014 */ +#include #include "Common.h" #include "RLP.h" #include "Exceptions.h" @@ -29,10 +30,17 @@ using namespace std; using namespace eth; -BlockChain::BlockChain() +BlockChain::BlockChain(std::string _path, bool _killExisting) { + if (_path.empty()) + _path = string(getenv("HOME")) + "/.ethereum"; + boost::filesystem::create_directory(_path); + if (_killExisting) + boost::filesystem::remove_all(_path + "/blocks"); + ldb::Options o; - auto s = ldb::DB::Open(o, "blockchain", &m_db); + o.create_if_missing = true; + auto s = ldb::DB::Open(o, _path + "/blocks", &m_db); // Initialise with the genesis as the last block on the longest chain. m_lastBlockHash = m_genesisHash = BlockInfo::genesis().hash; @@ -91,7 +99,7 @@ void BlockChain::import(bytes const& _block) BlockInfo biGrandParent; if (it->second.number) biGrandParent.populate(block(it->second.parent)); - u256 td = it->second.totalDifficulty + s.playback(&_block, bi, biParent, biGrandParent); + u256 td = it->second.totalDifficulty + s.playback(&_block, bi, biParent, biGrandParent, true); // All ok - insert into DB m_details[newHash] = BlockDetails{(uint)it->second.number + 1, bi.parentHash, td}; diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index ec95aa81c..7c422ec4c 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -44,7 +44,7 @@ static const h256s NullH256s; class BlockChain { public: - BlockChain(); + BlockChain(std::string _path = std::string(), bool _killExisting = false); ~BlockChain(); /// (Potentially) renders invalid existing bytesConstRef returned by lastBlock. diff --git a/libethereum/BlockInfo.cpp b/libethereum/BlockInfo.cpp index 7da8876fe..bd68b6086 100644 --- a/libethereum/BlockInfo.cpp +++ b/libethereum/BlockInfo.cpp @@ -121,6 +121,6 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const throw InvalidDifficulty(); // Check timestamp is after previous timestamp. - if (parentHash && _parent.timestamp <= _parent.timestamp) + if (parentHash && _parent.timestamp >= timestamp) throw InvalidTimestamp(); } diff --git a/libethereum/CMakeLists.txt b/libethereum/CMakeLists.txt index 843d53ca3..287ea1776 100644 --- a/libethereum/CMakeLists.txt +++ b/libethereum/CMakeLists.txt @@ -13,3 +13,5 @@ target_link_libraries(ethereum secp256k1) target_link_libraries(ethereum leveldb) target_link_libraries(ethereum cryptopp) target_link_libraries(ethereum gmp) +target_link_libraries(ethereum boost_system) +target_link_libraries(ethereum boost_filesystem) diff --git a/libethereum/Common.cpp b/libethereum/Common.cpp index 669728704..9cc3dbea1 100644 --- a/libethereum/Common.cpp +++ b/libethereum/Common.cpp @@ -144,7 +144,7 @@ h256 eth::sha3(bytesConstRef _input) return ret; } -Address eth::toPublic(PrivateKey _private) +Address eth::toAddress(Secret _private) { secp256k1_start(); diff --git a/libethereum/Common.h b/libethereum/Common.h index 2388a2042..1b9b5b6a0 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -83,6 +83,14 @@ public: bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; } bool operator<(FixedHash const& _c) const { return m_data < _c.m_data; } + FixedHash& operator^=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; } + FixedHash operator^(FixedHash const& _c) const { return FixedHash(*this) ^= _c; } + FixedHash& operator|=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] |= _c.m_data[i]; return *this; } + FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; } + FixedHash& operator&=(FixedHash const& _c) { for (auto i = 0; i < N; ++i) m_data[i] &= _c.m_data[i]; return *this; } + FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; } + FixedHash& operator~() { for (auto i = 0; i < N; ++i) m_data[i] = ~m_data[i]; return *this; } + byte& operator[](unsigned _i) { return m_data[_i]; } byte operator[](unsigned _i) const { return m_data[_i]; } @@ -98,8 +106,9 @@ private: template inline std::ostream& operator<<(std::ostream& _out, FixedHash const& _h) { + _out << std::noshowbase << std::hex << std::setfill('0'); for (unsigned i = 0; i < N; ++i) - _out << std::hex << std::setfill('0') << std::setw(2) << (int)_h[i]; + _out << std::setw(2) << (int)_h[i]; return _out; } @@ -110,7 +119,7 @@ using h160s = std::vector; using h256Set = std::set; using h160Set = std::set; -using PrivateKey = h256; +using Secret = h256; using Address = h160; using Addresses = h160s; @@ -317,6 +326,20 @@ inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)) /// Convert a private key into the public key equivalent. /// @returns 0 if it's not a valid private key. -Address toPublic(h256 _private); +Address toAddress(h256 _private); + +class KeyPair +{ +public: + KeyPair() {} + KeyPair(Secret _k): m_secret(_k), m_address(toAddress(_k)) {} + + Secret secret() const { return m_secret; } + Address address() const { return m_address; } + +private: + Secret m_secret; + Address m_address; +}; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 29c1069e6..f328618ae 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -44,30 +44,35 @@ using namespace std; using namespace eth; -u256 const State::c_stepFee = 0; -u256 const State::c_dataFee = 0; -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_stepFee = 10000; +u256 const State::c_dataFee = 20000; +u256 const State::c_memoryFee = 30000; +u256 const State::c_extroFee = 40000; +u256 const State::c_cryptoFee = 50000; +u256 const State::c_newContractFee = 60000; u256 const State::c_txFee = 0; -u256 const State::c_blockReward = 0; +u256 const State::c_blockReward = 1000000000; -State::State(Address _coinbaseAddress): m_state(&m_db), m_ourAddress(_coinbaseAddress) +State::State(Address _coinbaseAddress, std::string _path, bool _killExisting): m_state(&m_db), m_ourAddress(_coinbaseAddress) { secp256k1_start(); - m_previousBlock = BlockInfo::genesis(); - m_currentBlock.coinbaseAddress = m_ourAddress; - boost::filesystem::create_directory(string(getenv("HOME")) + "/.ethereum/"); + if (_path.empty()) + _path = string(getenv("HOME")) + "/.ethereum"; + boost::filesystem::create_directory(_path); + if (_killExisting) + boost::filesystem::remove_all(_path + "/state"); ldb::Options o; + o.create_if_missing = true; ldb::DB* db = nullptr; - ldb::DB::Open(o, string(getenv("HOME")) + "/.ethereum/state", &db); + ldb::DB::Open(o, _path + "/state", &db); m_db.setDB(db); m_state.init(); - m_state.setRoot(m_currentBlock.stateRoot); + + m_previousBlock = BlockInfo::genesis(); + resetCurrent(); } void State::ensureCached(Address _a, bool _requireMemory) const @@ -79,7 +84,9 @@ void State::ensureCached(Address _a, bool _requireMemory) const string stateBack = m_state.at(_a); RLP state(stateBack); AddressState s; - if (state.itemCount() == 2) + if (state.isNull()) + s = AddressState(0, 0); + else if (state.itemCount() == 2) s = AddressState(state[0].toInt(), state[1].toInt()); else s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash()); @@ -104,7 +111,7 @@ void State::commit() m_state.remove(i.first); else { - RLPStream s; + RLPStream s(i.second.type() == AddressType::Contract ? 3 : 2); s << i.second.balance() << i.second.nonce(); if (i.second.type() == AddressType::Contract) { @@ -114,7 +121,7 @@ void State::commit() memdb.init(); for (auto const& j: i.second.memory()) if (j.second) - memdb.insert(j.first, rlp(j.second)); // TODO: CHECK: check this isn't RLP or compact + memdb.insert(j.first, rlp(j.second)); s << memdb.root(); } else @@ -179,7 +186,7 @@ void State::sync(BlockChain const& _bc, h256 _block) // Iterate through in reverse, playing back each of the blocks. for (auto it = next(l.cbegin()); it != l.cend(); ++it) - playback(_bc.block(*it)); + playback(_bc.block(*it), true); m_currentNumber = _bc.details(_bc.currentHash()).number + 1; resetCurrent(); @@ -193,6 +200,8 @@ void State::resetCurrent() m_currentBlock = BlockInfo(); m_currentBlock.coinbaseAddress = m_ourAddress; m_currentBlock.stateRoot = m_previousBlock.stateRoot; + m_currentBlock.parentHash = m_previousBlock.hash; + m_state.setRoot(m_currentBlock.stateRoot); } void State::sync(TransactionQueue& _tq) @@ -224,13 +233,13 @@ void State::sync(TransactionQueue& _tq) } } -u256 State::playback(bytesConstRef _block) +u256 State::playback(bytesConstRef _block, bool _fullCommit) { try { m_currentBlock.populate(_block); m_currentBlock.verifyInternals(_block); - return playback(_block, BlockInfo()); + return playback(_block, BlockInfo(), _fullCommit); } catch (...) { @@ -240,14 +249,14 @@ u256 State::playback(bytesConstRef _block) } } -u256 State::playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent) +u256 State::playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit) { m_currentBlock = _bi; m_previousBlock = _parent; - return playback(_block, _grandParent); + return playback(_block, _grandParent, _fullCommit); } -u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent) +u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit) { if (m_currentBlock.parentHash != m_previousBlock.hash) throw InvalidParentHash(); @@ -279,16 +288,27 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent) // Hash the state trie and check against the state_root hash in m_currentBlock. if (m_currentBlock.stateRoot != rootHash()) { + cout << m_state; + cout << TrieDB(&m_db, m_state.root()); + cout << TrieDB(&m_db, m_currentBlock.stateRoot) << endl; // Rollback the trie. m_db.rollback(); throw InvalidStateRoot(); } - // Commit the new trie to disk. - m_db.commit(); + if (_fullCommit) + { + // Commit the new trie to disk. + m_db.commit(); - m_previousBlock = m_currentBlock; - resetCurrent(); + m_previousBlock = m_currentBlock; + resetCurrent(); + } + else + { + m_db.rollback(); + resetCurrent(); + } return tdIncrease; } @@ -298,17 +318,25 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent) void State::commitToMine(BlockChain const& _bc) { RLPStream uncles; + Addresses uncleAddresses; + if (m_previousBlock != BlockInfo::genesis()) { // Find uncles if we're not a direct child of the genesis. auto us = _bc.details(m_previousBlock.parentHash).children; uncles.appendList(us.size()); for (auto const& u: us) - BlockInfo(_bc.block(u)).fillStream(uncles, true); + { + BlockInfo ubi(_bc.block(u)); + ubi.fillStream(uncles, true); + uncleAddresses.push_back(ubi.coinbaseAddress); + } } else uncles.appendList(0); + applyRewards(uncleAddresses); + RLPStream txs(m_transactions.size()); for (auto const& i: m_transactions) i.second.fillStream(txs); @@ -321,7 +349,9 @@ void State::commitToMine(BlockChain const& _bc) // Commit any and all changes to the trie that are in the cache, then update the state root accordingly. commit(); + cout << m_state; m_currentBlock.stateRoot = m_state.root(); + m_currentBlock.parentHash = m_previousBlock.hash; } bool State::mine(uint _msTimeout) diff --git a/libethereum/State.h b/libethereum/State.h index a937a66b3..e902378b1 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -49,7 +49,7 @@ class State { public: /// Construct state object. - explicit State(Address _coinbaseAddress); + explicit State(Address _coinbaseAddress, std::string _path = std::string(), bool _killExisting = false); /// Cancels transactions and rolls back the state to the end of the previous block. /// @warning This will only work for on any transactions after you called the last commitToMine(). @@ -60,6 +60,7 @@ public: /// Commits all transactions into the trie, compiles uncles and transactions list, applies all /// rewards and populates the current block header with the appropriate hashes. /// The only thing left to do after this is to actually mine(). + /// @warning Only call this once! void commitToMine(BlockChain const& _bc); /// Attempt to find valid nonce for block that this state represents. @@ -127,7 +128,7 @@ public: /// @returns the additional total difficulty. /// If the _grandParent is passed, it will check the validity of each of the uncles. /// This might throw. - u256 playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent); + u256 playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit); private: /// Fee-adder on destruction RAII class. @@ -146,11 +147,11 @@ private: /// Execute the given block on our previous block. This will set up m_currentBlock first, then call the other playback(). /// Any failure will be critical. - u256 playback(bytesConstRef _block); + u256 playback(bytesConstRef _block, bool _fullCommit); /// Execute the given block, assuming it corresponds to m_currentBlock. If _grandParent is passed, it will be used to check the uncles. /// Throws on failure. - u256 playback(bytesConstRef _block, BlockInfo const& _grandParent); + u256 playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit); /// Execute a decoded transaction object, given a sender. /// This will append @a _t to the transaction list and change the state accordingly. diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 46fbee900..7d0a522a8 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -54,13 +54,16 @@ Address Transaction::sender() const return right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); } -void Transaction::sign(PrivateKey _priv) +void Transaction::sign(Secret _priv) { int v = 0; + secp256k1_start(); + h256 msg = sha3(false); byte sig[64]; - if (!secp256k1_ecdsa_sign_compact(msg.data(), 32, sig, _priv.data(), kFromMessage(msg, _priv).data(), &v)) + h256 nonce = kFromMessage(msg, _priv); + if (!secp256k1_ecdsa_sign_compact(msg.data(), 32, sig, _priv.data(), nonce.data(), &v)) throw InvalidSignature(); vrs.v = (byte)(v + 27); @@ -91,6 +94,6 @@ h256 Transaction::kFromMessage(h256 _msg, h256 _priv) v = hmac.new(k, v, hashlib.sha256).digest() return decode(hmac.new(k, v, hashlib.sha256).digest(),256) */ - return h256(); + return _msg ^ _priv; } diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 44dd022c0..c68b229f3 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -49,7 +49,7 @@ struct Transaction Signature vrs; Address sender() const; - void sign(PrivateKey _priv); + void sign(Secret _priv); static h256 kFromMessage(h256 _msg, h256 _priv); diff --git a/libethereum/TrieDB.h b/libethereum/TrieDB.h index cf333d17c..39ddf622e 100644 --- a/libethereum/TrieDB.h +++ b/libethereum/TrieDB.h @@ -95,7 +95,7 @@ private: /** * @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree. - * This version uses an LDB backend - TODO: split off m_db & m_over into opaque key/value map layer and allow caching & testing without DB. + * This version uses an LDB backend */ template class GenericTrieDB @@ -365,8 +365,8 @@ public: { auto p = Super::at(); value_type ret; - assert(p.first.size() == sizeof(ret)); - memcpy(&ret.first, p.first.data(), sizeof(ret)); + assert(p.first.size() == sizeof(KeyType)); + memcpy(&ret.first, p.first.data(), sizeof(KeyType)); ret.second = p.second; return ret; } @@ -415,7 +415,7 @@ template std::string GenericTrieDB::at(bytesConstRef _key) const template std::string GenericTrieDB::atAux(RLP const& _here, NibbleSlice _key) const { - if (_here.isEmpty()) + if (_here.isEmpty() || _here.isNull()) // not found. return std::string(); assert(_here.isList() && (_here.itemCount() == 2 || _here.itemCount() == 17)); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e9e464117..7e2d1ca84 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,3 +17,5 @@ target_link_libraries(testeth ethereum) target_link_libraries(testeth cryptopp) target_link_libraries(testeth secp256k1) target_link_libraries(testeth gmp) +target_link_libraries(testeth boost_system) +target_link_libraries(testeth boost_filesystem) diff --git a/test/crypto.cpp b/test/crypto.cpp index 0c9f9e351..75ac9e919 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -34,8 +34,16 @@ int cryptoTest() bytes tx = fromUserHex("88005401010101010101010101010101010101010101011f0de0b6b3a76400001ce8d4a5100080181c373130a009ba1f10285d4e659568bfcfec85067855c5a3c150100815dad4ef98fd37cf0593828c89db94bd6c64e210a32ef8956eaa81ea9307194996a3b879441f5d"); cout << "TX: " << RLP(tx) << endl; - Transaction t(tx); - cout << "SENDER: " << hex << t.sender() << endl; + Transaction t2(tx); + cout << "SENDER: " << hex << t2.sender() << endl; + + secp256k1_start(); + + Transaction t; + t.nonce = 0; + t.fee = 0; + t.value = 1; // 1 wei. + t.receiveAddress = toAddress(sha3("123")); bytes sig64 = toBigEndian(t.vrs.r) + toBigEndian(t.vrs.s); cout << "SIG: " << sig64.size() << " " << asHex(sig64) << " " << t.vrs.v << endl; @@ -48,8 +56,6 @@ int cryptoTest() bytes privkey = sha3Bytes("123"); - secp256k1_start(); - { bytes pubkey(65); int pubkeylen = 65; @@ -68,6 +74,9 @@ int cryptoTest() bytes sig(64); u256 nonce = 0; int v = 0; + cout << asHex(hmsg) << endl; + cout << asHex(privkey) << endl; + cout << hex << nonce << endl; 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; diff --git a/test/main.cpp b/test/main.cpp index 994b611f0..ff2be2a59 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -35,7 +35,7 @@ int main() rlpTest(); trieTest(); // daggerTest(); -// cryptoTest(); + cryptoTest(); stateTest(); return 0; } diff --git a/test/state.cpp b/test/state.cpp index c4c148b4c..517e1d5d1 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -20,43 +20,44 @@ * State test functions. */ +#include +#include #include using namespace std; using namespace eth; -struct KeyPair -{ - KeyPair() {} - KeyPair(PrivateKey _k): priv(_k), addr(toPublic(_k)) {} - PrivateKey priv; - Address addr; -}; - int stateTest() { KeyPair me = sha3("Gav Wood"); KeyPair myMiner = sha3("Gav's Miner"); // KeyPair you = sha3("123"); - State s(myMiner.addr); + BlockChain bc("/tmp"); + State s(myMiner.address(), "/tmp"); // Mine to get some ether! - s.mine(); + s.commitToMine(bc); + while (!s.mine(100)) {} + bc.attemptImport(s.blockData()); + s.sync(bc); bytes tx; { Transaction t; - t.nonce = s.transactionsFrom(myMiner.addr); + t.nonce = s.transactionsFrom(myMiner.address()); t.fee = 0; - t.value = 1; // 1 wei. - t.receiveAddress = me.addr; - t.sign(myMiner.priv); + t.value = 1000000000; // 1e9 wei. + t.receiveAddress = me.address(); + t.sign(myMiner.secret()); tx = t.rlp(); } cout << RLP(tx) << endl; s.execute(tx); - // TODO: Mine to set in stone. + s.commitToMine(bc); + while (!s.mine(100)) {} + bc.attemptImport(s.blockData()); + s.sync(bc); return 0; }