Browse Source

Avoid checking for difficulty when doing PoW test.

cl-refactor
Gav Wood 10 years ago
parent
commit
717c9c06df
  1. 35
      libethcore/BlockInfo.cpp
  2. 21
      libethcore/BlockInfo.h
  3. 30
      libethereum/Client.cpp
  4. 14
      libethereum/Client.h
  5. 65
      libethereum/State.cpp
  6. 30
      libethereum/State.h
  7. 2
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  8. 13
      test/blockchain.cpp
  9. 2
      test/dagger.cpp

35
libethcore/BlockInfo.cpp

@ -35,9 +35,9 @@ BlockInfo::BlockInfo(): timestamp(Invalid256)
{
}
BlockInfo::BlockInfo(bytesConstRef _block, bool _checkNonce)
BlockInfo::BlockInfo(bytesConstRef _block, Strictness _s)
{
populate(_block, _checkNonce);
populate(_block, _s);
}
void BlockInfo::setEmpty()
@ -61,10 +61,10 @@ void BlockInfo::setEmpty()
hash = headerHash(WithNonce);
}
BlockInfo BlockInfo::fromHeader(bytesConstRef _block)
BlockInfo BlockInfo::fromHeader(bytesConstRef _block, Strictness _s)
{
BlockInfo ret;
ret.populateFromHeader(RLP(_block));
ret.populateFromHeader(RLP(_block), _s);
return ret;
}
@ -89,7 +89,7 @@ h256 BlockInfo::headerHash(bytesConstRef _block)
return sha3(RLP(_block)[0].data());
}
void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce)
void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s)
{
hash = dev::sha3(_header.data());
@ -121,30 +121,33 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce)
}
// check it hashes according to proof of work or that it's the genesis block.
if (_checkNonce && parentHash && !ProofOfWork::verify(*this))
if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this))
BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty));
if (gasUsed > gasLimit)
BOOST_THROW_EXCEPTION(TooMuchGasUsed() << RequirementError(bigint(gasLimit), bigint(gasUsed)) );
if (_s != CheckNothing)
{
if (gasUsed > gasLimit)
BOOST_THROW_EXCEPTION(TooMuchGasUsed() << RequirementError(bigint(gasLimit), bigint(gasUsed)) );
if (difficulty < c_minimumDifficulty)
BOOST_THROW_EXCEPTION(InvalidDifficulty() << RequirementError(bigint(c_minimumDifficulty), bigint(difficulty)) );
if (difficulty < c_minimumDifficulty)
BOOST_THROW_EXCEPTION(InvalidDifficulty() << RequirementError(bigint(c_minimumDifficulty), bigint(difficulty)) );
if (gasLimit < c_minGasLimit)
BOOST_THROW_EXCEPTION(InvalidGasLimit() << RequirementError(bigint(c_minGasLimit), bigint(gasLimit)) );
if (gasLimit < c_minGasLimit)
BOOST_THROW_EXCEPTION(InvalidGasLimit() << RequirementError(bigint(c_minGasLimit), bigint(gasLimit)) );
if (number && extraData.size() > c_maximumExtraDataSize)
BOOST_THROW_EXCEPTION(ExtraDataTooBig() << RequirementError(bigint(c_maximumExtraDataSize), bigint(extraData.size())));
if (number && extraData.size() > c_maximumExtraDataSize)
BOOST_THROW_EXCEPTION(ExtraDataTooBig() << RequirementError(bigint(c_maximumExtraDataSize), bigint(extraData.size())));
}
}
void BlockInfo::populate(bytesConstRef _block, bool _checkNonce)
void BlockInfo::populate(bytesConstRef _block, Strictness _s)
{
RLP root(_block);
RLP header = root[0];
if (!header.isList())
BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block header needs to be a list") << BadFieldError(0, header.data().toString()));
populateFromHeader(header, _checkNonce);
populateFromHeader(header, _s);
if (!root[1].isList())
BOOST_THROW_EXCEPTION(InvalidBlockFormat() << errinfo_comment("block transactions need to be a list") << BadFieldError(1, root[1].data().toString()));

21
libethcore/BlockInfo.h

@ -36,6 +36,13 @@ enum IncludeNonce
WithNonce = 1
};
enum Strictness
{
CheckEverything,
IgnoreNonce,
CheckNothing
};
/** @brief Encapsulation of a block header.
* Class to contain all of a block header's data. It is able to parse a block header and populate
* from some given RLP block serialisation with the static fromHeader(), through the method
@ -79,14 +86,14 @@ public:
Nonce nonce;
BlockInfo();
explicit BlockInfo(bytes const& _block): BlockInfo(&_block) {}
explicit BlockInfo(bytesConstRef _block, bool _checkNonce = true);
explicit BlockInfo(bytes const& _block, Strictness _s = CheckEverything): BlockInfo(&_block, _s) {}
explicit BlockInfo(bytesConstRef _block, Strictness _s = CheckEverything);
static h256 headerHash(bytes const& _block) { return headerHash(&_block); }
static h256 headerHash(bytesConstRef _block);
static BlockInfo fromHeader(bytes const& _block) { return fromHeader(bytesConstRef(&_block)); }
static BlockInfo fromHeader(bytesConstRef _block);
static BlockInfo fromHeader(bytes const& _block, Strictness _s = CheckEverything) { return fromHeader(bytesConstRef(&_block), _s); }
static BlockInfo fromHeader(bytesConstRef _block, Strictness _s = CheckEverything);
explicit operator bool() const { return timestamp != Invalid256; }
@ -113,9 +120,9 @@ public:
void setEmpty();
void populateFromHeader(RLP const& _header, bool _checkNonce = true);
void populate(bytesConstRef _block, bool _checkNonce = true);
void populate(bytes const& _block, bool _checkNonce = true) { populate(&_block, _checkNonce); }
void populateFromHeader(RLP const& _header, Strictness _s = CheckEverything);
void populate(bytesConstRef _block, Strictness _s = CheckEverything);
void populate(bytes const& _block, Strictness _s = CheckEverything) { populate(&_block, _s); }
void verifyInternals(bytesConstRef _block) const;
void verifyParent(BlockInfo const& _parent) const;
void populateFromParent(BlockInfo const& parent);

30
libethereum/Client.cpp

@ -64,10 +64,38 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, bool _forceClean,
Worker("eth"),
m_vc(_dbPath),
m_bc(_dbPath, !m_vc.ok() || _forceClean),
m_gp(u256("60000000000000")),
m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)),
m_preMine(Address(), m_stateDB),
m_postMine(Address(), m_stateDB)
{
m_gp.updateQuartiles(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
if (_miners > -1)
setMiningThreads(_miners);
else
setMiningThreads();
if (_dbPath.size())
Defaults::setDBPath(_dbPath);
m_vc.setOk();
doWork();
startWorking();
}
Client::Client(p2p::Host* _extNet, u256 weiPerCent, std::string const& _dbPath, bool _forceClean, u256 _networkId, int _miners):
Worker("eth"),
m_vc(_dbPath),
m_bc(_dbPath, !m_vc.ok() || _forceClean),
m_gp(weiPerCent),
m_stateDB(State::openDB(_dbPath, !m_vc.ok() || _forceClean)),
m_preMine(Address(), m_stateDB),
m_postMine(Address(), m_stateDB)
{
m_gp.updateQuartiles(m_bc);
m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId));
if (_miners > -1)
@ -592,7 +620,7 @@ void Client::doWork()
// returns h256s as blooms, once for each transaction.
cwork << "postSTATE <== TQ";
TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq);
TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, m_gp);
if (newPendingReceipts.size())
{
for (size_t i = 0; i < newPendingReceipts.size(); i++)

14
libethereum/Client.h

@ -177,6 +177,15 @@ public:
int _miners = -1
);
explicit Client(
p2p::Host* _host,
u256 _weiPerCent,
std::string const& _dbPath = std::string(),
bool _forceClean = false,
u256 _networkId = 0,
int _miners = -1
);
/// Destructor.
virtual ~Client();
@ -352,6 +361,7 @@ private:
CanonBlockChain m_bc; ///< Maintains block database.
TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain.
BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported).
GasPricer m_gp; ///< The gas pricer.
mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine.
OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it.
@ -368,7 +378,9 @@ private:
bool m_paranoia = false; ///< Should we be paranoid about our state?
bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping.
bool m_forceMining = false; ///< Mine even when there are no transactions pending?
bool m_verifyOwnBlocks = true; ///< Shoudl be verify blocks that we mined?
bool m_verifyOwnBlocks = true; ///< Should be verify blocks that we mined?
mutable Mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters;

65
libethereum/State.cpp

@ -400,7 +400,7 @@ bool State::cull(TransactionQueue& _tq) const
return ret;
}
TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, bool* o_transactionQueueChanged)
TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, bool* o_transactionQueueChanged)
{
// TRANSACTIONS
TransactionReceipts ret;
@ -414,16 +414,20 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, bo
for (auto const& i: ts)
if (!m_transactionSet.count(i.first))
{
// don't have it yet! Execute it now.
try
{
uncommitToMine();
// boost::timer t;
execute(lh, i.second);
ret.push_back(m_receipts.back());
_tq.noteGood(i);
++goodTxs;
// cnote << "TX took:" << t.elapsed() * 1000;
Transaction t(i.second, CheckSignature::Sender);
if (t.gasPrice() >= _gp.ask(*this))
{
// don't have it yet! Execute it now.
uncommitToMine();
// boost::timer t;
execute(lh, i.second);
ret.push_back(m_receipts.back());
_tq.noteGood(i);
++goodTxs;
// cnote << "TX took:" << t.elapsed() * 1000;
}
}
catch (InvalidNonce const& in)
{
@ -460,12 +464,51 @@ TransactionReceipts State::sync(BlockChain const& _bc, TransactionQueue& _tq, bo
return ret;
}
void GasPricer::updateQuartiles(BlockChain const& _bc)
{
unsigned c = 0;
h256 p = _bc.currentHash();
map<u256, unsigned> dist;
unsigned total;
while (c < 1000 && p)
{
BlockInfo bi = _bc.info(p);
if (bi.transactionsRoot != EmptyTrie)
{
auto bb = _bc.block(p);
RLP r(bb);
BlockReceipts brs(_bc.receipts(bi.hash));
for (unsigned i = 0; i < r[1].size(); ++i)
{
auto gu = brs.receipts[i].gasUsed();
dist[Transaction(r[1][i].data(), CheckSignature::None).gasPrice()] += (unsigned)brs.receipts[i].gasUsed();
total += (unsigned)gu;
}
}
p = bi.parentHash;
++c;
}
if (total > 0)
{
unsigned t = 0;
unsigned q = 1;
for (auto const& i: dist)
{
for (; t <= total * q / 4 && t + i.second > total * q / 4; ++q)
m_quartiles[q - 1] = i.first;
if (q > 3)
break;
}
}
}
u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
{
// m_currentBlock is assumed to be prepopulated and reset.
#if !ETH_RELEASE
BlockInfo bi(_block, _checkNonce);
BlockInfo bi(_block, _checkNonce ? CheckEverything : IgnoreNonce);
assert(m_previousBlock.hash == bi.parentHash);
assert(m_currentBlock.parentHash == bi.parentHash);
assert(rootHash() == m_previousBlock.stateRoot);
@ -475,7 +518,7 @@ u256 State::enact(bytesConstRef _block, BlockChain const& _bc, bool _checkNonce)
BOOST_THROW_EXCEPTION(InvalidParentHash());
// Populate m_currentBlock with the correct values.
m_currentBlock.populate(_block, _checkNonce);
m_currentBlock.populate(_block, _checkNonce ? CheckEverything : IgnoreNonce);
m_currentBlock.verifyInternals(_block);
// cnote << "playback begins:" << m_state.root();

30
libethereum/State.h

@ -55,6 +55,8 @@ struct StateSafeExceptions: public LogChannel { static const char* name() { retu
enum class BaseState { Empty, CanonGenesis };
class GasPricer;
/**
* @brief Model of the current state of the ledger.
* Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block).
@ -147,7 +149,7 @@ public:
/// @returns a list of receipts one for each transaction placed from the queue into the state.
/// @a o_transactionQueueChanged boolean pointer, the value of which will be set to true if the transaction queue
/// changed and the pointer is non-null
TransactionReceipts sync(BlockChain const& _bc, TransactionQueue& _tq, bool* o_transactionQueueChanged = nullptr);
TransactionReceipts sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, bool* o_transactionQueueChanged = nullptr);
/// Like sync but only operate on _tq, killing the invalid/old ones.
bool cull(TransactionQueue& _tq) const;
@ -368,6 +370,32 @@ void commit(std::map<Address, Account> const& _cache, DB& _db, SecureTrieDB<Addr
}
}
enum class TransactionPriority
{
Low = 0,
Medium = 1,
High = 2
};
class GasPricer
{
public:
explicit GasPricer(u256 _weiPerCent): m_weiPerCent(_weiPerCent) {}
u256 ask(State const&) const { return m_weiPerCent * m_centsPerBlock / m_gasPerBlock; }
u256 bid(TransactionPriority _p = TransactionPriority::Medium) const { return m_quartiles[(int)_p]; }
void updateRefPrice(u256 _weiPerCent) { m_weiPerCent = _weiPerCent; }
void updateRefRequirement(u256 _centsPerBlock) { m_centsPerBlock = _centsPerBlock; }
void updateQuartiles(BlockChain const& _bc);
private:
u256 m_weiPerCent;
u256 m_centsPerBlock = 15;
u256 m_gasPerBlock = 1000000;
std::array<u256, 100> m_quartiles;
};
}
}

2
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -710,7 +710,7 @@ std::string WebThreeStubServerBase::eth_transact(Json::Value const& _json)
if (t.creation)
ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));;
if (!t.gasPrice)
t.gasPrice = 10 * dev::eth::szabo;
t.gasPrice = 10 * dev::eth::szabo; // TODO: should be determined by user somehow.
if (!t.gas)
t.gas = min<u256>(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice);

13
test/blockchain.cpp

@ -87,6 +87,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
// get txs
TransactionQueue txs;
GasPricer gp(10000);
BOOST_REQUIRE(blObj.count("transactions"));
for (auto const& txObj: blObj["transactions"].get_array())
{
@ -131,7 +132,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
try
{
state.sync(bc);
state.sync(bc,txs);
state.sync(bc, txs, gp);
state.commitToMine(bc);
MineInfo info;
for (info.completed = false; !info.completed; info = state.mine()) {}
@ -281,7 +282,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
BlockInfo blockHeaderFromFields;
const bytes c_rlpBytesBlockHeader = createBlockRLPFromFields(tObj);
const RLP c_blockHeaderRLP(c_rlpBytesBlockHeader);
blockHeaderFromFields.populateFromHeader(c_blockHeaderRLP, false);
blockHeaderFromFields.populateFromHeader(c_blockHeaderRLP, IgnoreNonce);
BlockInfo blockFromRlp = bc.info();
@ -381,7 +382,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
BlockInfo uncleBlockHeader;
try
{
uncleBlockHeader.populateFromHeader(c_uRLP, true);
uncleBlockHeader.populateFromHeader(c_uRLP);
}
catch(...)
{
@ -395,7 +396,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin)
for (auto const& uRLP: root[2])
{
BlockInfo uBl;
uBl.populateFromHeader(uRLP, true);
uBl.populateFromHeader(uRLP);
uBlHsFromRlp.push_back(uBl);
}
@ -538,7 +539,7 @@ void overwriteBlockHeader(BlockInfo& _current_BlockHeader, mObject& _blObj)
// take the blockheader as is
const bytes c_blockRLP = createBlockRLPFromFields(_blObj["blockHeader"].get_obj());
const RLP c_bRLP(c_blockRLP);
_current_BlockHeader.populateFromHeader(c_bRLP, false);
_current_BlockHeader.populateFromHeader(c_bRLP, IgnoreNonce);
}
}
@ -551,7 +552,7 @@ BlockInfo constructBlock(mObject& _o)
// construct genesis block
const bytes c_blockRLP = createBlockRLPFromFields(_o);
const RLP c_bRLP(c_blockRLP);
ret.populateFromHeader(c_bRLP, false);
ret.populateFromHeader(c_bRLP, IgnoreNonce);
}
catch (Exception const& _e)
{

2
test/dagger.cpp

@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(basic_test)
cnote << i.first;
js::mObject& o = i.second.get_obj();
vector<pair<string, string>> ss;
BlockInfo header = BlockInfo::fromHeader(fromHex(o["header"].get_str()));
BlockInfo header = BlockInfo::fromHeader(fromHex(o["header"].get_str()), CheckNothing);
h256 headerHash(o["header_hash"].get_str());
Nonce nonce(o["nonce"].get_str());
BOOST_REQUIRE_EQUAL(headerHash, header.headerHash(WithoutNonce));

Loading…
Cancel
Save