From 9cf6fd38cabe02646ea4059944e53b0906fd378a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Jan 2014 12:54:06 +0000 Subject: [PATCH] Rewards and uncle checking. --- libethereum/BlockChain.cpp | 41 +++++++------------ libethereum/BlockChain.h | 6 ++- libethereum/BlockInfo.h | 2 +- libethereum/Common.h | 4 ++ libethereum/State.cpp | 80 +++++++++++++++++++++++++++++--------- libethereum/State.h | 28 +++++++++---- libethereum/Transaction.h | 3 -- 7 files changed, 107 insertions(+), 57 deletions(-) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 794ecdec6..34ba1d0bb 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -37,6 +37,8 @@ BlockChain::BlockChain() // Initialise with the genesis as the last block on the longest chain. m_lastBlockHash = m_genesisHash = BlockInfo::genesis().hash; m_genesisBlock = BlockInfo::createGenesisBlock(); + + // TODO: Insert details of genesis block. } BlockChain::~BlockChain() @@ -45,8 +47,8 @@ BlockChain::~BlockChain() h256s BlockChain::blockChain(h256Set const& _earlyExit) const { - // TODO: return the current valid block chain from most recent to genesis. - // TODO: arguments for specifying a set of early-ends + // Return the current valid block chain from most recent to genesis. + // Arguments for specifying a set of early-ends h256s ret; ret.reserve(m_details[m_lastBlockHash].number + 1); auto i = m_lastBlockHash; @@ -55,7 +57,8 @@ h256s BlockChain::blockChain(h256Set const& _earlyExit) const ret.push_back(i); return ret; } - +// _bc.details(m_previousBlock.parentHash).children +// _bc->coinbaseAddress(i) void BlockChain::import(bytes const& _block) { try @@ -77,38 +80,22 @@ void BlockChain::import(bytes const& _block) return; // Check family: - BlockInfo bip(block(bi.parentHash)); - bi.verifyParent(bip); + BlockInfo biParent(block(bi.parentHash)); + bi.verifyParent(biParent); // Check transactions are valid and that they result in a state equivalent to our state_root. - // this saves us from an embarrassing exit later. State s(bi.coinbaseAddress); s.sync(*this, bi.parentHash); - for (auto const& i: RLP(_block)[1]) - s.execute(i.data()); - if (s.rootHash() != bi.stateRoot) - throw InvalidStateRoot(); - - // Initalise total difficulty calculation. - u256 td = it->second.totalDifficulty + bi.difficulty; - - // Check uncles. - for (auto const& i: RLP(_block)[2]) - { - BlockInfo uncle(i.data()); - if (it->second.parent != bi.parentHash) - throw InvalidUncle(); - - uncle.verifyParent(bip); - - td += uncle.difficulty; - } + // Get total difficulty increase and update state, checking it. + BlockInfo biGrandParent; + if (it->second.number) + biGrandParent.populate(block(it->second.parent)); + u256 td = it->second.totalDifficulty + s.playback(&_block, bi, biParent, biGrandParent); // All ok - insert into DB m_details[newHash] = BlockDetails{(uint)it->second.number + 1, bi.parentHash, td}; - - m_children.insert(make_pair(bi.parentHash, newHash)); + m_details[bi.parentHash].children.push_back(newHash); m_db->Put(m_writeOptions, ldb::Slice(toBigEndianString(newHash)), (ldb::Slice)ref(_block)); // This might be the new last block... diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 3f72d68ac..59b13294f 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -33,9 +33,11 @@ struct BlockDetails uint number; u256 totalDifficulty; h256 parent; + h256s children; }; static const BlockDetails NullBlockDetails({0, 0, h256()}); +static const h256s NullH256s; /** * @brief Implements the blockchain database. All data this gives is disk-backed. @@ -68,10 +70,12 @@ public: /// Get a given block (RLP format). h256 currentHash() const { return m_lastBlockHash; } + /// Get the coinbase address of a given block. + Address coinbaseAddress(h256 _hash) const; + private: /// Get fully populated from disk DB. mutable std::map m_details; - mutable std::multimap m_children; mutable std::map m_cache; diff --git a/libethereum/BlockInfo.h b/libethereum/BlockInfo.h index bd6269335..2ec47e5e7 100644 --- a/libethereum/BlockInfo.h +++ b/libethereum/BlockInfo.h @@ -43,7 +43,7 @@ public: BlockInfo(); explicit BlockInfo(bytesConstRef _block); - explicit operator bool() { return timestamp != Invalid256; } + explicit operator bool() const { return timestamp != Invalid256; } bool operator==(BlockInfo const& _cmp) const { return hash == _cmp.hash && parentHash == _cmp.parentHash && nonce == _cmp.nonce; } diff --git a/libethereum/Common.h b/libethereum/Common.h index 7e10bcdcc..ec69288ee 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -95,6 +95,10 @@ using h160s = std::vector; using h256Set = std::set; using h160Set = std::set; +using PrivateKey = h256; +using Address = h160; +using Addresses = h160s; + // Map types. using StringMap = std::map; using u256Map = std::map; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 7188b60ee..ff0e4cbe9 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -39,8 +39,9 @@ u256 const State::c_extroFee = 0; u256 const State::c_cryptoFee = 0; u256 const State::c_newContractFee = 0; u256 const State::c_txFee = 0; +u256 const State::c_blockReward = 0; -State::State(Address _coinbaseAddress): m_coinbaseAddress(_coinbaseAddress) +State::State(Address _coinbaseAddress): m_ourAddress(_coinbaseAddress) { secp256k1_start(); m_previousBlock = BlockInfo::genesis(); @@ -137,25 +138,13 @@ void State::sync(TransactionQueue& _tq) } } -void State::playback(bytesConstRef _block) +u256 State::playback(bytesConstRef _block) { try { m_currentBlock.populate(_block); m_currentBlock.verifyInternals(_block); - if (m_currentBlock.parentHash != m_previousBlock.hash) - throw InvalidParentHash(); - - // All ok with the block generally. Play back the transactions now... - RLP txs = RLP(_block)[1]; - for (auto const& i: txs) - execute(i.data()); - - // Hash the state trie and check against the state_root hash in m_currentBlock. - if (m_currentBlock.stateRoot != rootHash()) - throw InvalidStateRoot(); - - m_previousBlock = m_currentBlock; + return playback(_block, BlockInfo()); } catch (...) { @@ -165,9 +154,53 @@ void State::playback(bytesConstRef _block) } } +u256 State::playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent) +{ + m_currentBlock = _bi; + m_previousBlock = _parent; + return playback(_block, _grandParent); +} + +u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent) +{ + if (m_currentBlock.parentHash != m_previousBlock.hash) + throw InvalidParentHash(); + + // All ok with the block generally. Play back the transactions now... + for (auto const& i: RLP(_block)[1]) + execute(i.data()); + + // Initialise total difficulty calculation. + u256 tdIncrease = m_currentBlock.difficulty; + + // Check uncles & apply their rewards to state. + Addresses rewarded; + for (auto const& i: RLP(_block)[2]) + { + BlockInfo uncle(i.data()); + if (m_previousBlock.parentHash != uncle.parentHash) + throw InvalidUncle(); + if (_grandParent) + uncle.verifyParent(_grandParent); + tdIncrease += uncle.difficulty; + rewarded.push_back(uncle.coinbaseAddress); + } + applyRewards(rewarded); + + // Hash the state trie and check against the state_root hash in m_currentBlock. + if (m_currentBlock.stateRoot != rootHash()) + throw InvalidStateRoot(); + + m_previousBlock = m_currentBlock; + m_currentBlock = BlockInfo(); + m_currentBlock.coinbaseAddress = m_ourAddress; + + return tdIncrease; +} + h256 State::rootHash() const { - // TODO. + // TODO! return h256(); } @@ -252,6 +285,17 @@ bool State::execute(bytesConstRef _rlp) } } +void State::applyRewards(Addresses const& _uncleAddresses) +{ + u256 r = c_blockReward; + for (auto const& i: _uncleAddresses) + { + addBalance(i, c_blockReward * 4 / 3); + r += c_blockReward / 8; + } + addBalance(m_currentBlock.coinbaseAddress, r); +} + void State::execute(Transaction const& _t, Address _sender) { // Entry point for a contract-originated transaction. @@ -269,7 +313,7 @@ void State::execute(Transaction const& _t, Address _sender) { subBalance(_sender, _t.value + _t.fee); addBalance(_t.receiveAddress, _t.value); - addBalance(m_coinbaseAddress, _t.fee); + addBalance(m_currentBlock.coinbaseAddress, _t.fee); if (isContractAddress(_t.receiveAddress)) { @@ -292,7 +336,7 @@ void State::execute(Transaction const& _t, Address _sender) mem[i] = _t.data[i]; subBalance(_sender, _t.value + _t.fee); addBalance(newAddress, _t.value); - addBalance(m_coinbaseAddress, _t.fee); + addBalance(m_currentBlock.coinbaseAddress, _t.fee); } } diff --git a/libethereum/State.h b/libethereum/State.h index 9e43cad0f..e84fa23d7 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -105,15 +105,32 @@ public: /// The hash of the root of our state tree. h256 rootHash() const; + /// Finalise the block, applying the earned rewards. + void applyRewards(Addresses const& _uncleAddresses); + + /// Execute all transactions within a given block. + /// @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); + private: /// Fee-adder on destruction RAII class. struct MinerFeeAdder { - ~MinerFeeAdder() { state->addBalance(state->m_coinbaseAddress, fee); } + ~MinerFeeAdder() { state->addBalance(state->m_currentBlock.coinbaseAddress, fee); } State* state; u256 fee; }; + /// 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); + + /// 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); + /// Execute a decoded transaction object, given a sender. /// This will append @a _t to the transaction list and change the state accordingly. void execute(Transaction const& _t, Address _sender); @@ -121,11 +138,7 @@ private: /// Execute a contract transaction. void execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* o_totalFee); - /// Execute all transactions within a given block. - void playback(bytesConstRef _block); - - // TODO: std::hash
and then move to unordered_map. - // Will need to sort on hash construction. + // TODO: move over to Trie in leveldb; specify a snapshot for . std::map m_current; ///< The current state. We work with a C++ hash map rather than a Trie. std::map m_transactions; ///< The current list of transactions that we've included in the state. @@ -134,7 +147,7 @@ private: bytes m_currentBytes; ///< The current block. uint m_currentNumber; - Address m_coinbaseAddress; ///< Our address (i.e. the address to which fees go). + Address m_ourAddress; ///< Our address (i.e. the address to which fees go). /// The fee structure. Values yet to be agreed on... static const u256 c_stepFee; @@ -144,6 +157,7 @@ private: static const u256 c_cryptoFee; static const u256 c_newContractFee; static const u256 c_txFee; + static const u256 c_blockReward; }; } diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 183d95d2d..c289da204 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -27,9 +27,6 @@ namespace eth { -using PrivateKey = h256; -using Address = h160; - struct Signature { byte v;