If you've ever seen the ethereum blocktime chart, you've noticed that always it's between 10-19 and when ever it comes bigger than, network will set difficulty lower than before and again the block time is between 10 and 19. Is there any config in genesis or somewhere to set this time?
3 Answers
@RomanFrolov's answer is good; because my extension doesn't easily fit in a comment, I'm adding it as another answer.
There is no such thing as a minimum or maximum block time. There is a target time and a difficulty adjusting algorithm (as described by @RomanFrolov) that attempts to maintain the target time. Note sure where your 10-19s comes from because the normal range is actually a fair bit larger, unless you're looking at averages over longer periods of time. The mining of a block is a probabilistic event that can is described by a Poisson distribution. We can see from a Poisson table that the probability of having more than zero blocks after 10.5s (70% of the current target block time) is 1 - 0.4966
, or about 50%. The probability of no blocks for 21 seconds (140% of the current target block time) is 0.2466 -- or about 25%.
When you're looking at averages of larger sets, you can see, for example lambda=15 (225s) that, suddenly, the probability of seeing fewer than 11 blocks in that period (for an average block time of 20s) is about 10% (which is less probably than we found for single blocks at 21s). Similarly, the odds of finding more than 22 (10s average block time) is 1 - 0.9673
-- or about 3%. If you take the average block time for every 100 blocks, the range quickly converges to 15s and the 95% interval shrinks to what you observe.
TL;DR First, you set difficulty in genesis. Then, you calculate difficulty when you init your block from parent.
P.S: I highly suggest ctrl+F
difficulty
1. Set difficulty in genesis
def genesis(env, **kwargs):
assert isinstance(env, Env)
"""Build the genesis block."""
allowed_args = set(['start_alloc', 'prevhash', 'coinbase', 'difficulty', 'gas_limit',
'timestamp', 'extra_data', 'mixhash', 'nonce'])
assert set(kwargs.keys()).issubset(allowed_args)
start_alloc = kwargs.get('start_alloc', env.config['GENESIS_INITIAL_ALLOC'])
header = BlockHeader(
prevhash=kwargs.get('prevhash', env.config['GENESIS_PREVHASH']),
uncles_hash=utils.sha3(rlp.encode([])),
coinbase=kwargs.get('coinbase', env.config['GENESIS_COINBASE']),
state_root=trie.BLANK_ROOT,
tx_list_root=trie.BLANK_ROOT,
receipts_root=trie.BLANK_ROOT,
bloom=0,
difficulty=kwargs.get('difficulty', env.config['GENESIS_DIFFICULTY']),
number=0,
gas_limit=kwargs.get('gas_limit', env.config['GENESIS_GAS_LIMIT']),
gas_used=0,
timestamp=kwargs.get('timestamp', 0),
extra_data=kwargs.get('extra_data', env.config['GENESIS_EXTRA_DATA']),
mixhash=kwargs.get('mixhash', env.config['GENESIS_MIXHASH']),
nonce=kwargs.get('nonce', env.config['GENESIS_NONCE']),
)
block = Block(header, [], [], env=env)
for addr, data in start_alloc.items():
if len(addr) == 40:
addr = decode_hex(addr)
assert len(addr) == 20
if 'wei' in data:
block.set_balance(addr, utils.parse_int_or_hex(data['wei']))
if 'balance' in data:
block.set_balance(addr, utils.parse_int_or_hex(data['balance']))
if 'code' in data:
block.set_code(addr, utils.scanners['bin'](data['code']))
if 'nonce' in data:
block.set_nonce(addr, utils.parse_int_or_hex(data['nonce']))
if 'storage' in data:
for k, v in data['storage'].items():
block.set_storage_data(addr, utils.big_endian_to_int(decode_hex(k[2:])),
utils.big_endian_to_int(decode_hex(v[2:])))
block.commit_state()
block.state.db.commit()
# genesis block has predefined state root (so no additional finalization
# necessary)
return block
2. Calculate difficulty when you init new block
@classmethod
def init_from_parent(cls, parent, coinbase, nonce=b'', extra_data=b'',
timestamp=int(time.time()), uncles=[], env=None):
"""Create a new block based on a parent block.
The block will not include any transactions and will not be finalized.
"""
header = BlockHeader(prevhash=parent.hash,
uncles_hash=utils.sha3(rlp.encode(uncles)),
coinbase=coinbase,
state_root=parent.state_root,
tx_list_root=trie.BLANK_ROOT,
receipts_root=trie.BLANK_ROOT,
bloom=0,
difficulty=calc_difficulty(parent, timestamp),
mixhash='',
number=parent.number + 1,
gas_limit=calc_gaslimit(parent),
gas_used=0,
timestamp=timestamp,
extra_data=extra_data,
nonce=nonce)
block = Block(header, [], uncles, env=env or parent.env,
parent=parent, making=True)
block.ancestor_hashes = [parent.hash] + parent.ancestor_hashes
block.log_listeners = parent.log_listeners
return block
2.1 Difficulty adjustment algo
def calc_difficulty(parent, timestamp):
config = parent.config
offset = parent.difficulty // config['BLOCK_DIFF_FACTOR']
if parent.number >= (config['HOMESTEAD_FORK_BLKNUM'] - 1):
sign = max(1 - 2 * ((timestamp - parent.timestamp) //
config['HOMESTEAD_DIFF_ADJUSTMENT_CUTOFF']), -99)
else:
sign = 1 if timestamp - parent.timestamp <
config['DIFF_ADJUSTMENT_CUTOFF'] else -1
# If we enter a special mode where the genesis difficulty starts off
below
# the minimal difficulty, we allow low-difficulty blocks (this will never
# happen in the official protocol)
o = int(max(parent.difficulty + offset * sign, min(parent.difficulty,
config['MIN_DIFF'])))
period_count = (parent.number + 1) // config['EXPDIFF_PERIOD']
if period_count >= config['EXPDIFF_FREE_PERIODS']:
o = max(o + 2**(period_count - config['EXPDIFF_FREE_PERIODS']),
config['MIN_DIFF'])
return o
More human readable:
block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) + int(2**((block.number // 100000) - 2))
From EIP2 Homestead Hard-fork Changes.
Codes from this post are from the Python core library of the Ethereum project.
-
There can also be changes to the target difficulty after the genesis block. E.g., see github.com/ethereum/go-ethereum/commit/…– lungjCommented Dec 21, 2017 at 18:43
If you are running proof-of-authority, in the genesis block, you can change the config.clique.period value to be the minimum number of seconds for a new block. If a block is received with a timestamp less than the timestamp of the previous block plus this increment, it will be rejected as a bad block (invalid timestamp).