Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions .github/actions/api-tests-run/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,18 @@ runs:
shell: bash
run: |
set -euo pipefail
patch_path="../hardfork-support/patches/execution-specs-berlin-nonce-overflow-stubs.patch"
if git -C test/api-tests/execution-specs apply --check "${patch_path}"; then
git -C test/api-tests/execution-specs apply "${patch_path}"
elif git -C test/api-tests/execution-specs apply --reverse --check "${patch_path}"; then
echo "execution-specs patch already applied: ${patch_path}"
else
echo "::error::execution-specs patch does not apply: ${patch_path}"
git -C test/api-tests/execution-specs apply --check "${patch_path}"
fi
for patch_path in test/api-tests/hardfork-support/patches/*.patch; do
rel_path="../hardfork-support/patches/$(basename "${patch_path}")"
if git -C test/api-tests/execution-specs apply --check "${rel_path}"; then
git -C test/api-tests/execution-specs apply "${rel_path}"
echo "execution-specs patch applied: ${rel_path}"
elif git -C test/api-tests/execution-specs apply --reverse --check "${rel_path}"; then
echo "execution-specs patch already applied: ${rel_path}"
else
echo "::error::execution-specs patch does not apply: ${rel_path}"
git -C test/api-tests/execution-specs apply --check "${rel_path}"
fi
done

- name: Render run.toml from template
shell: bash
Expand Down
14 changes: 10 additions & 4 deletions .github/actions/testeth-run/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,22 @@ runs:

cd "${{ inputs.build_dir }}/test"

# Per-suite hard cap so one hung suite fails fast instead of stalling the job.
# -k sends SIGKILL if the suite ignores the initial SIGTERM (e.g. stuck in
# consensus teardown). Overridable via the SUITE_TIMEOUT env var.
SUITE_TIMEOUT="${SUITE_TIMEOUT:-25m}"
run_testeth() { timeout -k 1m "$SUITE_TIMEOUT" ./testeth "$@"; }

if [[ "${{ inputs.mode }}" == "historic" ]]; then
mkdir -p /tmp/tests/
if [[ "${{ inputs.verbosity }}" == "1" ]]; then
sudo rm -rf /tmp/tests/*
fi

if [[ "${{ inputs.verbosity }}" == "1" ]]; then
./testeth -t JsonRpcSuite -- --express && touch /tmp/tests/JsonRpcSuitePassed || true
run_testeth -t JsonRpcSuite -- --express && touch /tmp/tests/JsonRpcSuitePassed || true
else
ls /tmp/tests/JsonRpcSuitePassed 2>/dev/null || ./testeth -t JsonRpcSuite -- --express --verbosity 4
ls /tmp/tests/JsonRpcSuitePassed 2>/dev/null || run_testeth -t JsonRpcSuite -- --express --verbosity 4
fi
exit 0
fi
Expand All @@ -58,7 +64,7 @@ runs:

run_test() {
local suite="$1"
if ./testeth --report_level=detailed -t "$suite" -- --express; then
if run_testeth --report_level=detailed -t "$suite" -- --express; then
touch "/tmp/tests/${suite}Passed"
else
echo "Suite ${suite} failed (continuing)"
Expand All @@ -72,7 +78,7 @@ runs:
if ls "/tmp/tests/${suite}Passed" 2>/dev/null; then
return
fi
./testeth --report_level=detailed -t "$suite" -- --express --verbosity 4
run_testeth --report_level=detailed -t "$suite" -- --express --verbosity 4
}
for s in "${TEST_SUITES[@]}"; do rerun_test "$s"; done
fi
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
build-and-test:
name: ${{ matrix.name }}
runs-on: ubuntu-22.04
# Backstop so a hung test can't ride GitHub's 6h default and stall the queue.
timeout-minutes: 120

strategy:
fail-fast: false
Expand Down
37 changes: 30 additions & 7 deletions libethashseal/Ethash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <libethcore/ChainOperationParams.h>
#include <libethcore/CommonJS.h>
#include <libethereum/Interface.h>
#include <libethereum/SchainPatch.h>

#include <ethash/ethash.hpp>

Expand Down Expand Up @@ -89,12 +90,28 @@ void Ethash::verify( Strictness _s, BlockHeader const& _bi, BlockHeader const& _
SealEngineFace::verify( _s, _bi, _parent, _block );

if ( _parent ) {
// Check difficulty is correct given the two timestamps.
auto expected = calculateDifficulty( _bi, _parent );
auto difficulty = _bi.difficulty();
if ( difficulty != expected )
BOOST_THROW_EXCEPTION( InvalidDifficulty() << RequirementError(
( bigint ) expected, ( bigint ) difficulty ) );
const bool isParis =
ParisForkPatch::isEnabledWhen( static_cast< time_t >( _parent.timestamp() ) );
if ( isParis ) {
if ( _bi.sealFieldCount() != 2 )
BOOST_THROW_EXCEPTION(
InvalidBlockFormat()
<< errinfo_comment( "Paris block header must contain prevRandao and nonce" ) );
// prevRandao is accumulated at construction (SkaleHost::createBlock) from the
// parent's value and the previous block's threshold signature; verify cannot
// re-derive it here (no consensus access), so only the header shape and the
// zero nonce are pinned.
if ( nonce( _bi ) != Nonce( 0 ) )
BOOST_THROW_EXCEPTION( InvalidBlockFormat() << errinfo_comment(
"Paris block header nonce must be zero" ) );
} else {
// Check difficulty is correct given the two timestamps.
auto expected = calculateDifficulty( _bi, _parent );
auto difficulty = _bi.difficulty();
if ( difficulty != expected )
BOOST_THROW_EXCEPTION( InvalidDifficulty() << RequirementError(
( bigint ) expected, ( bigint ) difficulty ) );
}
}

// check it hashes according to proof of work or that it's the genesis block.
Expand Down Expand Up @@ -198,7 +215,13 @@ u256 Ethash::calculateDifficulty( BlockHeader const& _bi, BlockHeader const& _pa

void Ethash::populateFromParent( BlockHeader& _bi, BlockHeader const& _parent ) const {
SealEngineFace::populateFromParent( _bi, _parent );
_bi.setDifficulty( calculateDifficulty( _bi, _parent ) );
if ( ParisForkPatch::isEnabledWhen( static_cast< time_t >( _parent.timestamp() ) ) ) {
_bi.setDifficulty( 0 );
setPrevRandao( _bi, h256( 0 ) );
setNonce( _bi, Nonce( 0 ) );
} else {
_bi.setDifficulty( calculateDifficulty( _bi, _parent ) );
}
_bi.setGasLimit( childGasLimit( _parent, chainParams().getMinGasLimit() ) );
}

Expand Down
5 changes: 5 additions & 0 deletions libethashseal/Ethash.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Ethash : public SealEngineBase {
static h256 seedHash( BlockHeader const& _bi );
static Nonce nonce( BlockHeader const& _bi ) { return _bi.seal< Nonce >( NonceField ); }
static h256 mixHash( BlockHeader const& _bi ) { return _bi.seal< h256 >( MixHashField ); }
static h256 prevRandao( BlockHeader const& _bi ) { return _bi.prevRandao(); }
static h256 boundary( BlockHeader const& _bi ) {
auto d = _bi.difficulty();
return d ? ( h256 ) u256( ( bigint( 1 ) << 256 ) / d ) : h256();
Expand All @@ -79,6 +80,10 @@ class Ethash : public SealEngineBase {
_bi.setSeal( MixHashField, _v );
return _bi;
}
static BlockHeader& setPrevRandao( BlockHeader& _bi, h256 const& _v ) {
_bi.setPrevRandao( _v );
return _bi;
}

u256 calculateDifficulty( BlockHeader const& _bi, BlockHeader const& _parent ) const;
u256 childGasLimit( BlockHeader const& _bi, u256 const& _gasFloorTarget = Invalid256 ) const;
Expand Down
4 changes: 4 additions & 0 deletions libethcore/BlockHeader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ void BlockHeader::populateFromParent( BlockHeader const& _parent ) {
m_parentHash = _parent.m_hash;
m_gasLimit = _parent.m_gasLimit;
m_difficulty = _parent.m_difficulty;
// EIP-3675 (Paris): post-merge blocks must carry difficulty 0. Activation is
// keyed to the parent timestamp, same as the check in SealEngineFace::verify.
if ( ParisForkPatch::isEnabledWhen( static_cast< time_t >( _parent.timestamp() ) ) )
m_difficulty = 0;
m_gasUsed = 0;
m_baseFeePerGas = _parent.m_baseFeePerGas;
// At London activation, the parent may be a pre-London block with baseFeePerGas=0
Expand Down
6 changes: 6 additions & 0 deletions libethcore/BlockHeader.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ class BlockHeader {
void setSeal( T const& _value ) {
setSeal( 0, _value );
}
void setPrevRandao( h256 const& _value ) { setSeal( 0, _value ); }

h256 const& parentHash() const { return m_parentHash; }
h256 const& sha3Uncles() const { return m_sha3Uncles; }
Expand All @@ -200,6 +201,7 @@ class BlockHeader {
bytes const& extraData() const { return m_extraData; }
LogBloom const& logBloom() const { return m_logBloom; }
u256 const& difficulty() const { return m_difficulty; }
h256 prevRandao() const { return seal< h256 >( 0 ); }
u256 baseFeePerGas() const { return m_baseFeePerGas; }
void setBaseFeePerGas( u256 const& _v ) {
m_baseFeePerGas = _v;
Expand All @@ -213,6 +215,10 @@ class BlockHeader {
ret = RLP( m_seal[_offset] ).convert< T >( RLP::VeryStrict );
return ret;
}
size_t sealFieldCount() const {
Guard l( m_sealLock );
return m_seal.size();
}

private:
void populate( RLP const& _header );
Expand Down
10 changes: 9 additions & 1 deletion libethcore/SealEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,15 @@ void SealEngineFace::verify( Strictness _s, BlockHeader const& _bi, BlockHeader
_bi.verify( _s, _parent, _block );

if ( _s != CheckNothingNew ) {
if ( _bi.difficulty() < chainParams().getMinimumDifficulty() )
const time_t committedBlockTimestamp = _parent ?
static_cast< time_t >( _parent.timestamp() ) :
static_cast< time_t >( _bi.timestamp() );
if ( _parent && ParisForkPatch::isEnabledWhen( committedBlockTimestamp ) ) {
if ( _bi.difficulty() != 0 )
BOOST_THROW_EXCEPTION( InvalidDifficulty() << RequirementError(
bigint( 0 ), bigint( _bi.difficulty() ) ) );
} else if ( !ParisForkPatch::isEnabledWhen( committedBlockTimestamp ) &&
_bi.difficulty() < chainParams().getMinimumDifficulty() )
BOOST_THROW_EXCEPTION(
InvalidDifficulty() << RequirementError(
bigint( chainParams().getMinimumDifficulty() ), bigint( _bi.difficulty() ) ) );
Expand Down
34 changes: 28 additions & 6 deletions libethereum/Block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "Executive.h"
#include "ExtVM.h"
#include "GenesisInfo.h"
#include "SchainPatch.h"
#include "TransactionQueue.h"
#include <libdevcore/Assertions.h>
#include <libdevcore/CommonIO.h>
Expand Down Expand Up @@ -529,7 +530,7 @@ void Block::sanityCheckPartialTransactionReceipts( std::optional< BlockNumber >

tuple< TransactionReceipts, unsigned, bool > Block::syncEveryone( BlockChain const& _bc,
const Transactions& _transactions, uint64_t _timestamp, u256 _gasPrice, u256 _baseFeePerGas,
OnTransactionConsumed const& _onTransactionConsumed ) {
OnTransactionConsumed const& _onTransactionConsumed, u256 _prevRandao ) {
if ( isSealed() )
BOOST_THROW_EXCEPTION( InvalidOperationOnSealedBlock() );

Expand All @@ -540,7 +541,8 @@ tuple< TransactionReceipts, unsigned, bool > Block::syncEveryone( BlockChain con
context.singleCommitEnabled = SingleStateCommitPerBlockPatch::isEnabledInWorkingBlock();

if ( context.singleCommitEnabled && isCurrentBlockCommitted() ) {
auto recovered = recoverFromReceipts( _transactions, _timestamp, _baseFeePerGas );
auto recovered =
recoverFromReceipts( _transactions, _timestamp, _baseFeePerGas, _prevRandao );
bool needsQueueReadyNotification = false;
Transactions queueCleanupTransactions;
u256 cumulativeGas = 0;
Expand All @@ -563,7 +565,7 @@ tuple< TransactionReceipts, unsigned, bool > Block::syncEveryone( BlockChain con
return make_tuple( recovered.first, recovered.second, needsQueueReadyNotification );
}

prepareStateForSync( _timestamp, _baseFeePerGas, context );
prepareStateForSync( _timestamp, _baseFeePerGas, _prevRandao, context );
executeTransactions( _bc, _transactions, _gasPrice, context, _onTransactionConsumed );

if ( !context.singleCommitEnabled || !isCurrentBlockCommitted() ) {
Expand All @@ -575,7 +577,7 @@ tuple< TransactionReceipts, unsigned, bool > Block::syncEveryone( BlockChain con
}

std::pair< TransactionReceipts, unsigned > Block::recoverFromReceipts(
const Transactions& _transactions, uint64_t, u256 _baseFeePerGas ) {
const Transactions& _transactions, uint64_t, u256 _baseFeePerGas, u256 _prevRandao ) {
if ( !SingleStateCommitPerBlockPatch::isEnabledInWorkingBlock() ) {
BOOST_THROW_EXCEPTION(
std::runtime_error( "recoverFromReceipts called outside single commit mode" ) );
Expand All @@ -600,6 +602,9 @@ std::pair< TransactionReceipts, unsigned > Block::recoverFromReceipts(
resetCurrent( savedData->timestamp );
if ( _baseFeePerGas != 0 )
m_currentBlock.setBaseFeePerGas( _baseFeePerGas );
// Nonzero only post-Paris (gated in SkaleHost::createBlock); recovery must rebuild the
// exact header the pre-crash execution was producing.
applyPrevRandao( _prevRandao );

for ( const auto& tx : _transactions ) {
m_transactions.push_back( tx );
Expand All @@ -624,10 +629,20 @@ std::pair< TransactionReceipts, unsigned > Block::recoverFromReceipts(
return std::make_pair( m_receipts, m_receipts.size() - badCount );
}

void Block::prepareStateForSync( uint64_t _timestamp, u256 _baseFeePerGas, SyncContext& _context ) {
void Block::applyPrevRandao( u256 _prevRandao ) {
if ( _prevRandao != 0 && m_currentBlock.sealFieldCount() == 2 )
m_currentBlock.setPrevRandao( h256( _prevRandao ) );
}

void Block::prepareStateForSync(
uint64_t _timestamp, u256 _baseFeePerGas, u256 _prevRandao, SyncContext& _context ) {
resetCurrent( _timestamp );
if ( _baseFeePerGas != 0 )
m_currentBlock.setBaseFeePerGas( _baseFeePerGas );
// Nonzero only post-Paris (gated in SkaleHost::createBlock). resetCurrent() wrote the
// Paris zero seal fields via Ethash::populateFromParent; this overrides the value the
// same way baseFee is set.
applyPrevRandao( _prevRandao );
m_state = m_state.createStateCopyAndClearCaches();

#ifndef FAIR
Expand Down Expand Up @@ -1036,7 +1051,14 @@ u256 Block::enact( VerifiedBlockRef const& _block, BlockChain const& _bc ) {
u256 tdIncrease = m_currentBlock.difficulty();

// Check uncles & apply their rewards to state.
if ( rlp[2].itemCount() > 2 ) {
if ( ParisForkPatch::isEnabledWhen( previousInfo().timestamp() ) ) {
if ( rlp[2].itemCount() > 0 ) {
TooManyUncles ex;
ex << errinfo_max( 0 );
ex << errinfo_got( rlp[2].itemCount() );
BOOST_THROW_EXCEPTION( ex );
}
} else if ( rlp[2].itemCount() > 2 ) {
TooManyUncles ex;
ex << errinfo_max( 2 );
ex << errinfo_got( rlp[2].itemCount() );
Expand Down
15 changes: 12 additions & 3 deletions libethereum/Block.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,14 @@ class Block {
std::tuple< TransactionReceipts, unsigned, bool > syncEveryone( BlockChain const& _bc,
const Transactions& _transactions, uint64_t _timestamp, u256 _gasPrice,
u256 _baseFeePerGas = 0,
OnTransactionConsumed const& _onTransactionConsumed = OnTransactionConsumed() );
OnTransactionConsumed const& _onTransactionConsumed = OnTransactionConsumed(),
u256 _prevRandao = 0 );

/// Write prevRandao into the current (working) header. Applied only when the seal
/// engine produced the Paris 2-field seal shape — never invents seal fields on
/// engines (e.g. NoProof test chains) whose headers carry none: a single-field
/// seal is rejected by BlockHeader::populate().
void applyPrevRandao( u256 _prevRandao );

/// Execute all transactions within a given block.
/// @returns the additional total difficulty.
Expand Down Expand Up @@ -365,7 +372,8 @@ class Block {
/// Undo the changes to the state for committing to mine.
void uncommitToSeal();

void prepareStateForSync( uint64_t _timestamp, u256 _baseFeePerGas, SyncContext& _context );
void prepareStateForSync(
uint64_t _timestamp, u256 _baseFeePerGas, u256 _prevRandao, SyncContext& _context );
void executeTransactions( BlockChain const& _bc, const Transactions& _transactions,
u256 _gasPrice, SyncContext& _context,
OnTransactionConsumed const& _onTransactionConsumed );
Expand All @@ -377,7 +385,8 @@ class Block {
// Loads saved receipts from progress log to skip re-execution after crash.
// Throws if called outside single commit mode or if receipts are unavailable.
std::pair< TransactionReceipts, unsigned > recoverFromReceipts(
const Transactions& _transactions, uint64_t _timestamp, u256 _baseFeePerGas );
const Transactions& _transactions, uint64_t _timestamp, u256 _baseFeePerGas,
u256 _prevRandao );
void saveStateChanges(
BlockChain const& _bc, const Transactions& _transactions, const SyncContext& _context );
void runCommit( BlockChain const& _bc, const SyncContext& _context ); // run commit for state
Expand Down
15 changes: 10 additions & 5 deletions libethereum/Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ size_t Client::importTransactionsAsBlock( const Transactions& _transactions,
uint64_t _winningNodeIndex,
#endif
uint64_t _timestamp, Block::OnTransactionConsumed const& _onTransactionConsumed,
bool* _needsQueueReadyNotification ) {
bool* _needsQueueReadyNotification, u256 _prevRandao ) {
// on schain creation, SnapshotAgent needs timestamp of block 1
// so we use this HACK
// pass block number 0 as for bigger BN it is initialized in init()
Expand Down Expand Up @@ -591,7 +591,7 @@ size_t Client::importTransactionsAsBlock( const Transactions& _transactions,

size_t cntSucceeded = 0;
cntSucceeded = syncTransactions( _transactions, _gasPrice, _timestamp, _onTransactionConsumed,
_needsQueueReadyNotification );
_needsQueueReadyNotification, _prevRandao );
sealUnconditionally( false );
importWorkingBlock();

Expand Down Expand Up @@ -667,7 +667,7 @@ bool Client::updateGroupIfNeeded() {

size_t Client::syncTransactions( const Transactions& _transactions, u256 _gasPrice,
uint64_t _timestamp, Block::OnTransactionConsumed const& _onTransactionConsumed,
bool* _needsQueueReadyNotification ) {
bool* _needsQueueReadyNotification, u256 _prevRandao ) {
assert( m_skaleHost );

while ( m_working.isSealed() ) {
Expand Down Expand Up @@ -702,8 +702,8 @@ size_t Client::syncTransactions( const Transactions& _transactions, u256 _gasPri
}

tie( newPendingReceipts, goodReceipts, needsQueueReadyNotification ) =
m_working.syncEveryone(
bc(), _transactions, _timestamp, _gasPrice, baseFeePerGas, _onTransactionConsumed );
m_working.syncEveryone( bc(), _transactions, _timestamp, _gasPrice, baseFeePerGas,
_onTransactionConsumed, _prevRandao );
m_state = m_state.createStateCopyAndClearCaches();
#ifdef HISTORIC_STATE
// make sure the trie in new state object points to the new state root
Expand Down Expand Up @@ -779,6 +779,11 @@ void Client::restartMining() {
preChanged = newPreMine.sync( bc(), m_state );

if ( preChanged || m_postSeal.author() != m_preSeal.author() ) {
// Pending simulations (eth_call/eth_estimateGas execute against the working
// block) must see the same prevRandao a real next block would carry, not the
// zero placeholder populateFromParent writes.
if ( m_skaleHost )
newPreMine.applyPrevRandao( m_skaleHost->getPrevRandaoForPendingBlock() );
DEV_WRITE_GUARDED( x_preSeal )
m_preSeal = newPreMine;
DEV_WRITE_GUARDED( x_working )
Expand Down
Loading
Loading