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

[WIP] State transition #1177

Merged
merged 23 commits into from
Sep 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a504fc7
Decouple Randao.reveal(byte[] key) from Randao.revealNext()
mkalinin Sep 3, 2018
9daf87b
Add Utils.parseHex method, copied from tests#Utils.parseData
mkalinin Sep 3, 2018
15c2006
Add a json representation of beacon genesis
mkalinin Sep 3, 2018
670c2eb
Set init sequence: validator.init -> beaconchain.synced -> proposer.init
mkalinin Sep 4, 2018
c81f360
Add ValidatorRepository.query(byte[] toBlock), it starts lookup from …
mkalinin Sep 4, 2018
7240145
Introduce validator set built on top of Trie
mkalinin Sep 5, 2018
7220a12
Boostrap initial validator set defined in genesis
mkalinin Sep 5, 2018
d4046a2
Check validator set size in tests
mkalinin Sep 6, 2018
84aa8b5
Introduce safe distance for mainChainRef
mkalinin Sep 6, 2018
a553261
Add ByteUtil.toInt24() method
mkalinin Sep 12, 2018
3eb067d
Add ValidatorSet.EMPTY_HASH constant
mkalinin Sep 12, 2018
f5b88bb
Replace hashes in the state with objects they are representing
mkalinin Sep 12, 2018
e086bb0
Add StateRepository.getEmpty() method
mkalinin Sep 12, 2018
1ed1c22
Update DepositContract tx with correct RLP encoding of integers
mkalinin Sep 12, 2018
63e0871
Add crystallized state structure
mkalinin Sep 12, 2018
cb546a0
Design beacon chain state transition function
mkalinin Sep 12, 2018
c369f56
Implement basic dynasty transition function
mkalinin Sep 12, 2018
da5247b
Replace NoTransition with BeaconStateTransition in BeaconchainImpl
mkalinin Sep 12, 2018
6253e26
Add empty line to json files
mkalinin Sep 12, 2018
471e4ea
Fix increment of crystallizedState.lastStateRecalc
mkalinin Sep 14, 2018
3c1f7f3
Cover state transition with tests
mkalinin Sep 14, 2018
5b1a81a
Add odd-length data to beacon-genesis-parse-test.json
mkalinin Sep 14, 2018
7c4449c
Check for null in equals() in Beacon and BeaconState
mkalinin Sep 14, 2018
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
import org.ethereum.db.TransactionStore;
import org.ethereum.facade.Ethereum;
import org.ethereum.manager.WorldManager;
import org.ethereum.sharding.processing.db.TrieValidatorSet;
import org.ethereum.sharding.processing.db.ValidatorSet;
import org.ethereum.sharding.pubsub.Publisher;
import org.ethereum.sharding.manager.ShardingWorldManager;
import org.ethereum.sharding.processing.BeaconChain;
Expand Down Expand Up @@ -156,13 +158,15 @@ public BeaconStore beaconStore() {
@Bean
public StateRepository beaconStateRepository() {
Source<byte[], byte[]> src = cachedBeaconChainSource("beacon_state");
return new BeaconStateRepository(src);
Source<byte[], byte[]> validatorSrc = cachedBeaconChainSource("validator_set");
Source<byte[], byte[]> crystallizedSrc = cachedBeaconChainSource("crystallized_state");
return new BeaconStateRepository(src, crystallizedSrc, validatorSrc);
}

@Bean
public BeaconChain beaconChain() {
BeaconChain beaconChain = BeaconChainFactory.create(
beaconDbFlusher(), beaconStore(), beaconStateRepository());
BeaconChain beaconChain = BeaconChainFactory.create(beaconDbFlusher(), beaconStore(),
beaconStateRepository(), validatorRepository(), blockStore.getBestBlock());
shardingWorldManager.setBeaconChain(beaconChain);
return beaconChain;
}
Expand Down Expand Up @@ -213,7 +217,7 @@ public Publisher publisher() {
@Bean
public BeaconProposer beaconProposer() {
return new BeaconProposerImpl(ethereum, publisher(), randao(), beaconStateRepository(),
BeaconChainFactory.stateTransition());
BeaconChainFactory.stateTransition(validatorRepository()));
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.ethereum.listener.RecommendedGasPriceTracker;
import org.ethereum.sharding.crypto.DepositAuthority;
import org.ethereum.sharding.domain.Validator;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.FastByteComparisons;
import org.ethereum.util.Futures;
import org.ethereum.util.blockchain.EtherUtil;
Expand All @@ -43,7 +44,9 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static org.ethereum.util.ByteUtil.bigIntegerToBytes;
import static org.ethereum.util.ByteUtil.longToBytes;
import static org.ethereum.util.ByteUtil.longToBytesNoLeadZeroes;
import static org.ethereum.util.blockchain.EtherUtil.convert;

/**
Expand All @@ -67,7 +70,7 @@ public class DepositContract {

private static final Logger logger = LoggerFactory.getLogger("beacon");

private static final byte[] DEPOSIT_WEI = convert(32, EtherUtil.Unit.ETHER).toByteArray();
private static final byte[] DEPOSIT_WEI = bigIntegerToBytes(convert(32, EtherUtil.Unit.ETHER));
private static final long GAS_LIMIT = 200_000;
private static final long DEFAULT_GAS_PRICE = convert(5, EtherUtil.Unit.GWEI).longValue();
private static final long DEPOSIT_TIMEOUT = 15; // 15 minutes
Expand Down Expand Up @@ -141,8 +144,8 @@ public Transaction depositTx(final byte[] pubKey, long withdrawalShard, byte[] w
BigInteger nonce = ethereum.getRepository().getNonce(authority.address());
long gasPrice = gasPriceTracker.getRecommendedGasPrice();
Integer chainId = ethereum.getChainIdForNextBlock();
Transaction tx = new Transaction(nonce.toByteArray(), longToBytes(gasPrice), longToBytes(GAS_LIMIT),
address, DEPOSIT_WEI, data, chainId);
Transaction tx = new Transaction(bigIntegerToBytes(nonce), longToBytesNoLeadZeroes(gasPrice),
longToBytesNoLeadZeroes(GAS_LIMIT), address, DEPOSIT_WEI, data, chainId);
authority.sign(tx);
return tx;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void setStateHash(byte[] stateHash) {
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof Beacon)) return false;
if (other == null || !(other instanceof Beacon)) return false;

return FastByteComparisons.equal(this.getHash(), ((Beacon) other).getHash());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,21 @@
*/
package org.ethereum.sharding.domain;

import org.ethereum.sharding.processing.state.BeaconState;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.io.ByteStreams;
import org.ethereum.util.Utils;

import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import static org.ethereum.util.Utils.parseHex;

/**
* A special beacon chain block that is the first block of the chain.
Expand All @@ -35,24 +47,25 @@ public class BeaconGenesis extends Beacon {

private static BeaconGenesis instance;

// stub for proposer, 08/28/2018 @ 11:13am (UTC)
private long timestamp = 1535454832000L;
/* Genesis timestamp, a source of slot's time frames */
private long timestamp = 0L;
/* A list of public keys that identifies participants of initial active validator set */
private List<String> initialValidators = new ArrayList<>();

private BeaconGenesis() {
super(EMPTY, EMPTY, EMPTY, EMPTY, SLOT);
setStateHash(getState().getHash());
BeaconGenesis(Json json) {
super(json.parentHash(), json.randaoReveal(), json.mainChainRef(), EMPTY, SLOT);
this.timestamp = json.timestamp();
this.initialValidators = json.validatorSet();
}

public static BeaconGenesis instance() {
if (instance == null)
instance = new BeaconGenesis();
if (instance == null) {
Json json = Json.fromResource("beacon-genesis.json");
instance = new BeaconGenesis(json);
}
return instance;
}

public BeaconState getState() {
return new BeaconState();
}

public BigInteger getScore() {
return BigInteger.ZERO;
}
Expand All @@ -61,8 +74,69 @@ public long getTimestamp() {
return timestamp;
}

public List<String> getInitialValidators() {
return initialValidators;
}

@Override
public String toString() {
return super.toString() + ", timestamp: " + timestamp;
}

@JsonSerialize
static class Json {
String parentHash;
String randaoReveal;
String mainChainRef;
long timestamp;
String[] validatorSet;

byte[] parentHash() {
return parseHex(parentHash);
}

byte[] randaoReveal() {
return parseHex(randaoReveal);
}

byte[] mainChainRef() {
return parseHex(mainChainRef);
}

long timestamp() {
return timestamp * 1000; // seconds to millis
}

List<String> validatorSet() {
List<String> validators = new ArrayList<>(validatorSet.length);
for (String val : validatorSet) {
if ("0x".equals(val.substring(0, 2))) {
validators.add(val.substring(2));
} else {
validators.add(val);
}
}
return validators;
}

static Json fromResource(String resourceName) {
return fromStream(Json.class.getClassLoader().getResourceAsStream(resourceName));
}

static Json fromStream(InputStream in) {
String json = null;
try {
json = new String(ByteStreams.toByteArray(in));
ObjectMapper mapper = new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

return mapper.readValue(json, Json.class);
} catch (Exception e) {
Utils.showErrorAndExit("Failed to parse beacon chain genesis: " + e.getMessage(), json);
throw new RuntimeException(e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@
*/
package org.ethereum.sharding.domain;

import org.ethereum.datasource.Serializer;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPList;

import java.math.BigInteger;

/**
* Represents and item of a list of validators held by validator registration contract.
* Represents an item of a list of validators held by validator registration contract.
*
* @author Mikhail Kalinin
* @since 21.07.2018
Expand All @@ -30,7 +37,13 @@ public class Validator {
private byte[] withdrawalAddress;
private byte[] randao;

private Validator() {
public Validator(byte[] encoded) {
RLPList list = RLP.unwrapList(encoded);

this.pubKey = list.get(0).getRLPData();
this.withdrawalShard = ByteUtil.bytesToBigInteger(list.get(1).getRLPData()).longValue();
this.withdrawalAddress = list.get(2).getRLPData();
this.randao = list.get(3).getRLPData();
}

public Validator(byte[] pubKey, long withdrawalShard, byte[] withdrawalAddress, byte[] randao) {
Expand All @@ -40,6 +53,10 @@ public Validator(byte[] pubKey, long withdrawalShard, byte[] withdrawalAddress,
this.randao = randao;
}

public byte[] getEncoded() {
return RLP.wrapList(pubKey, BigInteger.valueOf(withdrawalShard).toByteArray(), withdrawalAddress, randao);
}

public byte[] getPubKey() {
return pubKey;
}
Expand All @@ -55,4 +72,16 @@ public byte[] getWithdrawalAddress() {
public byte[] getRandao() {
return randao;
}

public static final Serializer<Validator, byte[]> Serializer = new Serializer<Validator, byte[]>() {
@Override
public byte[] serialize(Validator validator) {
return validator.getEncoded();
}

@Override
public Validator deserialize(byte[] stream) {
return stream == null ? null : new Validator(stream);
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@
import org.ethereum.sharding.config.DepositContractConfig;
import org.ethereum.sharding.processing.BeaconChain;
import org.ethereum.sharding.proposer.ProposerService;
import org.ethereum.sharding.pubsub.BeaconChainSynced;
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;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigInteger;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -69,8 +69,10 @@ public class ShardingWorldManager extends WorldManager {
DbFlushManager dbFlushManager;
DbFlushManager beaconDbFlusher;
Publisher publisher;
ValidatorService validatorService;

private CompletableFuture<Void> contractInit = new CompletableFuture<>();
private CompletableFuture<ValidatorService.State> validatorServiceInit = new CompletableFuture<>();

public ShardingWorldManager(SystemProperties config, Repository repository, EthereumListener listener,
Blockchain blockchain, BlockStore blockStore, DepositContractConfig contractConfig,
Expand Down Expand Up @@ -128,19 +130,34 @@ public byte[] getContractAddress() {
}

public void setValidatorService(final ValidatorService validatorService) {
contractInit.thenRunAsync(validatorService::init);
this.validatorService = validatorService;
if (validatorService.getState() == ValidatorService.State.Disabled) {
validatorServiceInit.complete(ValidatorService.State.Disabled);
} else {
publisher.subscribe(ValidatorStateUpdated.class, data -> {
if (data.getNewState() == ValidatorService.State.Enlisted ||
data.getNewState() == ValidatorService.State.DepositFailed) {
validatorServiceInit.complete(data.getNewState());
}
});
}
contractInit.thenRunAsync(this.validatorService::init);
}

public void setProposerService(final ProposerService proposerService) {
publisher.subscribe(ValidatorStateUpdated.class, data -> {
if (data.getNewState() == ValidatorService.State.Enlisted) {
// start only if validator is enlisted and after sync is finished
publisher.subscribe(BeaconChainSynced.class, data -> {
if (validatorService.getState() == ValidatorService.State.Enlisted) {
proposerService.init();
}
});
}

public void setBeaconChain(final BeaconChain beaconChain) {
contractInit.thenRunAsync(beaconChain::init);
// PoC mode:
// wait for validator initialization
// it's needed to be sure that the validator is registered before beacon genesis state calculation
validatorServiceInit.runAfterBothAsync(contractInit, beaconChain::init);
}

public void setPublisher(Publisher publisher) {
Expand Down
Loading