Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

EIP96 - blockhash refactoring #4066

Merged
merged 12 commits into from
Jun 3, 2017
Merged

EIP96 - blockhash refactoring #4066

merged 12 commits into from
Jun 3, 2017

Conversation

gumb0
Copy link
Member

@gumb0 gumb0 commented May 3, 2017

#3612
ethereum/EIPs#210

  • Contract creation
  • A call to update stored block hashes before processing transactions in each block
  • BLOCKHASH implementation using the call to contract
  • Change BLOCKHASH cost after Metropolis

@gumb0 gumb0 added this to the Metropolis milestone May 3, 2017
@gumb0 gumb0 force-pushed the eip96 branch 3 times, most recently from e44f355 to f5f802a Compare May 4, 2017 10:01
@@ -67,6 +67,8 @@ const Address dev::ZeroAddress = Address();

const Address dev::MaxAddress = Address("0xffffffffffffffffffffffffffffffffffffffff");

const Address dev::SuperuserAddress = Address("0xfffffffffffffffffffffffffffffffffffffffe");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been renamed to system.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll rename it


h256 ExtVM::blockHash(u256 _number)
{
if (envInfo().number() < m_sealEngine.chainParams().u256Param("metropolisForkBlock") + 256)
Copy link
Member

@axic axic May 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was agreed last time that the opcode remains limited to the last 256 blocks.

Also the comment in ExtVM.h should be kept up to date with the final decision:

  • /// Hash of a block if within the last 256 blocks, or h256() otherwise.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So where the code or comment disagrees with this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the comment disagrees with the code. If you update the code to match the current EIP (limited to 256) then the comment is fine again :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the disagreement.
This line in particular just checks whether the current block (not the block requested) is before or after Metro+256 (according to this spec https:/ethereum/EIPs/pull/210/files#diff-e02a92c2fb96c1a1bfb05e4c6e2ef5daR34 )

If we are before Metro+256, we return, as comment says, "hash of a block if within the last 256 blocks, or h256() otherwise."
Otherwise we do the same but through the call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see that it is limited to the last 256 blocks through the call below after Metropolis?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see, I was assuming that the contract itself (the new version of it) will return 0 if requested block is out of bounds.
Yeah, anyway it will makes sense to avoid making a call in this case, I'll change it.
Thanks for noticing this!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The contract has no easy way knowing where the call came from and its code may be different too in the future. Additionally the spec will/should say that the opcode itself filters for <=256.

@gumb0
Copy link
Member Author

gumb0 commented May 17, 2017

Current status:

  1. Waiting for Blockchain tests update to be merged Update Blockchain Transition tests for BLOCKHASH refactoring EIP tests#167
  2. Waiting for failing state tests to be updated by @winsvega (the plan is to convert state tests that use BLOCKHASH into Blockchain tests)
  3. don't pass last hashes to Executive after Metropolis, saving performance

This should better go into separate PR, because looks like some non-trivial refactoring is needed there

Otherwise I think it's ready for review.

@@ -108,6 +109,7 @@ static const EVMSchedule EIP158Schedule = []
static const EVMSchedule MetropolisSchedule = []
{
EVMSchedule schedule = EIP158Schedule;
schedule.blockhashGas = 800;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's uncertain to me if 800 should be applied from the beginning of Metropolis from 256 blocks into Metropolis. I asked a question https:/ethereum/EIPs/pull/210/files#r117211219

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I will ask in the allcoredev meeting ethereum/pm#14 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this seem to be strange. Shouldn't we just charge for the contract execution?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can stay as it is. In this previous allcoredev meeting, it was agreed that the gas schedule changes at the Metropolis block (inclusive), not later.

Transaction tx(0, 0, gas, c_blockhashContractAddress, m_previousBlock.hash().asBytes(), nonce);
tx.forceSender(SystemAddress);
// passing empty lastHashes - assuming the contract doesn't use BLOCKHASH
m_state.execute(EnvInfo(info(), {}, 0), *m_sealEngine, tx, Permanence::Committed);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this doesn't increment the nonce.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I think it does. Shouldn't it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if it should or not. So I need to ask.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performing the call to update the blockhash contract should be similar to CALL from EVM, not to external transaction and should not increment SYSTEM_ADDRESS's nonce.

So the fix is needed here.


if (currentNumber < m_sealEngine.chainParams().u256Param("metropolisForkBlock") + 256)
{
assert(envInfo().lastHashes().size() > (unsigned)(currentNumber - 1 - _number));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion seems to fail if currentNumber == 0 and _number == 0xff..ff (which is 0 - 1). I think lastHashes().size() should be zero in that case, and the right hand side evaluates to zero.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We won't get here because of the check at line 142, requested _number is always less than currentNumber here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also currentNumber is never 0 here, because you can't execute anything in the genesis block

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, seems valid.

@gumb0
Copy link
Member Author

gumb0 commented May 19, 2017

Rebased and comments addressed.

"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this from the codebase?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove it in a separate PR

@@ -53,6 +53,9 @@ const unsigned c_databaseVersionModifier = 0;

const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (23 << 9);

const Address c_blockhashContractAddress(0xf0);
const bytes c_blockhashContractCode(fromHex("0x73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use an assert to check the hash of this code against other implementations? Or will the code hash be part of the block tests anyway?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might there be any global variable initialization race condition problems here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Yeah, blockchain tests check that the contract with correct code exists in the state

  2. There shouldn't be as long as we don't use these constants for other static variable initialization in another module. Which we shouldn't do.

gumb0 and others added 2 commits May 19, 2017 19:32
…f executing fake Transaction. Thus SystemAddresss's nonce won't be incremented as EIP96 requires.
# Conflicts:
#	test/tools/libtesteth/TestHelper.h
@pirapira
Copy link
Member

The Travis test failure looks related to the blockhash https://travis-ci.org/ethereum/cpp-ethereum/jobs/237208475#L1908

@@ -115,25 +116,49 @@ class generaltestfixture

void fillAllFilesInFolder(string _folder)
{
std::string fillersPath = dev::test::getTestPath() + "/src/GeneralStateTestsFiller/" + _folder;
std::string fillersPath = getTestPath() + "/src/GeneralStateTestsFiller/" + _folder;
using dirIt = boost::filesystem::directory_iterator;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@winsvega Begin type alias with uppercase latter


boost::filesystem::directory_iterator iterator_tmp(fillersPath);
dirIt iterator_tmp(fillersPath);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@winsvega use camelCase instead of snake_case

//Skip filling this tests as state tests
if (Options::get().filltests && !Options::get().fillchain)
{
TestOutputHelper::incTestCount(2);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@winsvega What does this do?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as state test was not designed to run as a blockchain test. I have to overrun the test counter with this to get 100% tests executed output.

if (boost::filesystem::is_regular_file(iterator->path()) && iterator->path().extension() == ".json")
{
string fileboost = iterator->path().filename().string();
dev::test::executeTests(fileboost, "/GeneralStateTests/"+_folder, "/GeneralStateTestsFiller/"+_folder, dev::test::doStateTests);
if (fileboost.rfind("BCFiller.json") != std::string::npos)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@winsvega could you explain what happens here and why do you need to hardcode json file name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so not to convert 100 of tests into blockchain we decided to add a marker to such tests to be run as blockchain but being state tests.
I had an option in mind - to read file contents and then look for an option to run this test as blockchain.
then I realized that to do that I will have to parse whole json file and that would add to execution time.
there for we have this hardcoded thing.

the mere fact that state tests are being run as blockchain is a workaround. it was not designed to be run as blockchain tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@winsvega can you keep a list of these names in a file in the tests repository?

if (!Options::get().fillchain) //fill blockchain through state tests
bool isRunFromStateTests = true;
string suitename = boost::unit_test::framework::get<boost::unit_test::test_suite>(boost::unit_test::framework::current_test_case().p_parent_id).p_name;
if (suitename.find("StateTests") == string::npos)
Copy link
Member Author

@gumb0 gumb0 Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@winsvega Is there any way to pass the flag/enum to this function telling that we're in a special case of StateTests instead of querying suite name from boost? Why is doBlockchainTests called from StateTests suite anyway?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read above

@gumb0
Copy link
Member Author

gumb0 commented Jun 2, 2017

After discussion with @winsvega he agreed to try a different approach with fixing tests for this - rolling back current workaround changes in testeth code and just convert affected json files

Copy link
Member

@chfast chfast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need more time to review test changes.

@gumb0
Copy link
Member Author

gumb0 commented Jun 2, 2017

@chfast Don't spend your time on that yet, @winsvega plans now a different approach

@codecov-io
Copy link

codecov-io commented Jun 2, 2017

Codecov Report

Merging #4066 into develop will increase coverage by 0.3%.
The diff coverage is 99.18%.

@@            Coverage Diff             @@
##           develop    #4066     +/-   ##
==========================================
+ Coverage    65.41%   65.71%   +0.3%     
==========================================
  Files          301      303      +2     
  Lines        22882    22998    +116     
==========================================
+ Hits         14968    15114    +146     
+ Misses        7914     7884     -30
Impacted Files Coverage Δ
test/tools/jsontests/vm.h 100% <ø> (ø) ⬆️
libethcore/Common.h 90% <ø> (ø) ⬆️
libethereum/Block.h 60.86% <ø> (ø) ⬆️
test/tools/libtesteth/TestHelper.cpp 50.54% <ø> (-0.71%) ⬇️
libevm/ExtVMFace.h 78.57% <ø> (-0.31%) ⬇️
test/tools/libtesteth/TestHelper.h 50% <ø> (ø) ⬆️
libethereum/ExtVM.h 100% <ø> (ø) ⬆️
libevmcore/Instruction.cpp 17.5% <ø> (ø) ⬆️
libdevcrypto/Common.h 90% <ø> (ø) ⬆️
libevm/VM.cpp 91.78% <100%> (+1.49%) ⬆️
... and 29 more

{
if (_number < envInfo().number() && _number >= (std::max<u256>(256, envInfo().number()) - 256))
return sha3(toString(_number));
else
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be no else.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants