Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1219 from ethereum/sharding/propose-attestations
Browse files Browse the repository at this point in the history
Sharding/propose attestations
  • Loading branch information
mkalinin authored Nov 15, 2018
2 parents 476fbfd + c628918 commit 4fb98c4
Show file tree
Hide file tree
Showing 27 changed files with 916 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import org.ethereum.sharding.processing.db.IndexedBeaconStore;
import org.ethereum.sharding.processing.state.BeaconStateRepository;
import org.ethereum.sharding.processing.state.StateRepository;
import org.ethereum.sharding.validator.AttestationPool;
import org.ethereum.sharding.validator.AttestationPoolImpl;
import org.ethereum.sharding.validator.BeaconAttester;
import org.ethereum.sharding.validator.BeaconAttesterImpl;
import org.ethereum.sharding.validator.BeaconProposer;
Expand Down Expand Up @@ -170,8 +172,8 @@ public StateRepository beaconStateRepository() {

@Bean
public BeaconChain beaconChain() {
BeaconChain beaconChain = BeaconChainFactory.create(beaconDbFlusher(), beaconStore(),
beaconStateRepository(), validatorRepository(), blockStore.getBestBlock());
BeaconChain beaconChain = BeaconChainFactory.create(beaconDbFlusher(), beaconStore(), beaconStateRepository(),
validatorRepository(), blockStore.getBestBlock(), beaconAttester(), publisher(), sign());
shardingWorldManager.setBeaconChain(beaconChain);
return beaconChain;
}
Expand Down Expand Up @@ -222,14 +224,20 @@ public Publisher publisher() {
@Bean
public BeaconProposer beaconProposer() {
return new BeaconProposerImpl(randao(), beaconStateRepository(), beaconStore(),
BeaconChainFactory.stateTransition(validatorRepository()), validatorConfig());
BeaconChainFactory.stateTransition(validatorRepository(), beaconAttester(), publisher()),
validatorConfig(), attestationPool());
}

@Bean
public BeaconAttester beaconAttester() {
return new BeaconAttesterImpl(beaconStateRepository(), beaconStore(), validatorConfig(), sign());
}

@Bean
public AttestationPool attestationPool() {
return new AttestationPoolImpl(beaconStore(), sign(), publisher());
}

@Bean
public Randao randao() {
DbSource<byte[]> src = commonConfig.keyValueDataSource("randao");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import org.ethereum.manager.WorldManager;
import org.ethereum.sharding.manager.ShardingWorldManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -69,8 +68,8 @@ public class ShardingConfig {

@Bean
public ShardingWorldManager worldManager() {
return new ShardingWorldManager(systemProperties, repository,
ethereumListener, blockchain, blockStore, depositContractConfig(), dbFlushManager);
return new ShardingWorldManager(systemProperties, repository, ethereumListener, blockchain, blockStore,
depositContractConfig(), dbFlushManager);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,26 @@

import org.ethereum.core.Block;
import org.ethereum.db.DbFlushManager;
import org.ethereum.sharding.crypto.Sign;
import org.ethereum.sharding.processing.consensus.BeaconStateTransition;
import org.ethereum.sharding.processing.consensus.GenesisTransition;
import org.ethereum.sharding.processing.consensus.NumberAsScore;
import org.ethereum.sharding.processing.consensus.MaximumVotesAsScore;
import org.ethereum.sharding.processing.consensus.ScoreFunction;
import org.ethereum.sharding.processing.consensus.StateTransition;
import org.ethereum.sharding.processing.db.BeaconStore;
import org.ethereum.sharding.processing.state.BeaconState;
import org.ethereum.sharding.processing.state.StateRepository;
import org.ethereum.sharding.processing.validation.AttestationsValidator;
import org.ethereum.sharding.processing.validation.BeaconValidator;
import org.ethereum.sharding.processing.validation.BasicBeaconValidator;
import org.ethereum.sharding.processing.validation.MultiBeaconValidator;
import org.ethereum.sharding.processing.validation.StateValidator;
import org.ethereum.sharding.pubsub.Publisher;
import org.ethereum.sharding.registration.ValidatorRepository;
import org.ethereum.sharding.validator.BeaconAttester;

import java.util.ArrayList;
import java.util.List;

/**
* A factory that creates {@link BeaconChain} instance.
Expand All @@ -47,28 +56,35 @@ public class BeaconChainFactory {

public static BeaconChain create(DbFlushManager beaconDbFlusher, BeaconStore store,
StateRepository repository, StateTransition<BeaconState> genesisStateTransition,
StateTransition<BeaconState> stateTransitionFunction) {

BeaconValidator beaconValidator = new BeaconValidator(store);
StateTransition<BeaconState> stateTransitionFunction, Sign sign) {
List<BeaconValidator> beaconValidators = new ArrayList<BeaconValidator>() {{
add(new BasicBeaconValidator(store));
add(new AttestationsValidator(store, repository, sign));
}};
BeaconValidator multiValidator = new MultiBeaconValidator(beaconValidators);
StateValidator stateValidator = new StateValidator();
ScoreFunction scoreFunction = new NumberAsScore();
ScoreFunction scoreFunction = new MaximumVotesAsScore(store);

return new BeaconChainImpl(beaconDbFlusher, store, stateTransitionFunction,
repository, beaconValidator, stateValidator, scoreFunction, genesisStateTransition);
return new BeaconChainImpl(beaconDbFlusher, store, stateTransitionFunction, repository,
multiValidator, stateValidator, scoreFunction, genesisStateTransition);
}

public static BeaconChain create(DbFlushManager beaconDbFlusher, BeaconStore store, StateRepository repository,
ValidatorRepository validatorRepository, Block bestBlock) {
ValidatorRepository validatorRepository, Block bestBlock,
BeaconAttester beaconAttester, Publisher publisher, Sign sign) {

StateTransition<BeaconState> genesisStateTransition = new GenesisTransition(validatorRepository)
.withMainChainRef(bestBlock.getHash());

return create(beaconDbFlusher, store, repository, genesisStateTransition, stateTransition(validatorRepository));
return create(beaconDbFlusher, store, repository, genesisStateTransition,
stateTransition(validatorRepository, beaconAttester, publisher), sign);
}

public static StateTransition<BeaconState> stateTransition(ValidatorRepository validatorRepository) {
public static StateTransition<BeaconState> stateTransition(ValidatorRepository validatorRepository,
BeaconAttester beaconAttester,
Publisher publisher) {
if (stateTransition == null)
stateTransition = new BeaconStateTransition(validatorRepository);
stateTransition = new BeaconStateTransition(validatorRepository, beaconAttester, publisher);
return stateTransition;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,8 @@ public synchronized ProcessingResult insert(Beacon block) {
if ((vRes = stateValidator.validateAndLog(block, newState)) != ValidationResult.Success)
return ProcessingResult.fromValidation(vRes);

// calculate block and chain score
BigInteger blockScore = scoreFunction.apply(block, newState);
BigInteger chainScore = store.getChainScore(parent.getHash()).add(blockScore);
// calculate chain score
BigInteger chainScore = scoreFunction.apply(block, newState);

ScoredChainHead newHead = new ScoredChainHead(block, chainScore, newState);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public static ProcessingResult fromValidation(ValidationResult res) {
return NoParent;
case StateMismatch:
return ConsensusBreak;
case InvalidAttestations:
return Invalid;
default:
throw new RuntimeException("Can't convert " + res + " to processing result");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,23 @@
package org.ethereum.sharding.processing.consensus;

import org.ethereum.sharding.domain.Beacon;
import org.ethereum.sharding.processing.state.ActiveState;
import org.ethereum.sharding.processing.state.BeaconState;
import org.ethereum.sharding.processing.state.CrystallizedState;
import org.ethereum.sharding.processing.state.Dynasty;
import org.ethereum.sharding.processing.state.Finality;
import org.ethereum.sharding.pubsub.Publisher;
import org.ethereum.sharding.registration.ValidatorRepository;
import org.ethereum.sharding.validator.BeaconAttester;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

import static org.ethereum.sharding.processing.consensus.BeaconConstants.CYCLE_LENGTH;
import static org.ethereum.sharding.pubsub.Events.onBeaconAttestationIncluded;
import static org.ethereum.sharding.pubsub.Events.onStateRecalc;
import static org.ethereum.sharding.util.BeaconUtils.cycleStartSlot;

/**
Expand All @@ -39,10 +47,15 @@ public class BeaconStateTransition implements StateTransition<BeaconState> {

StateTransition<Dynasty> dynastyTransition;
StateTransition<Finality> finalityTransition;
BeaconAttester beaconAttester;
Publisher publisher;

public BeaconStateTransition(ValidatorRepository validatorRepository) {
public BeaconStateTransition(ValidatorRepository validatorRepository, BeaconAttester beaconAttester,
Publisher publisher) {
this.dynastyTransition = new DynastyTransition(new ValidatorSetTransition(validatorRepository));
this.finalityTransition = new FinalityTransition();
this.beaconAttester = beaconAttester;
this.publisher = publisher;
}

public BeaconStateTransition(StateTransition<Dynasty> dynastyTransition,
Expand All @@ -55,6 +68,10 @@ public BeaconStateTransition(StateTransition<Dynasty> dynastyTransition,
public BeaconState applyBlock(Beacon block, BeaconState to) {

CrystallizedState crystallized = to.getCrystallizedState();
ActiveState activeState = to.getActiveState();

block.getAttestations().forEach(at -> publisher.publish(onBeaconAttestationIncluded(at)));
activeState = activeState.addPendingAttestations(block.getAttestations());

if (block.getSlotNumber() - crystallized.getLastStateRecalc() >= CYCLE_LENGTH) {
logger.info("Calculate new crystallized state, slot: {}, prev slot: {}",
Expand All @@ -67,8 +84,14 @@ public BeaconState applyBlock(Beacon block, BeaconState to) {
.withDynasty(dynasty)
.withLastStateRecalc(cycleStartSlot(block))
.withFinality(finality);
if (publisher != null) {
publisher.publish(onStateRecalc(crystallized.getLastStateRecalc()));
}

// remove attestations older than last_state_recalc
activeState = activeState.removeAttestationsPriorTo(crystallized.getLastStateRecalc());
}

return new BeaconState(crystallized, to.getActiveState());
return new BeaconState(crystallized, activeState);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) [2016] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.sharding.processing.consensus;

import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.sharding.domain.Beacon;
import org.ethereum.sharding.processing.db.BeaconStore;
import org.ethereum.sharding.processing.state.ActiveState;
import org.ethereum.sharding.processing.state.AttestationRecord;
import org.ethereum.sharding.processing.state.BeaconState;
import org.ethereum.sharding.util.Bitfield;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Score uses IMD GHOST algorithm combined with finality
* For more info check following pages:
* - https://ethresear.ch/t/immediate-message-driven-ghost-as-ffg-fork-choice-rule/2561
* - https://ethresear.ch/t/beacon-chain-casper-ffg-rpj-mini-spec/2760
*/
public class MaximumVotesAsScore implements ScoreFunction {

BeaconStore store;

public MaximumVotesAsScore(BeaconStore store) {
this.store = store;
}

@Override
public BigInteger apply(Beacon block, BeaconState state) {
ActiveState activeState = state.getActiveState();

// Assumes that data from block is already in active state
List<AttestationRecord> pendingAttestations = activeState.getPendingAttestations();
AttestationRecord first = block.getAttestations().get(0);
boolean found = false;
for (int i = pendingAttestations.size() - 1; i >= 0; --i) {
if (pendingAttestations.get(i).equals(first)) {
found = true;
}
}
if (!found) {
throw new RuntimeException("State should already include scored block");
}

long lastJustified = state.getCrystallizedState().getFinality().getLastJustifiedSlot();

// Calculate per block votes
Map<ByteArrayWrapper, List<AttestationRecord>> perBlockAttestations = new HashMap<>();
pendingAttestations.forEach(at -> {
ByteArrayWrapper blockHash = new ByteArrayWrapper(at.getShardBlockHash());
if (perBlockAttestations.containsKey(blockHash)) {
perBlockAttestations.get(blockHash).add(at);
} else {
perBlockAttestations.put(blockHash, new ArrayList<AttestationRecord>(){{add(at);}});
}
});

int maxVotes = 0;
Beacon current = block;
while (current.getSlotNumber() != lastJustified) {
int currentVotes = 0;
List<AttestationRecord> blockAttestations = perBlockAttestations.get(new ByteArrayWrapper(current.getHash()));
if (blockAttestations != null && !blockAttestations.isEmpty()) {
currentVotes = Bitfield.orBitfield(
blockAttestations.stream()
.map(at -> at.getAttesterBitfield())
.collect(Collectors.toList())
).calcVotes();
}
if (currentVotes > maxVotes) {
maxVotes = currentVotes;
}
current = store.getByHash(current.getParentHash());
}

return BigInteger.valueOf(maxVotes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@
import java.math.BigInteger;

/**
* A function that takes beacon chain block and state that block produces and returns a score of that block.
* A function that takes beacon chain head block and state that block produces and
* returns a score of the chain that given block is a head of.
*
* <p>
* Basically, this function represents a fork choice rule.
*
* <p>
* When score is calculated it's added to the total score of the chain that block does belong to,
* and next it is compared to total score of canonical chain,
* if calculated total score is greater than canonical total score then beacon chain reorgs to the new block.
* When score is calculated it's stored as a score of the chain that block does belong to,
* and next it is compared to the score of canonical chain,
* if calculated score is greater than canonical score then beacon chain reorgs to the new block.
*
* Check {@link org.ethereum.sharding.processing.db.BeaconStore#reorgTo(Beacon)} for details.
*
Expand All @@ -44,7 +45,7 @@
public interface ScoreFunction {

/**
* Calculates block score, accepts block and state.
* Calculates chain score, accepts block and state.
* The state is a result of applying {@link StateTransition} function to the block.
*/
BigInteger apply(Beacon block, BeaconState state);
Expand Down
Loading

0 comments on commit 4fb98c4

Please sign in to comment.