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

Implement minimal block proposer for beacon chain #1168

Merged
merged 7 commits into from
Aug 30, 2018
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.ethereum.config.CommonConfig;
import org.ethereum.config.SystemProperties;
import org.ethereum.core.EventDispatchThread;
import org.ethereum.crypto.HashUtil;
import org.ethereum.datasource.AbstractCachedSource;
import org.ethereum.datasource.AsyncWriteCache;
Expand All @@ -34,13 +35,18 @@
import org.ethereum.db.TransactionStore;
import org.ethereum.facade.Ethereum;
import org.ethereum.manager.WorldManager;
import org.ethereum.sharding.pubsub.Publisher;
import org.ethereum.sharding.manager.ShardingWorldManager;
import org.ethereum.sharding.processing.BeaconChain;
import org.ethereum.sharding.processing.BeaconChainFactory;
import org.ethereum.sharding.processing.db.BeaconStore;
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.proposer.BeaconProposer;
import org.ethereum.sharding.proposer.BeaconProposerImpl;
import org.ethereum.sharding.proposer.ProposerService;
import org.ethereum.sharding.proposer.ProposerServiceImpl;
import org.ethereum.sharding.service.ValidatorRepositoryImpl;
import org.ethereum.sharding.service.ValidatorService;
import org.ethereum.sharding.crypto.DepositAuthority;
Expand All @@ -50,7 +56,6 @@
import org.ethereum.sharding.service.ValidatorRepository;
import org.ethereum.sharding.service.ValidatorServiceImpl;
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.Condition;
import org.springframework.context.annotation.ConditionContext;
Expand Down Expand Up @@ -98,6 +103,9 @@ public class BeaconConfig {
@Autowired
SystemProperties systemProperties;

@Autowired
EventDispatchThread eventDispatchThread;

@Bean
public ValidatorConfig validatorConfig() {
return ValidatorConfig.fromFile();
Expand All @@ -108,14 +116,25 @@ public ValidatorService validatorService() {
ValidatorService validatorService;
if (validatorConfig().isEnabled()) {
validatorService = new ValidatorServiceImpl(ethereum, validatorConfig(),
depositContract(), depositAuthority(), randao());
depositContract(), depositAuthority(), randao(), publisher());
} else {
validatorService = new ValidatorService() {};
}
shardingWorldManager.setValidatorService(validatorService);
return validatorService;
}

@Bean
public ProposerService proposerService() {
if (validatorConfig().isEnabled()) {
ProposerService proposerService = new ProposerServiceImpl(beaconProposer(), beaconChain(), publisher());
shardingWorldManager.setProposerService(proposerService);
return proposerService;
} else {
return new ProposerService() {};
}
}

@Bean
public ValidatorRepository validatorRepository() {
return new ValidatorRepositoryImpl(blockStore, txStore, depositContract());
Expand Down Expand Up @@ -184,6 +203,29 @@ public DbSource<byte[]> beaconChainDB() {
return commonConfig.keyValueDataSource("beaconchain", settings);
}

@Bean
public Publisher publisher() {
Publisher publisher = new Publisher(eventDispatchThread);
shardingWorldManager.setPublisher(publisher);
return publisher;
}

@Bean
public BeaconProposer beaconProposer() {
return new BeaconProposerImpl(ethereum, publisher(), randao(), beaconStateRepository(),
BeaconChainFactory.stateTransition());
}

@Bean
public Randao randao() {
DbSource<byte[]> src = commonConfig.keyValueDataSource("randao");
WriteCache.BytesKey<byte[]> cache = new WriteCache.BytesKey<>(
new BatchSourceWriter<>(src), WriteCache.CacheType.SIMPLE);
cache.setFlushSource(true);

return new Randao(cache);
}

private DbFlushManager beaconDbFlusher;
public DbFlushManager beaconDbFlusher() {
if (beaconDbFlusher != null)
Expand All @@ -198,15 +240,6 @@ public DepositAuthority depositAuthority() {
return new UnsecuredDepositAuthority(validatorConfig());
}

public Randao randao() {
DbSource<byte[]> src = commonConfig.keyValueDataSource("randao");
WriteCache.BytesKey<byte[]> cache = new WriteCache.BytesKey<>(
new BatchSourceWriter<>(src), WriteCache.CacheType.SIMPLE);
cache.setFlushSource(true);

return new Randao(cache);
}

public static class Enabled implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public long getSlotNumber() {
}

public boolean isParentOf(Beacon other) {
return FastByteComparisons.equal(this.getParentHash(), other.getHash());
return FastByteComparisons.equal(this.getHash(), other.getParentHash());
Copy link
Collaborator

Choose a reason for hiding this comment

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

lol, tests are a great thing!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed! 🤣

}

public void setStateHash(byte[] stateHash) {
Expand All @@ -112,7 +112,8 @@ public boolean equals(Object other) {
@Override
public String toString() {
return "#" + getSlotNumber() + " (" + Hex.toHexString(getHash()).substring(0,6) + " <~ "
+ Hex.toHexString(getParentHash()).substring(0,6) + ")";
+ Hex.toHexString(getParentHash()).substring(0,6) + "; mainChainRef: " +
Hex.toHexString(mainChainRef).substring(0,6) + ")";
}

public boolean isGenesis() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public class BeaconGenesis extends Beacon {

private static BeaconGenesis instance;

// stub for proposer, 08/28/2018 @ 11:13am (UTC)
private long timestamp = 1535454832000L;

private BeaconGenesis() {
super(EMPTY, EMPTY, EMPTY, EMPTY, SLOT);
setStateHash(getState().getHash());
Expand All @@ -53,4 +56,13 @@ public BeaconState getState() {
public BigInteger getScore() {
return BigInteger.ZERO;
}

public long getTimestamp() {
return timestamp;
}

@Override
public String toString() {
return super.toString() + ", timestamp: " + timestamp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
import org.ethereum.manager.WorldManager;
import org.ethereum.sharding.config.DepositContractConfig;
import org.ethereum.sharding.processing.BeaconChain;
import org.ethereum.sharding.proposer.ProposerService;
import org.ethereum.sharding.pubsub.Publisher;
import org.ethereum.sharding.pubsub.ValidatorStateUpdated;
import org.ethereum.sharding.service.ValidatorService;
import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl;
import org.slf4j.Logger;
Expand Down Expand Up @@ -65,6 +68,7 @@ public class ShardingWorldManager extends WorldManager {
DepositContractConfig contractConfig;
DbFlushManager dbFlushManager;
DbFlushManager beaconDbFlusher;
Publisher publisher;

private CompletableFuture<Void> contractInit = new CompletableFuture<>();

Expand Down Expand Up @@ -127,10 +131,22 @@ public void setValidatorService(final ValidatorService validatorService) {
contractInit.thenRunAsync(validatorService::init);
}

public void setProposerService(final ProposerService proposerService) {
publisher.subscribe(ValidatorStateUpdated.class, data -> {
if (data.getNewState() == ValidatorService.State.Enlisted) {
proposerService.init();
}
});
}

public void setBeaconChain(final BeaconChain beaconChain) {
contractInit.thenRunAsync(beaconChain::init);
}

public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}

public void setBeaconDbFlusher(final DbFlushManager beaconDbFlusher) {
this.beaconDbFlusher = beaconDbFlusher;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,21 @@
*/
public class BeaconChainFactory {

private static StateTransition stateTransition;

public static BeaconChain create(DbFlushManager beaconDbFlusher, BeaconStore store, StateRepository repository) {

StateTransition transitionFunction = new NoTransition();
BeaconValidator beaconValidator = new BeaconValidator(store);
StateValidator stateValidator = new StateValidator();
ScoreFunction scoreFunction = new NumberAsScore();

return new BeaconChainImpl(beaconDbFlusher, store, transitionFunction,
return new BeaconChainImpl(beaconDbFlusher, store, stateTransition(),
repository, beaconValidator, stateValidator, scoreFunction);
}

public static StateTransition stateTransition() {
if (stateTransition == null)
stateTransition = new NoTransition();
return stateTransition;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
package org.ethereum.sharding.processing;

import org.ethereum.db.DbFlushManager;
import org.ethereum.sharding.pubsub.Event;
import org.ethereum.sharding.pubsub.Publisher;
import org.ethereum.sharding.processing.consensus.ScoreFunction;
import org.ethereum.sharding.processing.consensus.StateTransition;
import org.ethereum.sharding.processing.state.BeaconState;
Expand All @@ -30,9 +32,13 @@
import org.ethereum.sharding.processing.validation.ValidationResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigInteger;

import static org.ethereum.sharding.pubsub.Events.onBeaconBlock;
import static org.ethereum.sharding.pubsub.Events.onBeaconChainLoaded;

/**
* This is default and likely the only implementation of {@link BeaconChain}
*
Expand All @@ -52,6 +58,8 @@ public class BeaconChainImpl implements BeaconChain {
StateRepository repository;
ScoreFunction scoreFunction;

Publisher publisher;

private ChainHead canonicalHead;

public BeaconChainImpl(DbFlushManager beaconDbFlusher, BeaconStore store,
Expand All @@ -69,11 +77,17 @@ public BeaconChainImpl(DbFlushManager beaconDbFlusher, BeaconStore store,

@Override
public void init() {
logger.info("Load chain with genesis: {}", BeaconGenesis.instance());

if (store.getCanonicalHead() == null)
insertGenesis();

canonicalHead = new ChainHead(store.getCanonicalHead(), store.getCanonicalHeadScore(),
repository.get(store.getCanonicalHead().getStateHash()));

publish(onBeaconChainLoaded(canonicalHead.block));

logger.info("Chain loaded with head: {}", canonicalHead.block);
}

private void insertGenesis() {
Expand Down Expand Up @@ -131,6 +145,10 @@ public synchronized ProcessingResult insert(Beacon block) {
});

ProcessingResult res = canonicalHead.equals(newHead) ? ProcessingResult.Best : ProcessingResult.NotBest;

// publish beacon block event
publish(onBeaconBlock(block, res == ProcessingResult.Best));

logger.info("Process block {}, status: {}", block.toString(), res);

return res;
Expand Down Expand Up @@ -177,4 +195,14 @@ public boolean equals(Object other) {
return this.block.equals(((ChainHead) other).block);
}
}

void publish(Event e) {
if (publisher != null)
publisher.publish(e);
}

@Autowired
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.proposer;

import org.ethereum.sharding.domain.Beacon;
import org.ethereum.sharding.domain.BeaconGenesis;

/**
* Beacon chain block proposer.
*
* <p>
* Is responsible only for creating new block.
* Task scheduling is provided by {@link ProposerService}
*
* @author Mikhail Kalinin
* @since 28.08.2018
*/
public interface BeaconProposer {

/**
* Slot duration for the beacon chain
*/
long SLOT_DURATION = 8 * 1000; // 8 seconds

/**
* Creates new block on top of the beacon chain head.
*
* @param slotNumber number of the slot that block does belong to.
* @return newly created block
*/
Beacon createNewBlock(long slotNumber);

/**
* Calculates minimal timestamp for specified slot number.
*
* @param slotNumber slot number
* @return timestamp in milliseconds
*/
long getTimestamp(long slotNumber);

/**
* Calculates a number of slot that given moment of time does fit to.
* Uses {@link BeaconGenesis#timestamp} and {@link #SLOT_DURATION}
*
* @param timestamp timestamp in milliseconds
* @return slot number
*/
long getSlotNumber(long timestamp);
}
Loading