Revamp per block processing / state transition

This commit is contained in:
mratsim 2018-08-21 18:21:45 +02:00
parent 0b560f7307
commit 14bb9b6efd
4 changed files with 90 additions and 16 deletions

View File

@ -11,22 +11,21 @@ import intsets, eth_common, math
# ⚠ Spec is updated very often, implementation might quickly be outdated # ⚠ Spec is updated very often, implementation might quickly be outdated
type type
Keccak256_Digest* = Hash256 # TODO, previously Keccak256 fields used the "bytes" type Blake2_256_Digest* = Hash256 # TODO change to Blake2b-512[0 ..< 32] see https://github.com/status-im/nim-beacon-chain/issues/3
Blake2_256_Digest* = Hash256 # while Blake2 used hash32, but latest spec changed everything to hash32
Uint24* = range[0'u32 .. 0xFFFFFF'u32] # TODO: wrap-around Uint24* = range[0'u32 .. 0xFFFFFF'u32] # TODO: wrap-around
BeaconBlock* = object BeaconBlock* = object
parent_hash*: Keccak256_Digest # Hash of the parent block parent_hash*: Blake2_256_Digest # Hash of the parent block
slot_number*: int64 # Slot number (for the PoS mechanism) slot_number*: int64 # Slot number (for the PoS mechanism)
randao_reveal*: Keccak256_Digest # Randao commitment reveal randao_reveal*: Blake2_256_Digest # Randao commitment reveal
attestations*: seq[AttestationRecord] # Attestation votes attestations*: seq[AttestationRecord] # Attestation votes
pow_chain_ref*: Keccak256_Digest # Reference to main chain block pow_chain_ref*: Blake2_256_Digest # Reference to main chain block
active_state_root*: Blake2_256_Digest # Hash of the active state active_state_root*: Blake2_256_Digest # Hash of the active state
crystallized_state_root*: Blake2_256_Digest # Hash of the crystallized state crystallized_state_root*: Blake2_256_Digest # Hash of the crystallized state
ActiveState* = object ActiveState* = object
pending_attestations*: seq[AttestationRecord] # Attestations that have not yet been processed pending_attestations*: seq[AttestationRecord] # Attestations that have not yet been processed
recent_block_hashes*: seq[Keccak256_Digest] # Most recent 2 * CYCLE_LENGTH block hashes, older to newer recent_block_hashes*: seq[Blake2_256_Digest] # Most recent 2 * CYCLE_LENGTH block hashes, older to newer
CrystallizedState* = object CrystallizedState* = object
validators*: seq[ValidatorRecord] # List of active validators validators*: seq[ValidatorRecord] # List of active validators
@ -42,7 +41,7 @@ type
crosslinking_start_shard*: int16 # The next shard that cross-linking assignment will start from crosslinking_start_shard*: int16 # The next shard that cross-linking assignment will start from
crosslink_records*: seq[CrosslinkRecord] # Records about the most recent crosslink for each shard crosslink_records*: seq[CrosslinkRecord] # Records about the most recent crosslink for each shard
total_deposits*: Int256 # Total balance of deposits total_deposits*: Int256 # Total balance of deposits
dynasty_seed*: Keccak256_Digest # Used to select the committees for each shard dynasty_seed*: Blake2_256_Digest # Used to select the committees for each shard
dynasty_seed_last_reset*: int64 # Last epoch the crosslink seed was reset dynasty_seed_last_reset*: int64 # Last epoch the crosslink seed was reset
ShardAndCommittee* = object ShardAndCommittee* = object
@ -53,14 +52,14 @@ type
pubkey*: BLSPublicKey # The validator's public key pubkey*: BLSPublicKey # The validator's public key
withdrawal_shard*: int16 # What shard the validator's balance will be sent to after withdrawal withdrawal_shard*: int16 # What shard the validator's balance will be sent to after withdrawal
withdrawal_address*: EthAddress # And what address withdrawal_address*: EthAddress # And what address
randao_commitment*: Keccak256_Digest # The validator's current RANDAO beacon commitment randao_commitment*: Blake2_256_Digest # The validator's current RANDAO beacon commitment
balance*: int64 # Current balance balance*: int64 # Current balance
start_dynasty*: int64 # Dynasty where the validator is inducted start_dynasty*: int64 # Dynasty where the validator is inducted
end_dynasty*: int64 # Dynasty where the validator leaves end_dynasty*: int64 # Dynasty where the validator leaves
CrosslinkRecord* = object CrosslinkRecord* = object
dynasty: int64 # What dynasty the crosslink was submitted in dynasty: int64 # What dynasty the crosslink was submitted in
hash: Keccak256_Digest # The block hash hash: Blake2_256_Digest # The block hash
BLSPublicKey = object BLSPublicKey = object
# Stub for BLS signature # Stub for BLS signature
@ -69,10 +68,10 @@ type
AttestationRecord* = object AttestationRecord* = object
slot*: int64 # Slot number slot*: int64 # Slot number
shard_id*: int16 # Shard ID shard_id*: int16 # Shard ID
oblique_parent_hashes*: seq[Keccak256_Digest] oblique_parent_hashes*: seq[Blake2_256_Digest]
# List of block hashes that this signature is signing over that # List of block hashes that this signature is signing over that
# are NOT part of the current chain, in order of oldest to newest # are NOT part of the current chain, in order of oldest to newest
shard_block_hash*: Keccak256_Digest # Block hash in the in the shard that we are attesting to shard_block_hash*: Blake2_256_Digest # Block hash in the shard that we are attesting to
attester_bitfield*: IntSet # Who is participating attester_bitfield*: IntSet # Who is participating
aggregateSig*: seq[BLSPublicKey] # The actual signature aggregateSig*: seq[BLSPublicKey] # The actual signature
# Note: # Note:

View File

@ -21,7 +21,7 @@ func checkPartialCrosslinkRecords*(beaconBlock: BeaconBlock, crystalState: Cryst
if heightInEpoch < EpochLength - EndEpochGracePeriod: if heightInEpoch < EpochLength - EndEpochGracePeriod:
assert heightCutoffs[height_in_epoch] <= int(shardCutoffs[si] < heightCutoffs[heightInEpoch + 1]) # TODO Spec unclear assert heightCutoffs[height_in_epoch] <= int(shardCutoffs[si] < heightCutoffs[heightInEpoch + 1]) # TODO Spec unclear
else: else:
assert vote.shardId == 65535 and vote.shardBlockHash == Keccak256_Digest() assert vote.shardId == 65535 and vote.shardBlockHash == Blake2_256_Digest()
var shard_start, shard_end: int var shard_start, shard_end: int
if heightInEpoch < EpochLength - 8: if heightInEpoch < EpochLength - 8:

View File

@ -91,13 +91,23 @@ func get_indices_for_slot*(crystallized_state: CrystallizedState,
# Clarify with EF if light clients will need the beacon chain # Clarify with EF if light clients will need the beacon chain
func get_block_hash*(active_state: ActiveState, func get_block_hash*(active_state: ActiveState,
beacon_block: BeaconBlock, slot: int64): Keccak256_Digest = beacon_block: BeaconBlock, slot: int64): Blake2_256_Digest =
# TODO: Spec uses crystallized_state as arg and activ_state.slot_number
# which doesn't exist
let sback = beacon_block.slot_number - CYCLE_LENGTH * 2 let sback = beacon_block.slot_number - CYCLE_LENGTH * 2
assert sback <= slot assert sback <= slot
assert slot < sback + CYCLE_LENGTH * 2 assert slot < sback + CYCLE_LENGTH * 2
result = active_state.recent_block_hashes[int slot - sback] result = active_state.recent_block_hashes[int slot - sback]
func get_new_recent_block_hashes*(
old_block_hashes: seq[Blake2_256_Digest],
parent_slot, current_slot: int64,
parent_hash: Blake2_256_Digest
): seq[Blake2_256_Digest] =
# Should throw for `current_slot - CYCLE_LENGTH * 2 - 1` according to spec comment
let d = current_slot - parent_slot
result = old_block_hashes[d .. ^1]
for _ in 0 ..< min(d, old_block_hashes.len):
result.add parent_hash

View File

@ -0,0 +1,65 @@
# beacon_chain
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# Note: this is also inspired by https://github.com/ethereum/beacon_chain/blob/master/beacon_chain/state/state_transition.py
# The official spec at https://notes.ethereum.org/SCIg8AH5SA-O4C1G1LYZHQ# is not fully
# defining the state transitions.
#
# Note that the ethresearch impl is using "block_vote_cache" field, which is a dictionary mapping hashes
# to the following sub-dictionary:
# {
# 'voter_indices': set(),
# 'total_voter_deposits': 0
# }
# It should not be needed anymore with the new AttestationRecord type
{.warning: "The official spec at https://notes.ethereum.org/SCIg8AH5SA-O4C1G1LYZHQ# is not fully defining state transitions.".}
import ./datatypes, ./private/helpers
func process_block(active_state: ActiveState, crystallized_state: CrystallizedState, blck: BeaconBlock, slot: int64) =
# TODO: unfinished spec
for attestation in blck.attestations:
# Verify that slot < block.slot_number and slot >= max(block.slot_number - CYCLE_LENGTH, 0)
doAssert slot < blck.slot_number
doAssert slot >= max(blck.slot_number - CYCLE_LENGTH, 0)
# Compute parent_hashes = [get_block_hash(active_state, block, slot - CYCLE_LENGTH + i) for i in range(CYCLE_LENGTH - len(oblique_parent_hashes))] + oblique_parent_hashes
# TODO - don't allocate in tight loop
var parent_hashes = newSeq[Blake2_256_Digest](CYCLE_LENGTH - attestation.oblique_parent_hashes.len)
for idx, val in parent_hashes.mpairs:
val = get_block_hash(active_state, blck, slot - CYCLE_LENGTH + idx)
parent_hashes.add attestation.oblique_parent_hashes
# Let attestation_indices be get_indices_for_slot(crystallized_state, slot)[x], choosing x so that attestation_indices.shard_id equals the shard_id value provided to find the set of validators that is creating this attestation record.
let attestation_indices = block:
let shard_and_committees = get_indices_for_slot(crystallized_state, slot)
var
x = 1
record_creator = shard_and_committees[0]
while record_creator.shard_id != attestation.shard_id:
record_creator = shard_and_committees[x]
inc x
record_creator
# Verify that len(attester_bitfield) == ceil_div8(len(attestation_indices)), where ceil_div8 = (x + 7) // 8. Verify that bits len(attestation_indices).... and higher, if present (i.e. len(attestation_indices) is not a multiple of 8), are all zero
doAssert attestation.attester_bitfield.len == attestation_indices.committee.len
# Derive a group public key by adding the public keys of all of the attesters in attestation_indices for whom the corresponding bit in attester_bitfield (the ith bit is (attester_bitfield[i // 8] >> (7 - (i %8))) % 2) equals 1
# TODO
# Verify that aggregate_sig verifies using the group pubkey generated and hash((slot % CYCLE_LENGTH).to_bytes(8, 'big') + parent_hashes + shard_id + shard_block_hash) as the message.
# TODO
# Extend the list of AttestationRecord objects in the active_state, ordering the new additions in the same order as they came in the block.
# TODO
# Verify that the slot % len(get_indices_for_slot(crystallized_state, slot-1)[0])'th attester in get_indices_for_slot(crystallized_state, slot-1)[0]is part of at least one of the AttestationRecord objects; this attester can be considered to be the proposer of the block.
# TODO