Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create blockhash_refactoring.md #210

Merged
merged 13 commits into from
Apr 20, 2018
84 changes: 84 additions & 0 deletions EIPS/blockhash_refactoring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
### Preamble

EIP: <to be assigned>
Title: Blockhash refactoring
Author: Vitalik Buterin
Type: Standard Track
Category: Core
Status: Draft
Created: 2017-02-10

### Summary

Stores blockhashes in the state, reducing the protocol complexity and the need for client implementation complexity in order to process the BLOCKHASH opcode. Also extends the range of how far back blockhash checking can go, with the side effect of creating direct links between blocks with very distant block numbers, facilitating much more efficient initial light client syncing.

### Parameters

* `METROPOLIS_FORK_BLKNUM`: TBD
Copy link
Member

Choose a reason for hiding this comment

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

Now, this should say BYZANTIUM_FORK_BLKNUM.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nope, should say CONSTANTINOPLE_FORK_BLNUM

Copy link
Member

Choose a reason for hiding this comment

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

Right, it has changed.

* `SUPER_USER`: 2**160 - 2
* `BLOCKHASH_CONTRACT_ADDR`: 0xf0 (ie. 240)
Copy link
Member

Choose a reason for hiding this comment

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

Can this be a continuous address or is there a special reason for using 0xf0?

Copy link
Member

Choose a reason for hiding this comment

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

@vbuterin @chfast this should be 0x9 to avoid any holes.

Copy link
Member

Choose a reason for hiding this comment

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

I asked about this before. The answer was this is not regular precompiled contract, because you deploy real bytecode. I think it make sense, because from EVM you don't need additional check "if precompiled" to use this contract.

However, I'd use address of 0x100 to leave the lower byte for precompiled contracts only.

* `BLOCKHASH_CONTRACT_CODE`: see below

### Specification

If `block.number == METROPOLIS_FORK_BLKNUM`, then when processing the block, before processing any transactions set the code of BLOCKHASH_CONTRACT_ADDR to BLOCKHASH_CONTRACT_CODE.

If `block.number >= METROPOLIS_FORK_BLKNUM`, then when processing a block, before processing any transactions execute a call with the parameters:
Copy link
Member

Choose a reason for hiding this comment

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

Should the gas consumption here be counted in the gas usage of the block?

Copy link
Member

Choose a reason for hiding this comment

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

Should this call increment the nonce? I feel ambiguous ethereum/aleth#4066 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

In the second last allcoredev call, I heard this should not increment the nonce.


* `SENDER`: SUPER_USER
* `GAS`: 1000000
Copy link
Member

@pirapira pirapira May 8, 2017

Choose a reason for hiding this comment

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

What is the gas price and the nonce of this call transaction?

* `TO`: BLOCKHASH_CONTRACT_ADDR
* `VALUE`: 0
* `DATA`: <32 bytes corresponding to the block's prevhash>

If `block.number >= METROPOLIS_FORK_BLKNUM + 256`, then the BLOCKHASH opcode instead returns the result of executing a call with the parameters:
Copy link
Member

Choose a reason for hiding this comment

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

Is the call-stack depth limit of 1024 still relevant?

Copy link
Member

Choose a reason for hiding this comment

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

This question was answered in #210 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

If it was decided that BLOCKHASH should not work more than 256 blocks back #210 (comment), this line needs to change. BLOCKHASH opcode needs to perform a range check before calling BLOCKHASH_CONTRACT_ADDR.


* `SENDER`: <account from which the opcode was called>
Copy link
Member

Choose a reason for hiding this comment

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

Github Markdown renderer doesn't show this text in angle brackets for some reason

* `GAS`: 1000000
Copy link
Member

@pirapira pirapira May 8, 2017

Choose a reason for hiding this comment

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

What happens when 1000000 is more than "all-but-64th" of the remaining gas? A usual CALL instruction would lower the number automatically.

* `TO`: BLOCKHASH_CONTRACT_ADDR
* `VALUE`: 0
* `DATA`: 32 byte zero-byte-leftpadded integer representing the stack argument with which the opcode was called
Copy link
Member

@pirapira pirapira May 8, 2017

Choose a reason for hiding this comment

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

What is the gas price and the nonce of this call? I guess this is just a message call and not a separate transaction.


Also, the gas cost is increased from 20 to 800 to reflect the higher costs of processing the algorithm in the contract code.
Copy link
Member

@axic axic May 4, 2017

Choose a reason for hiding this comment

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

The serpent code below seems to take more than 800 gas for reading.

Copy link
Member

Choose a reason for hiding this comment

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

I think it would make sense setting the cost to the upper bound of what the read actually costs.

If the current proposed rules make that upper bound very large and therefore pose an unrealistically high cost for the opcode, then I would suggest:

  • have a contract which stores less and has a lower higher bound or
  • limiting the parameter to 256 (as currently) for the opcode

Copy link
Member

Choose a reason for hiding this comment

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

Does the gas cost increase from METROPOLIS_FORK_BLKNUM or METROPOLIS_FORK_BLKNUM + 256?

Copy link
Member

Choose a reason for hiding this comment

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

I would prefer the increase at METROPOLIS_FORK_BLKNUM , as all previous cost changes happened at the fork block


### BLOCKHASH_CONTRACT_CODE

BLOCKHASH_CONTRACT_CODE is set to:

```
0x73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b
Copy link
Member

Choose a reason for hiding this comment

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

For me this is no-go, because we can't audit the code here. Can we have it in assembly code? If you can't do it ask someone from solidity team to do it. They have nice assembly language that maps 1 to 1 to EVM bytecode.

Copy link
Member

@axic axic May 5, 2017

Choose a reason for hiding this comment

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

Here is a sample (probably broken) implementation, though not sure it is easier to read: https://gist.github.com/axic/c97d104ac63a97049a5d2559ba43460f

Copy link
Member

@pirapira pirapira May 8, 2017

Choose a reason for hiding this comment

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

I need the bytecode. EXTCODECOPY would copy from the bytecode. It's also good if some code is attached in some other languages, with a link to a specific version of a compiler/assembler.

Copy link
Member

Choose a reason for hiding this comment

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

This code is different from the runtime code pasted below.

```

The Serpent source code is:

```python
# Setting the block hash
if msg.sender == 2**160 - 2:
with prev_block_number = block.number - 1:
# Use storage fields 0..255 to store the last 256 hashes
~sstore(prev_block_number % 256, ~calldataload(0))
# Use storage fields 256..511 to store the hashes of the last 256
# blocks with block.number % 256 == 0
if not (prev_block_number % 256):
Copy link
Member

Choose a reason for hiding this comment

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

Last inserted value in this case can never be accessed (is duplication of the first case).

~sstore(256 + (prev_block_number / 256) % 256, ~calldataload(0))
# Use storage fields 512..767 to store the hashes of the last 256
# blocks with block.number % 65536 == 0
if not (prev_block_number % 65536):
Copy link
Member

Choose a reason for hiding this comment

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

Last inserted value in this case can never be accessed.

~sstore(512 + (prev_block_number / 65536) % 256, ~calldataload(0))
# Getting the block hash
else:
if ~calldataload(0) >= block.number:
Copy link
Member

@chfast chfast Jun 9, 2017

Choose a reason for hiding this comment

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

Because serpent uses only signed integers, if the ~calldataload(0) is a negative number, this check will not work.

The test case showing the bug: 47f6160

return(0)
Copy link
Member

Choose a reason for hiding this comment

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

When asking for non-existing block hash, the contract should signal an error and not return anything. REVERT?

Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't we keep current BLOCKHASH behavior (return 0 in this case) not to break existing contracts?

Copy link
Member

Choose a reason for hiding this comment

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

We can wrap this case up. But for new contracts calling the contract directly it would be much useful if the call returned false instead of null output.

Copy link
Member

Choose a reason for hiding this comment

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

Anyway, BLOCKHASH instruction needs some kind of wrap-around. BLOCKHASH needs to return 0 when it's asked about blocks more than 256-block older (there was a decision about that).

elif block.number - ~calldataload(0) <= 256:
return(~sload(~calldataload(0) % 256))
elif (not (~calldataload(0) % 256) and block.number - ~calldataload(0) <= 65536):
return(~sload(256 + (~calldataload(0) / 256) % 256))
elif (not (~calldataload(0) % 65536) and block.number - ~calldataload(0) <= 16777216):
return(~sload(512 + (~calldataload(0) / 65536) % 256))
else:
return(0)
```

### Rationale

This removes the need for implementaitons to have an explicit way to look into historical block hashes, simplifying the protocol definition and removing a large component of the "implied state" (information that is technically state but is not part of the state tree) and thereby making the protocol more "pure". Additionally, it allows blocks to directly point to blocks far behind them, which enables extremely efficient and secure light client protocols.