Skip to content
This repository was archived by the owner on Oct 25, 2023. It is now read-only.
Draft
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
168 changes: 4 additions & 164 deletions Sources/BeaconChain/StateTransition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ extension StateTransition {
eth1data(state: &state, block: block)
proposerSlashings(state: &state, block: block)
attesterSlashings(state: &state, block: block)
attestations(state: &state, block: block)
deposits(state: &state, block: block)
voluntaryExits(state: &state, block: block)
Attestations.transition(state: &state, block: block)
Deposits.transition(state: &state, block: block)
VoluntaryExits.transition(state: &state, block: block)
Transfers.transition(state: &state, block: block)
}

static func blockSignature(state: inout BeaconState, block: BeaconBlock) {
Expand Down Expand Up @@ -137,167 +138,6 @@ extension StateTransition {
}
}

static func attestations(state: inout BeaconState, block: BeaconBlock) {
assert(block.body.attestations.count <= MAX_ATTESTATIONS)

for attestation in block.body.attestations {
assert(attestation.data.slot >= GENESIS_SLOT)
assert(attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot)
assert(state.slot < attestation.data.slot + SLOTS_PER_EPOCH)

let e = (attestation.data.slot + 1).toEpoch() >= BeaconChain.getCurrentEpoch(state: state) ? state.justifiedEpoch : state.previousJustifiedEpoch
assert(attestation.data.justifiedEpoch == e)
assert(attestation.data.justifiedBlockRoot == BeaconChain.getBlockRoot(state: state, slot: attestation.data.justifiedEpoch.startSlot()))

assert(
state.latestCrosslinks[Int(attestation.data.shard)] == attestation.data.latestCrosslink ||
state.latestCrosslinks[Int(attestation.data.shard)] == Crosslink(
epoch: attestation.data.slot.toEpoch(),
crosslinkDataRoot: attestation.data.crosslinkDataRoot
)
)

assert(attestation.custodyBitfield == Data(repeating: 0, count: 32))
assert(attestation.aggregationBitfield != Data(repeating: 0, count: 32))

let crosslinkCommittee = BeaconChain.crosslinkCommittees(state, at: attestation.data.slot).filter {
$0.1 == attestation.data.shard
}.first?.0

for i in 0..<crosslinkCommittee!.count {
if BeaconChain.getBitfieldBit(bitfield: attestation.aggregationBitfield, i: i) == 0b0 {
assert(BeaconChain.getBitfieldBit(bitfield: attestation.custodyBitfield, i: i) == 0b1)
}
}

let participants = BeaconChain.getAttestationParticipants(
state: state,
attestationData: attestation.data,
bitfield: attestation.aggregationBitfield
)

let custodyBit1Participants = BeaconChain.getAttestationParticipants(
state: state,
attestationData: attestation.data,
bitfield: attestation.custodyBitfield
)

let custodyBit0Participants = participants.filter {
!custodyBit1Participants.contains($0)
}

assert(
BLS.verify(
pubkeys: [
BLS.aggregate(pubkeys: custodyBit0Participants.map {
return state.validatorRegistry[Int($0)].pubkey
}),
BLS.aggregate(pubkeys: custodyBit1Participants.map {
return state.validatorRegistry[Int($0)].pubkey
})
],
messages: [
BeaconChain.hashTreeRoot(AttestationDataAndCustodyBit(data: attestation.data, custodyBit: false)),
BeaconChain.hashTreeRoot(AttestationDataAndCustodyBit(data: attestation.data, custodyBit: true))
],
signature: attestation.aggregateSignature,
domain: state.fork.domain(epoch: attestation.data.slot.toEpoch(), type: .attestation)
)
)

assert(attestation.data.crosslinkDataRoot == ZERO_HASH) // @todo remove in phase 1

state.latestAttestations.append(
PendingAttestation(
aggregationBitfield: attestation.aggregationBitfield, data: attestation.data,
custodyBitfield: attestation.custodyBitfield,
inclusionSlot: state.slot
)
)
}
}

static func deposits(state: inout BeaconState, block: BeaconBlock) {
assert(block.body.deposits.count <= MAX_DEPOSITS)

for deposit in block.body.deposits {
let serializedDepositData = Data(count: 64) // @todo when we have SSZ

assert(
verifyMerkleBranch(
leaf: BeaconChain.hash(serializedDepositData),
branch: deposit.branch,
depth: Int(DEPOSIT_CONTRACT_TREE_DEPTH),
index: Int(deposit.index),
root: state.latestEth1Data.depositRoot
)
)

BeaconChain.processDeposit(
state: &state,
deposit: deposit
)

state.depositIndex += 1
}
}

static func voluntaryExits(state: inout BeaconState, block: BeaconBlock) {
assert(block.body.voluntaryExits.count <= MAX_VOLUNTARY_EXITS)

for exit in block.body.voluntaryExits {
let validator = state.validatorRegistry[Int(exit.validatorIndex)]

let epoch = BeaconChain.getCurrentEpoch(state: state)
assert(validator.exitEpoch > epoch.delayedActivationExitEpoch())
assert(epoch >= exit.epoch)

assert(
BLS.verify(
pubkey: validator.pubkey,
message: BeaconChain.signedRoot(exit, field: "signature"),
signature: exit.signature,
domain: state.fork.domain(epoch: exit.epoch, type: .exit)
)
)

state.validatorRegistry[Int(exit.validatorIndex)].initiatedExit = true
}
}

static func transfers(state: inout BeaconState, block: BeaconBlock) {
assert(block.body.transfers.count <= MAX_TRANSFERS)

for transfer in block.body.transfers {
assert(state.validatorBalances[Int(transfer.from)] >= transfer.amount)
assert(state.validatorBalances[Int(transfer.from)] >= transfer.fee)
assert(
state.validatorBalances[Int(transfer.from)] == transfer.amount + transfer.fee
|| state.validatorBalances[Int(transfer.from)] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT
)

assert(state.slot == transfer.slot)
assert(
BeaconChain.getCurrentEpoch(state: state) >= state.validatorRegistry[Int(transfer.from)].withdrawableEpoch
|| state.validatorRegistry[Int(transfer.from)].activationEpoch == FAR_FUTURE_EPOCH
)
assert(state.validatorRegistry[Int(transfer.from)].withdrawalCredentials == BLS_WITHDRAWAL_PREFIX_BYTE + BeaconChain.hash(transfer.pubkey).suffix(from: 1))

assert(
BLS.verify(
pubkey: transfer.pubkey,
message: BeaconChain.signedRoot(transfer, field: "signature"),
signature: transfer.signature,
domain: state.fork.domain(epoch: transfer.slot.toEpoch(), type: .transfer)
)
)

state.validatorBalances[Int(transfer.from)] -= transfer.amount + transfer.fee
state.validatorBalances[Int(transfer.to)] += transfer.amount
state.validatorBalances[Int(BeaconChain.getBeaconProposerIndex(state: state, slot: state.slot))] += transfer.fee
}
}

static func verifyMerkleBranch(leaf: Bytes32, branch: [Bytes32], depth: Int, index: Int, root: Bytes32) -> Bool {
var value = leaf
for i in 0..<depth {
Expand Down
84 changes: 84 additions & 0 deletions Sources/BeaconChain/StateTransitions/Blocks/Attestations.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import Foundation

class Attestations: BlockTransitions {

static func transition(state: inout BeaconState, block: BeaconBlock) {
assert(block.body.attestations.count <= MAX_ATTESTATIONS)

for attestation in block.body.attestations {
assert(attestation.data.slot >= GENESIS_SLOT)
assert(attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot)
assert(state.slot < attestation.data.slot + SLOTS_PER_EPOCH)

let e = (attestation.data.slot + 1).toEpoch() >= BeaconChain.getCurrentEpoch(state: state) ? state.justifiedEpoch : state.previousJustifiedEpoch
assert(attestation.data.justifiedEpoch == e)
assert(attestation.data.justifiedBlockRoot == BeaconChain.getBlockRoot(state: state, slot: attestation.data.justifiedEpoch.startSlot()))

assert(
state.latestCrosslinks[Int(attestation.data.shard)] == attestation.data.latestCrosslink ||
state.latestCrosslinks[Int(attestation.data.shard)] == Crosslink(
epoch: attestation.data.slot.toEpoch(),
crosslinkDataRoot: attestation.data.crosslinkDataRoot
)
)

assert(attestation.custodyBitfield == Data(repeating: 0, count: 32))
assert(attestation.aggregationBitfield != Data(repeating: 0, count: 32))

let crosslinkCommittee = BeaconChain.crosslinkCommittees(state, at: attestation.data.slot).filter {
$0.1 == attestation.data.shard
}.first?.0

for i in 0..<crosslinkCommittee!.count {
if BeaconChain.getBitfieldBit(bitfield: attestation.aggregationBitfield, i: i) == 0b0 {
assert(BeaconChain.getBitfieldBit(bitfield: attestation.custodyBitfield, i: i) == 0b1)
}
}

let participants = BeaconChain.getAttestationParticipants(
state: state,
attestationData: attestation.data,
bitfield: attestation.aggregationBitfield
)

let custodyBit1Participants = BeaconChain.getAttestationParticipants(
state: state,
attestationData: attestation.data,
bitfield: attestation.custodyBitfield
)

let custodyBit0Participants = participants.filter {
!custodyBit1Participants.contains($0)
}

assert(
BLS.verify(
pubkeys: [
BLS.aggregate(pubkeys: custodyBit0Participants.map {
return state.validatorRegistry[Int($0)].pubkey
}),
BLS.aggregate(pubkeys: custodyBit1Participants.map {
return state.validatorRegistry[Int($0)].pubkey
})
],
messages: [
BeaconChain.hashTreeRoot(AttestationDataAndCustodyBit(data: attestation.data, custodyBit: false)),
BeaconChain.hashTreeRoot(AttestationDataAndCustodyBit(data: attestation.data, custodyBit: true))
],
signature: attestation.aggregateSignature,
domain: state.fork.domain(epoch: attestation.data.slot.toEpoch(), type: .attestation)
)
)

assert(attestation.data.crosslinkDataRoot == ZERO_HASH) // @todo remove in phase 1

state.latestAttestations.append(
PendingAttestation(
aggregationBitfield: attestation.aggregationBitfield, data: attestation.data,
custodyBitfield: attestation.custodyBitfield,
inclusionSlot: state.slot
)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Foundation

protocol BlockTransitions {

static func transition(state: inout BeaconState, block: BeaconBlock);
}
29 changes: 29 additions & 0 deletions Sources/BeaconChain/StateTransitions/Blocks/Deposits.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Foundation

class Deposits: BlockTransitions {

static func transition(state: inout BeaconState, block: BeaconBlock) {
assert(block.body.deposits.count <= MAX_DEPOSITS)

for deposit in block.body.deposits {
let serializedDepositData = Data(count: 64) // @todo when we have SSZ

assert(
StateTransition.verifyMerkleBranch(
leaf: BeaconChain.hash(serializedDepositData),
branch: deposit.branch,
depth: Int(DEPOSIT_CONTRACT_TREE_DEPTH),
index: Int(deposit.index),
root: state.latestEth1Data.depositRoot
)
)

BeaconChain.processDeposit(
state: &state,
deposit: deposit
)

state.depositIndex += 1
}
}
}
37 changes: 37 additions & 0 deletions Sources/BeaconChain/StateTransitions/Blocks/Transfers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Foundation

class Transfers: BlockTransitions {

static func transition(state: inout BeaconState, block: BeaconBlock) {
assert(block.body.transfers.count <= MAX_TRANSFERS)

for transfer in block.body.transfers {
assert(state.validatorBalances[Int(transfer.from)] >= transfer.amount)
assert(state.validatorBalances[Int(transfer.from)] >= transfer.fee)
assert(
state.validatorBalances[Int(transfer.from)] == transfer.amount + transfer.fee
|| state.validatorBalances[Int(transfer.from)] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT
)

assert(state.slot == transfer.slot)
assert(
BeaconChain.getCurrentEpoch(state: state) >= state.validatorRegistry[Int(transfer.from)].withdrawableEpoch
|| state.validatorRegistry[Int(transfer.from)].activationEpoch == FAR_FUTURE_EPOCH
)
assert(state.validatorRegistry[Int(transfer.from)].withdrawalCredentials == BLS_WITHDRAWAL_PREFIX_BYTE + BeaconChain.hash(transfer.pubkey).suffix(from: 1))

assert(
BLS.verify(
pubkey: transfer.pubkey,
message: BeaconChain.signedRoot(transfer, field: "signature"),
signature: transfer.signature,
domain: state.fork.domain(epoch: transfer.slot.toEpoch(), type: .transfer)
)
)

state.validatorBalances[Int(transfer.from)] -= transfer.amount + transfer.fee
state.validatorBalances[Int(transfer.to)] += transfer.amount
state.validatorBalances[Int(BeaconChain.getBeaconProposerIndex(state: state, slot: state.slot))] += transfer.fee
}
}
}
27 changes: 27 additions & 0 deletions Sources/BeaconChain/StateTransitions/Blocks/VoluntaryExits.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation

class VoluntaryExits: BlockTransitions {

static func transition(state: inout BeaconState, block: BeaconBlock) {
assert(block.body.voluntaryExits.count <= MAX_VOLUNTARY_EXITS)

for exit in block.body.voluntaryExits {
let validator = state.validatorRegistry[Int(exit.validatorIndex)]

let epoch = BeaconChain.getCurrentEpoch(state: state)
assert(validator.exitEpoch > epoch.delayedActivationExitEpoch())
assert(epoch >= exit.epoch)

assert(
BLS.verify(
pubkey: validator.pubkey,
message: BeaconChain.signedRoot(exit, field: "signature"),
signature: exit.signature,
domain: state.fork.domain(epoch: exit.epoch, type: .exit)
)
)

state.validatorRegistry[Int(exit.validatorIndex)].initiatedExit = true
}
}
}