diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/config/BeaconConfig.java b/ethereumj-core/src/main/java/org/ethereum/sharding/config/BeaconConfig.java index 837d039b83..f3b76ee86a 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/config/BeaconConfig.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/config/BeaconConfig.java @@ -35,6 +35,8 @@ import org.ethereum.db.TransactionStore; import org.ethereum.facade.Ethereum; import org.ethereum.manager.WorldManager; +import org.ethereum.sharding.crypto.DummySign; +import org.ethereum.sharding.crypto.Sign; import org.ethereum.sharding.pubsub.Publisher; import org.ethereum.sharding.manager.ShardingWorldManager; import org.ethereum.sharding.processing.BeaconChain; @@ -43,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.BeaconAttester; +import org.ethereum.sharding.validator.BeaconAttesterImpl; import org.ethereum.sharding.validator.BeaconProposer; import org.ethereum.sharding.validator.BeaconProposerImpl; import org.ethereum.sharding.validator.ValidatorService; @@ -127,8 +131,8 @@ public ValidatorRegistrationService validatorRegistrationService() { @Bean public ValidatorService validatorService() { if (validatorConfig().isEnabled()) { - ValidatorService validatorService = new ValidatorServiceImpl(beaconProposer(), beaconChain(), - publisher(), validatorConfig(), ethereum, blockStore); + ValidatorService validatorService = new ValidatorServiceImpl(beaconProposer(), beaconAttester(), + beaconChain(), publisher(), validatorConfig(), ethereum, blockStore); shardingWorldManager.setProposerService(validatorService); return validatorService; } else { @@ -160,7 +164,8 @@ public StateRepository beaconStateRepository() { Source validatorSrc = cachedBeaconChainSource("validator_set"); Source validatorIndexSrc = cachedBeaconChainSource("validator_index"); Source crystallizedSrc = cachedBeaconChainSource("crystallized_state"); - return new BeaconStateRepository(src, crystallizedSrc, validatorSrc, validatorIndexSrc); + Source activeSrc = cachedBeaconChainSource("active_state"); + return new BeaconStateRepository(src, crystallizedSrc, activeSrc, validatorSrc, validatorIndexSrc); } @Bean @@ -216,10 +221,15 @@ public Publisher publisher() { @Bean public BeaconProposer beaconProposer() { - return new BeaconProposerImpl(randao(), beaconStateRepository(), + return new BeaconProposerImpl(randao(), beaconStateRepository(), beaconStore(), BeaconChainFactory.stateTransition(validatorRepository()), validatorConfig()); } + @Bean + public BeaconAttester beaconAttester() { + return new BeaconAttesterImpl(beaconStateRepository(), beaconStore(), validatorConfig(), sign()); + } + @Bean public Randao randao() { DbSource src = commonConfig.keyValueDataSource("randao"); @@ -244,6 +254,10 @@ public DepositAuthority depositAuthority() { return new UnsecuredDepositAuthority(validatorConfig()); } + public Sign sign() { + return new DummySign(); + } + public static class Enabled implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/crypto/DummySign.java b/ethereumj-core/src/main/java/org/ethereum/sharding/crypto/DummySign.java new file mode 100644 index 0000000000..988481464e --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/crypto/DummySign.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.crypto; + +import java.math.BigInteger; + +import static org.ethereum.crypto.HashUtil.sha3; + +/** + * Dummy signature implementation without real crypto underneath + */ +public class DummySign implements Sign { + + /** + * Sign the message + */ + public Signature sign(byte[] msg, byte[] privateKey) { + byte[] rSource = sha3(privateKey); + byte[] sSource = sha3(msg, privateKey); + Signature res = new Signature(); + res.r = new BigInteger(rSource); + res.s = new BigInteger(sSource); + + return res; + } + + /** + * Verifies whether signature is made by signer with publicKey + */ + public boolean verify(Signature signature, byte[] publicKey) { + return true; + } + + /** + * Aggregates several signatures in one + */ + public Signature aggSigns(Signature[] signatures) { + int signatureLen = signatures.length; + Signature aggSignature = new Signature(); + aggSignature.r = BigInteger.ZERO; + aggSignature.s = BigInteger.ZERO; + for (int i = 0; i < signatureLen; ++i) { + for (Signature signature : signatures) { + aggSignature.r = aggSignature.r.xor(signature.r); + aggSignature.s = aggSignature.s.xor(signature.s); + } + } + + return aggSignature; + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/crypto/Sign.java b/ethereumj-core/src/main/java/org/ethereum/sharding/crypto/Sign.java new file mode 100644 index 0000000000..04e25a9955 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/crypto/Sign.java @@ -0,0 +1,48 @@ +package org.ethereum.sharding.crypto; + +import java.math.BigInteger; +import java.util.Objects; + +/** + * Signature utilities + * Signature should be implemented using BLS + */ +public interface Sign { + + /** + * Sign the message + */ + Signature sign(byte[] msg, byte[] privateKey); + + /** + * Verifies whether signature is made by signer with publicKey + */ + boolean verify(Signature signature, byte[] publicKey); + + /** + * Aggregates several signatures in one + */ + Signature aggSigns(Signature[] signatures); + + class Signature { + public BigInteger r; + public BigInteger s; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Signature signature = (Signature) o; + return Objects.equals(r, signature.r) && + Objects.equals(s, signature.s); + } + + @Override + public String toString() { + return "Signature{" + + "r=" + r + + ", s=" + s + + '}'; + } + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/domain/Beacon.java b/ethereumj-core/src/main/java/org/ethereum/sharding/domain/Beacon.java index 59729ae4ff..9520fc6af4 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/domain/Beacon.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/domain/Beacon.java @@ -18,15 +18,21 @@ package org.ethereum.sharding.domain; import org.ethereum.datasource.Serializer; +import org.ethereum.sharding.processing.state.AttestationRecord; import org.ethereum.util.ByteUtil; import org.ethereum.util.FastByteComparisons; import org.ethereum.util.RLP; +import org.ethereum.util.RLPElement; import org.ethereum.util.RLPList; import org.spongycastle.util.encoders.Hex; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; import static org.ethereum.crypto.HashUtil.blake2b; +import static org.ethereum.util.ByteUtil.ZERO_BYTE_ARRAY; +import static org.ethereum.util.ByteUtil.isSingleZero; /** * Beacon chain block structure. @@ -46,13 +52,17 @@ public class Beacon { private byte[] stateHash; /* Slot number */ private long slotNumber; + /* Attestations */ + private List attestations; - public Beacon(byte[] parentHash, byte[] randaoReveal, byte[] mainChainRef, byte[] stateHash, long slotNumber) { + public Beacon(byte[] parentHash, byte[] randaoReveal, byte[] mainChainRef, byte[] stateHash, + long slotNumber,List attestations) { this.parentHash = parentHash; this.randaoReveal = randaoReveal; this.mainChainRef = mainChainRef; this.stateHash = stateHash; this.slotNumber = slotNumber; + this.attestations = attestations; } public Beacon(byte[] rlp) { @@ -62,11 +72,24 @@ public Beacon(byte[] rlp) { this.mainChainRef = items.get(2).getRLPData(); this.stateHash = items.get(3).getRLPData(); this.slotNumber = ByteUtil.bytesToBigInteger(items.get(4).getRLPData()).longValue(); + + this.attestations = new ArrayList<>(); + if (!isSingleZero(items.get(5).getRLPData())) { + RLPList attestationsRlp = RLP.unwrapList(items.get(5).getRLPData()); + for (RLPElement anAttestationsRlp : attestationsRlp) { + attestations.add(new AttestationRecord(anAttestationsRlp.getRLPData())); + } + } } public byte[] getEncoded() { + byte[][] encodedAttestations = new byte[attestations.size()][]; + for (int i = 0; i < attestations.size(); i++) + encodedAttestations[i] = attestations.get(i).getEncoded(); + return RLP.wrapList(parentHash, randaoReveal, mainChainRef, stateHash, - BigInteger.valueOf(slotNumber).toByteArray()); + BigInteger.valueOf(slotNumber).toByteArray(), + encodedAttestations.length == 0 ? ZERO_BYTE_ARRAY : RLP.wrapList(encodedAttestations)); } public byte[] getHash() { @@ -93,6 +116,10 @@ public long getSlotNumber() { return slotNumber; } + public List getAttestations() { + return attestations; + } + public boolean isParentOf(Beacon other) { return FastByteComparisons.equal(this.getHash(), other.getParentHash()); } @@ -101,10 +128,14 @@ public void setStateHash(byte[] stateHash) { this.stateHash = stateHash; } + public void setAttestations(List attestations) { + this.attestations = attestations; + } + @Override public boolean equals(Object other) { if (this == other) return true; - if (other == null || !(other instanceof Beacon)) return false; + if (!(other instanceof Beacon)) return false; return FastByteComparisons.equal(this.getHash(), ((Beacon) other).getHash()); } diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/domain/BeaconGenesis.java b/ethereumj-core/src/main/java/org/ethereum/sharding/domain/BeaconGenesis.java index c0ae4fb5b6..94738cb429 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/domain/BeaconGenesis.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/domain/BeaconGenesis.java @@ -53,7 +53,7 @@ public class BeaconGenesis extends Beacon { private List initialValidators = new ArrayList<>(); BeaconGenesis(Json json) { - super(json.parentHash(), json.randaoReveal(), json.mainChainRef(), EMPTY, SLOT); + super(json.parentHash(), json.randaoReveal(), json.mainChainRef(), EMPTY, SLOT, new ArrayList<>()); this.timestamp = json.timestamp(); this.initialValidators = json.validatorSet(); } diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/consensus/BeaconStateTransition.java b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/consensus/BeaconStateTransition.java index bee585d5ae..d69c83f797 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/consensus/BeaconStateTransition.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/consensus/BeaconStateTransition.java @@ -69,6 +69,6 @@ public BeaconState applyBlock(Beacon block, BeaconState to) { .withFinality(finality); } - return new BeaconState(crystallized); + return new BeaconState(crystallized, to.getActiveState()); } } diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/consensus/GenesisTransition.java b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/consensus/GenesisTransition.java index 59c1e292b6..6263f2f353 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/consensus/GenesisTransition.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/consensus/GenesisTransition.java @@ -80,7 +80,7 @@ public BeaconState applyBlock(Beacon block, BeaconState to) { .withLastStateRecalc(0L) .withCrosslinks(Crosslink.empty(SHARD_COUNT)); - return new BeaconState(crystallizedState); + return new BeaconState(crystallizedState, to.getActiveState()); } class ValidatorSetInitiator implements StateTransition { diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/ActiveState.java b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/ActiveState.java new file mode 100644 index 0000000000..b693de845c --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/ActiveState.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.processing.state; + +import org.ethereum.datasource.Serializer; +import org.ethereum.util.ByteUtil; +import org.ethereum.util.RLP; +import org.ethereum.util.RLPElement; +import org.ethereum.util.RLPList; + +import java.util.ArrayList; +import java.util.List; + +import static org.ethereum.crypto.HashUtil.blake2b; +import static org.ethereum.sharding.processing.consensus.BeaconConstants.CYCLE_LENGTH; +import static org.ethereum.util.ByteUtil.isSingleZero; +import static org.ethereum.util.ByteUtil.toHexString; + +/** + * Active beacon chain state + */ +public class ActiveState { + // Attestations that have not yet been processed + private final List pendingAttestations; + // Special objects that have not yet been processed + private final List pendingSpecials; + // Most recent 2 * CYCLE_LENGTH block hashes, older to newer + private final List recentBlockHashes; + // RANDAO state + private final byte[] randaoMix; + + // TODO: Add pending_specials + + + public ActiveState(List pendingAttestations, List pendingSpecials, + List recentBlockHashes, byte[] randaoMix) { + this.pendingAttestations = pendingAttestations; + this.pendingSpecials = pendingSpecials; + this.recentBlockHashes = recentBlockHashes; + this.randaoMix = randaoMix; + } + + /** + * Creates active state with empty pending attestations + * and block hashes filled with `00`*32 + */ + public static ActiveState createEmpty() { + List pendingAttestations = new ArrayList<>(); + List pendingSpecials = new ArrayList<>(); + List recentBlockHashes = new ArrayList<>(); + for (int i = 0; i < (CYCLE_LENGTH * 2); ++i) { + recentBlockHashes.add(new byte[32]); + } + + return new ActiveState(pendingAttestations, pendingSpecials, recentBlockHashes, new byte[32]); + } + + public ActiveState(byte[] encoded) { + RLPList list = RLP.unwrapList(encoded); + + this.pendingAttestations = new ArrayList<>(); + if (!isSingleZero(list.get(0).getRLPData())) { + RLPList attestationList = RLP.unwrapList(list.get(0).getRLPData()); + for (RLPElement attestationRlp : attestationList) + pendingAttestations.add(new AttestationRecord(attestationRlp.getRLPData())); + } + + this.pendingSpecials = new ArrayList<>(); + if (!isSingleZero(list.get(1).getRLPData())) { + RLPList specialsList = RLP.unwrapList(list.get(1).getRLPData()); + for (RLPElement specialRlp : specialsList) + pendingSpecials.add(new SpecialRecord(specialRlp.getRLPData())); + } + + this.recentBlockHashes = new ArrayList<>(); + if (!isSingleZero(list.get(2).getRLPData())) { + RLPList hashesList = RLP.unwrapList(list.get(2).getRLPData()); + for (RLPElement hashRlp : hashesList) + recentBlockHashes.add(hashRlp.getRLPData()); + } + + this.randaoMix = list.get(3).getRLPData(); + } + + public List getPendingAttestations() { + return pendingAttestations; + } + + public List getPendingSpecials() { + return pendingSpecials; + } + + public List getRecentBlockHashes() { + return recentBlockHashes; + } + + public byte[] getRandaoMix() { + return randaoMix; + } + + public byte[] getEncoded() { + byte[][] encodedAttestations = new byte[pendingAttestations.size()][]; + for (int i = 0; i < pendingAttestations.size(); i++) + encodedAttestations[i] = pendingAttestations.get(i).getEncoded(); + + byte[][] encodedSpecials = new byte[pendingSpecials.size()][]; + for (int i = 0; i < pendingSpecials.size(); i++) + encodedSpecials[i] = pendingSpecials.get(i).getEncoded(); + + byte[][] encodedHashes = new byte[recentBlockHashes.size()][]; + for (int i = 0; i < recentBlockHashes.size(); i++) + encodedHashes[i] = RLP.encodeElement(recentBlockHashes.get(i)); + + return RLP.wrapList( + pendingAttestations.size() > 0 ? RLP.wrapList(encodedAttestations) : ByteUtil.ZERO_BYTE_ARRAY, + pendingSpecials.size() > 0 ? RLP.wrapList(encodedSpecials) : ByteUtil.ZERO_BYTE_ARRAY, + recentBlockHashes.size() > 0 ? RLP.wrapList(encodedHashes) : ByteUtil.ZERO_BYTE_ARRAY, + RLP.encodeElement(randaoMix) + ); + } + + public byte[] getHash() { + return blake2b(getEncoded()); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder().append("ActiveState{") + .append("pendingAttestations=[").append(pendingAttestations.size()).append(" item(s)]") + .append("pendingSpecials=[").append(pendingSpecials.size()).append(" item(s)]") + .append(", recentBlockHashes=[..."); + + for (int i = Math.max(0, recentBlockHashes.size() - 3); i < recentBlockHashes.size(); ++i) { + builder.append(", ").append(toHexString(recentBlockHashes.get(i))); + } + builder.append("]"); + + builder.append(", randaoMix=") + .append(ByteUtil.toHexString(randaoMix)) + .append('}'); + + return builder.toString(); + } + + public static final org.ethereum.datasource.Serializer Serializer = new Serializer() { + @Override + public byte[] serialize(ActiveState state) { + return state == null ? null : state.getEncoded(); + } + + @Override + public ActiveState deserialize(byte[] stream) { + return stream == null ? null : new ActiveState(stream); + } + }; +} diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/AttestationRecord.java b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/AttestationRecord.java new file mode 100644 index 0000000000..0d56b0fb9b --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/AttestationRecord.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.processing.state; + +import org.ethereum.sharding.crypto.Sign; +import org.ethereum.sharding.util.Bitfield; +import org.ethereum.util.ByteUtil; +import org.ethereum.util.RLP; +import org.ethereum.util.RLPList; + +import java.util.Arrays; + +import static org.ethereum.util.ByteUtil.bigIntegerToBytes; +import static org.ethereum.util.ByteUtil.byteArrayToLong; +import static org.ethereum.util.ByteUtil.bytesToBigInteger; +import static org.ethereum.util.ByteUtil.intToBytesNoLeadZeroes; +import static org.ethereum.util.ByteUtil.longToBytesNoLeadZeroes; +import static org.ethereum.util.ByteUtil.toHexString; + +/** + * Slot attestation data + */ +public class AttestationRecord { + + // Slot number + private final long slot; + // Shard ID + private final int shardId; + // List of block hashes that this signature is signing over that + // are NOT part of the current chain, in order of oldest to newest + private final byte[][] obliqueParentHashes; + // Block hash in the shard that we are attesting to + private final byte[] shardBlockHash; + // Who is participating + private final Bitfield attesterBitfield; + // Last justified block + private final long justifiedSlot; + private final byte[] justifiedBlockHash; + // The actual signature + private final Sign.Signature aggregateSig; + + public AttestationRecord(long slot, int shardId, byte[][] obliqueParentHashes, byte[] shardBlockHash, + Bitfield attesterBitfield, long justifiedSlot, byte[] justifiedBlockHash, + Sign.Signature aggregateSig) { + this.slot = slot; + this.shardId = shardId; + this.obliqueParentHashes = obliqueParentHashes; + this.shardBlockHash = shardBlockHash; + this.attesterBitfield = attesterBitfield; + this.justifiedSlot = justifiedSlot; + this.justifiedBlockHash = justifiedBlockHash; + this.aggregateSig = aggregateSig; + } + + public AttestationRecord(byte[] encoded) { + RLPList list = RLP.unwrapList(encoded); + + this.slot = byteArrayToLong(list.get(0).getRLPData()); + this.shardId = bytesToBigInteger(list.get(1).getRLPData()).shortValue(); + + RLPList hashesList = RLP.unwrapList(list.get(2).getRLPData()); + this.obliqueParentHashes = new byte[hashesList.size()][]; + for (int i = 0; i < hashesList.size(); i++) + this.obliqueParentHashes[i] = hashesList.get(i).getRLPData(); + + this.shardBlockHash = list.get(3).getRLPData(); + this.attesterBitfield = new Bitfield(list.get(4).getRLPData()); + this.justifiedSlot = byteArrayToLong(list.get(5).getRLPData()); + this.justifiedBlockHash = list.get(6).getRLPData(); + + RLPList sigList = RLP.unwrapList(list.get(7).getRLPData()); + this.aggregateSig = new Sign.Signature(); + this.aggregateSig.r = ByteUtil.bytesToBigInteger(sigList.get(0).getRLPData()); + this.aggregateSig.s = ByteUtil.bytesToBigInteger(sigList.get(1).getRLPData()); + } + + public long getSlot() { + return slot; + } + + public int getShardId() { + return shardId; + } + + public byte[][] getObliqueParentHashes() { + return obliqueParentHashes; + } + + public byte[] getShardBlockHash() { + return shardBlockHash; + } + + public Bitfield getAttesterBitfield() { + return attesterBitfield; + } + + public long getJustifiedSlot() { + return justifiedSlot; + } + + public byte[] getJustifiedBlockHash() { + return justifiedBlockHash; + } + + public Sign.Signature getAggregateSig() { + return aggregateSig; + } + + public byte[] getEncoded() { + byte[][] encodedAggSig = new byte[2][]; + encodedAggSig[0] = bigIntegerToBytes(aggregateSig.r); + encodedAggSig[1] = bigIntegerToBytes(aggregateSig.s); + + return RLP.wrapList(longToBytesNoLeadZeroes(slot), + intToBytesNoLeadZeroes(shardId), + RLP.wrapList(obliqueParentHashes), + shardBlockHash, + attesterBitfield.getData(), + longToBytesNoLeadZeroes(justifiedSlot), + justifiedBlockHash, + RLP.wrapList(encodedAggSig)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AttestationRecord that = (AttestationRecord) o; + return slot == that.slot && + shardId == that.shardId && + attesterBitfield == that.attesterBitfield && + justifiedSlot == that.justifiedSlot && + Arrays.equals(obliqueParentHashes, that.obliqueParentHashes) && + Arrays.equals(shardBlockHash, that.shardBlockHash) && + Arrays.equals(justifiedBlockHash, that.justifiedBlockHash) && + aggregateSig == that.aggregateSig; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder() + .append("AttestationRecord{") + .append("slot=").append(slot) + .append(", shardId=").append(shardId) + .append(", obliqueParentHashes=[").append(obliqueParentHashes.length).append(" item(s)]") + .append(", shardBlockHash=").append(toHexString(shardBlockHash)) + .append(", attesterBitfield=").append(attesterBitfield) + .append(", justifiedSlot=").append(justifiedSlot) + .append(", justifiedBlockHash=").append(toHexString(justifiedBlockHash)) + .append(", aggregateSig=[").append(aggregateSig).append("}"); + + return builder.toString(); + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/BeaconState.java b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/BeaconState.java index ee2b68c4ea..1d0479120f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/BeaconState.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/BeaconState.java @@ -35,21 +35,27 @@ public class BeaconState { private final CrystallizedState crystallizedState; + private final ActiveState activeState; - public BeaconState(CrystallizedState crystallizedState) { + public BeaconState(CrystallizedState crystallizedState, ActiveState activeState) { this.crystallizedState = crystallizedState; + this.activeState = activeState; } public CrystallizedState getCrystallizedState() { return crystallizedState; } + public ActiveState getActiveState() { + return activeState; + } + public byte[] getHash() { return flatten().getHash(); } public Flattened flatten() { - return new Flattened(crystallizedState); + return new Flattened(crystallizedState, activeState); } public Committee[][] getCommittees() { @@ -70,26 +76,33 @@ public boolean equals(Object o) { public static class Flattened { private final byte[] crystallizedStateHash; + private final byte[] activeStateHash; - public Flattened(CrystallizedState crystallizedState) { + public Flattened(CrystallizedState crystallizedState, ActiveState activeState) { this.crystallizedStateHash = crystallizedState.getHash(); + this.activeStateHash = activeState.getHash(); } public Flattened(byte[] encoded) { RLPList list = RLP.unwrapList(encoded); this.crystallizedStateHash = list.get(0).getRLPData(); + this.activeStateHash = list.get(1).getRLPData(); } public byte[] getCrystallizedStateHash() { return crystallizedStateHash; } + public byte[] getActiveStateHash() { + return activeStateHash; + } + public byte[] getHash() { return blake2b(encode()); } public byte[] encode() { - return RLP.wrapList(crystallizedStateHash); + return RLP.wrapList(crystallizedStateHash, activeStateHash); } public static final org.ethereum.datasource.Serializer Serializer = new Serializer() { diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/BeaconStateRepository.java b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/BeaconStateRepository.java index 06ed4f9c38..c271ae825f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/BeaconStateRepository.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/BeaconStateRepository.java @@ -35,10 +35,12 @@ public class BeaconStateRepository implements StateRepository { ObjectDataSource stateDS; Source crystallizedSrc; ObjectDataSource crystallizedDS; + ObjectDataSource activeDS; Source validatorSrc; Source validatorIndexSrc; public BeaconStateRepository(Source src, Source crystallizedSrc, + Source activeSrc, Source validatorSrc, Source validatorIndexSrc) { this.src = src; this.crystallizedSrc = crystallizedSrc; @@ -48,12 +50,16 @@ public BeaconStateRepository(Source src, Source this.stateDS = new ObjectDataSource<>(src, BeaconState.Flattened.Serializer, BeaconStore.BLOCKS_IN_MEM); this.crystallizedDS = new ObjectDataSource<>(crystallizedSrc, CrystallizedState.Flattened.Serializer, BeaconStore.BLOCKS_IN_MEM); + this.activeDS = new ObjectDataSource<>(activeSrc, + ActiveState.Serializer, BeaconStore.BLOCKS_IN_MEM); } @Override public void insert(BeaconState state) { CrystallizedState crystallized = state.getCrystallizedState(); crystallizedDS.put(crystallized.getHash(), crystallized.flatten()); + ActiveState activeState = state.getActiveState(); + activeDS.put(activeState.getHash(), activeState); stateDS.put(state.getHash(), state.flatten()); } @@ -70,15 +76,18 @@ public BeaconState get(byte[] hash) { public BeaconState getEmpty() { CrystallizedState.Flattened crystallizedFlattened = CrystallizedState.Flattened.empty(); CrystallizedState crystallizedState = fromFlattened(crystallizedFlattened); + ActiveState activeState = ActiveState.createEmpty(); - return new BeaconState(crystallizedState); + return new BeaconState(crystallizedState, activeState); } BeaconState fromFlattened(BeaconState.Flattened flattened) { CrystallizedState.Flattened crystallizedFlattened = crystallizedDS.get(flattened.getCrystallizedStateHash()); CrystallizedState crystallizedState = fromFlattened(crystallizedFlattened); - return new BeaconState(crystallizedState); + ActiveState activeState = activeDS.get(flattened.getActiveStateHash()); + + return new BeaconState(crystallizedState, activeState); } CrystallizedState fromFlattened(CrystallizedState.Flattened flattened) { diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/SpecialRecord.java b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/SpecialRecord.java new file mode 100644 index 0000000000..760ba6f59c --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/processing/state/SpecialRecord.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.processing.state; + +import org.ethereum.util.RLP; +import org.ethereum.util.RLPList; +import org.spongycastle.util.encoders.Hex; + +import java.util.Arrays; + +import static org.ethereum.util.ByteUtil.byteArrayToInt; +import static org.ethereum.util.ByteUtil.intToBytesNoLeadZeroes; + +/** + * Special record, sort of container + */ +public class SpecialRecord { + + // Kind of object + private final int kind; + // Payload + private final byte[] data; + + public SpecialRecord(int kind, byte[] data) { + this.kind = kind; + this.data = data; + } + + public SpecialRecord(byte[] encoded) { + RLPList list = RLP.unwrapList(encoded); + + this.kind = byteArrayToInt(list.get(0).getRLPData()); + this.data = list.get(1).getRLPData(); + } + + public int getKind() { + return kind; + } + + public byte[] getData() { + return data; + } + + public byte[] getEncoded() { + return RLP.wrapList(intToBytesNoLeadZeroes(kind), + data); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SpecialRecord that = (SpecialRecord) o; + return kind == that.kind && + Arrays.equals(data, that.data); + } + + @Override + public String toString() { + return "SpecialRecord{" + + "kind=" + kind + + ", data=" + Hex.toHexString(data) + + '}'; + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/pubsub/BeaconBlockAttested.java b/ethereumj-core/src/main/java/org/ethereum/sharding/pubsub/BeaconBlockAttested.java new file mode 100644 index 0000000000..bdc4797980 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/pubsub/BeaconBlockAttested.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.pubsub; + +import org.ethereum.sharding.processing.state.AttestationRecord; + +/** + * When new {@link AttestationRecord} received + */ +public class BeaconBlockAttested extends Event { + + public static class Data { + private final AttestationRecord attestationRecord; + + public Data(AttestationRecord attestationRecord) { + this.attestationRecord = attestationRecord; + } + + public AttestationRecord getAttestationRecord() { + return attestationRecord; + } + } + + public BeaconBlockAttested(AttestationRecord attestationRecord) { + super(new Data(attestationRecord)); + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/util/Bitfield.java b/ethereumj-core/src/main/java/org/ethereum/sharding/util/Bitfield.java new file mode 100644 index 0000000000..b3e635b287 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/util/Bitfield.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.util; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; + +/** + * Bitfield is bit array where every bit represents status of + * attester with corresponding index + */ +public class Bitfield { + + private final BitSet payload; + private final int size; // in Bits + + private Bitfield(int size) { + this.size = calcLength(size) * Byte.SIZE; + this.payload = new BitSet(size); + } + + public Bitfield(byte[] data) { + this.size = data.length * Byte.SIZE; + this.payload = BitSet.valueOf(data); + } + + /** + * Calculates attesters bitfield length + * @param num Number of attesters + * @return Bitfield length in bytes + */ + private static int calcLength(int num) { + return (num + 7) / Byte.SIZE; + } + + /** + * Creates empty bitfield for estimated number of attesters + * @param validatorsCount Number of attesters + * @return empty bitfield with correct length + */ + public static Bitfield createEmpty(int validatorsCount) { + return new Bitfield(validatorsCount); + } + + /** + * Modifies bitfield to represent attester's vote + * Should place its bit on the right place + * Doesn't modify original bitfield + * @param bitfield Original bitfield + * @param index Index number of attester + * @return bitfield with vote in place + */ + public static Bitfield markVote(final Bitfield bitfield, int index) { + Bitfield newBitfield = bitfield.clone(); + newBitfield.payload.set(index); + return newBitfield; + } + + /** + * Checks whether validator with provided index did his vote + * @param bitfield Bitfield + * @param index Index number of attester + */ + public static boolean hasVoted(Bitfield bitfield, int index) { + return bitfield.payload.get(index); + } + + /** + * Calculate number of votes in provided bitfield + * @param bitfield Bitfield + * @return number of votes + */ + public static int calcVotes(Bitfield bitfield) { + int votes = 0; + for (int i = 0; i < bitfield.size(); ++i) { + if (hasVoted(bitfield, i)) ++votes; + } + + return votes; + } + + /** + * OR aggregation function + * OR aggregation of input bitfields + * @param bitfields Bitfields + * @return All bitfields aggregated using OR + */ + public static Bitfield orBitfield(List bitfields) { + if (bitfields.isEmpty()) return null; + + int bitfieldLen = bitfields.get(0).size(); + Bitfield aggBitfield = new Bitfield(bitfieldLen); + for (int i = 0; i < bitfieldLen; ++i) { + for (Bitfield bitfield : bitfields) { + if (aggBitfield.payload.get(i) | bitfield.payload.get(i)) { + aggBitfield.payload.set(i); + } + } + } + + return aggBitfield; + } + + public int size() { + return size; + } + + public byte[] getData() { + return Arrays.copyOf(payload.toByteArray(), size * Byte.SIZE); + } + + public Bitfield clone() { + return new Bitfield(getData()); + } + +} diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconAttester.java b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconAttester.java new file mode 100644 index 0000000000..6756359e66 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconAttester.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.validator; + +import org.ethereum.sharding.domain.Beacon; +import org.ethereum.sharding.processing.state.AttestationRecord; +import org.ethereum.sharding.processing.state.BeaconState; +import org.ethereum.sharding.processing.state.Committee; + +/** + * Beacon chain block attester + */ +public interface BeaconAttester { + + /** + * Attests block + * + * @param in Input data + * @param pubKey Public key of the attestation validator + * @return Attestation record + */ + AttestationRecord attestBlock(Input in, byte[] pubKey); + + + class Input { + long slotNumber; + Committee.Index index; + Beacon block; + BeaconState state; + + public Input(long slotNumber, Committee.Index index, Beacon block, BeaconState state) { + this.slotNumber = slotNumber; + this.index = index; + this.block = block; + this.state = state; + } + + public Input(long slotNumber, Committee.Index index, ValidatorService.ChainHead head) { + this.slotNumber = slotNumber; + this.index = index; + this.block = head.block; + this.state = head.state; + } + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconAttesterImpl.java b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconAttesterImpl.java new file mode 100644 index 0000000000..eddfe73f8e --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconAttesterImpl.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.validator; + +import org.ethereum.sharding.config.ValidatorConfig; +import org.ethereum.sharding.processing.db.BeaconStore; +import org.ethereum.sharding.processing.state.AttestationRecord; +import org.ethereum.sharding.processing.state.StateRepository; +import org.ethereum.sharding.util.Bitfield; +import org.ethereum.sharding.crypto.Sign; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Default implementation of {@link BeaconAttester}. + */ +public class BeaconAttesterImpl implements BeaconAttester { + + private static final Logger logger = LoggerFactory.getLogger("attester"); + + StateRepository repository; + BeaconStore store; + ValidatorConfig config; + Sign sign; + + public BeaconAttesterImpl(StateRepository repository, BeaconStore store, ValidatorConfig config, + Sign sign) { + this.repository = repository; + this.store = store; + this.config = config; + this.sign = sign; + } + + @Override + public AttestationRecord attestBlock(Input in, byte[] pubKey) { + long lastJustified = in.state.getCrystallizedState().getFinality().getLastJustifiedSlot(); + AttestationRecord attestationRecord = new AttestationRecord( + in.slotNumber, + in.index.getShardId(), + new byte[0][0], + in.block.getHash(), + Bitfield.markVote(Bitfield.createEmpty(in.index.getCommitteeSize()), in.index.getValidatorIdx()), + lastJustified, + store.getCanonicalByNumber(lastJustified) == null ? new byte[32] : store.getCanonicalByNumber(lastJustified).getHash(), + sign.aggSigns(new Sign.Signature[]{sign.sign(in.block.getEncoded(), pubKey)}) + ); + + logger.info("Block {} attested by #{} in slot {} ", in.block, in.index.getValidatorIdx(), in.slotNumber); + return attestationRecord; + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconProposerImpl.java b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconProposerImpl.java index 02775b96ed..cc7ef97eb2 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconProposerImpl.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/BeaconProposerImpl.java @@ -21,6 +21,7 @@ import org.ethereum.sharding.config.ValidatorConfig; import org.ethereum.sharding.domain.Beacon; import org.ethereum.sharding.domain.Validator; +import org.ethereum.sharding.processing.db.BeaconStore; import org.ethereum.sharding.pubsub.BeaconChainSynced; import org.ethereum.sharding.processing.consensus.StateTransition; import org.ethereum.sharding.processing.state.BeaconState; @@ -29,6 +30,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; + /** * Default implementation of {@link BeaconProposer}. * @@ -47,11 +50,13 @@ public class BeaconProposerImpl implements BeaconProposer { StateTransition stateTransition; StateRepository repository; ValidatorConfig config; + BeaconStore store; - public BeaconProposerImpl(Randao randao, StateRepository repository, + public BeaconProposerImpl(Randao randao, StateRepository repository, BeaconStore store, StateTransition stateTransition, ValidatorConfig config) { this.randao = randao; this.repository = repository; + this.store = store; this.stateTransition = stateTransition; this.config = config; } @@ -74,10 +79,9 @@ byte[] randaoReveal(BeaconState state, byte[] pubKey) { @Override public Beacon createNewBlock(Input in, byte[] pubKey) { Beacon block = new Beacon(in.parent.getHash(), randaoReveal(in.state, pubKey), in.mainChainRef, - HashUtil.EMPTY_DATA_HASH, in.slotNumber); + HashUtil.EMPTY_DATA_HASH, in.slotNumber, new ArrayList<>()); BeaconState newState = stateTransition.applyBlock(block, in.state); block.setStateHash(newState.getHash()); - logger.info("New block created {}", block); return block; } diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/validator/ValidatorService.java b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/ValidatorService.java index d0c41dd839..9a941827b4 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/validator/ValidatorService.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/ValidatorService.java @@ -19,6 +19,7 @@ import org.ethereum.sharding.domain.Beacon; import org.ethereum.sharding.processing.state.BeaconState; +import org.ethereum.sharding.processing.state.Committee; /** * Service that is responsible for scheduling attestation and proposal tasks for the beacon chain validator. @@ -51,7 +52,7 @@ default void propose(long slotNumber, int validatorIdx) {} * Submits a task to make an attestation in a given slot number. * Thread safe. */ - default void attest(long slotNumber, int validatorIdx) {} + default void attest(long slotNumber, Committee.Index index) {} /** * Handy aggregator diff --git a/ethereumj-core/src/main/java/org/ethereum/sharding/validator/ValidatorServiceImpl.java b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/ValidatorServiceImpl.java index d7a71d82fa..3cdfde89ec 100644 --- a/ethereumj-core/src/main/java/org/ethereum/sharding/validator/ValidatorServiceImpl.java +++ b/ethereumj-core/src/main/java/org/ethereum/sharding/validator/ValidatorServiceImpl.java @@ -26,7 +26,9 @@ import org.ethereum.sharding.config.ValidatorConfig; import org.ethereum.sharding.domain.Beacon; import org.ethereum.sharding.domain.Validator; +import org.ethereum.sharding.processing.state.AttestationRecord; import org.ethereum.sharding.processing.state.Committee; +import org.ethereum.sharding.pubsub.BeaconBlockAttested; import org.ethereum.sharding.pubsub.BeaconBlockImported; import org.ethereum.sharding.pubsub.Publisher; import org.ethereum.sharding.processing.BeaconChain; @@ -62,6 +64,7 @@ public class ValidatorServiceImpl implements ValidatorService { private static final Logger logger = LoggerFactory.getLogger("validator"); BeaconProposer proposer; + BeaconAttester attester; BeaconChain beaconChain; Publisher publisher; ValidatorConfig config; @@ -75,9 +78,10 @@ public class ValidatorServiceImpl implements ValidatorService { private ChainHead head; private byte[] mainChainRef; - public ValidatorServiceImpl(BeaconProposer proposer, BeaconChain beaconChain, Publisher publisher, - ValidatorConfig config, Ethereum ethereum, BlockStore blockStore) { + public ValidatorServiceImpl(BeaconProposer proposer, BeaconAttester attester, BeaconChain beaconChain, + Publisher publisher, ValidatorConfig config, Ethereum ethereum, BlockStore blockStore) { this.proposer = proposer; + this.attester = attester; this.beaconChain = beaconChain; this.publisher = publisher; this.config = config; @@ -162,7 +166,7 @@ private void submitIfAssigned(Committee[][] committees) { if (slotNumber % index.getCommitteeSize() == index.getArrayIdx()) { this.propose(slotNumber, index.getValidatorIdx()); } else { - this.attest(slotNumber, index.getValidatorIdx()); + this.attest(slotNumber, index); } } } @@ -180,15 +184,17 @@ public void propose(long slotNumber, int validatorIdx) { } @Override - public void attest(long slotNumber, int validatorIdx) { + public void attest(long slotNumber, Committee.Index index) { // attester's job should be triggered in the middle of slot's time period - long delay = submit(SLOT_DURATION / 2, slotNumber, validatorIdx, () -> { - // TODO add attester routine here - logger.info("Fake attestation on slot #{}", slotNumber); - return (Void) null; + long delay = submit(SLOT_DURATION / 2, slotNumber, index.getValidatorIdx(), () -> { + BeaconAttester.Input input = new BeaconAttester.Input(slotNumber, index, head); + AttestationRecord attestation = attester.attestBlock(input, pubKeysMap.get(index.getValidatorIdx())); + logger.info("Attestation by #{} on slot #{}", index.getValidatorIdx(), slotNumber); + publisher.publish(new BeaconBlockAttested(attestation)); + return attestation; }); - if (delay >= 0) logger.info("Attester {}: schedule new slot #{} in {}ms", validatorIdx, slotNumber, delay); + if (delay >= 0) logger.info("Attester {}: schedule new slot #{} in {}ms", index.getValidatorIdx(), slotNumber, delay); } long submit(long delayShiftMillis, long slotNumber, int validatorIdx, Supplier supplier) { diff --git a/ethereumj-core/src/test/java/org/ethereum/sharding/crypto/SignTest.java b/ethereumj-core/src/test/java/org/ethereum/sharding/crypto/SignTest.java new file mode 100644 index 0000000000..06b98a0b1a --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/sharding/crypto/SignTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.crypto; + +import org.junit.Test; +import org.spongycastle.util.encoders.Hex; + +import static org.ethereum.crypto.HashUtil.sha3; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test for {@link Sign} + */ +public class SignTest { + + Sign sign = new DummySign(); + + @Test + public void testDummy() { + byte[] msg = Hex.decode("0737626387abcdef"); + byte[] privKey = sha3(msg); + byte[] pubKey = sha3(privKey); + Sign.Signature signature = sign.sign(msg, privKey); + assertTrue(sign.verify(signature, pubKey)); + + byte[] privKey2 = sha3(Hex.decode("abcdef")); + byte[] pubKey2 = sha3(privKey); + // FIXME: Dummy sign is always correct, not true for real implementation + // assertFalse(sign.verify(signature, pubKey2)); + + Sign.Signature signature2 = sign.sign(msg, privKey2); + Sign.Signature aggSign = sign.aggSigns(new Sign.Signature[] {signature, signature2}); + assertTrue(sign.verify(aggSign, pubKey)); + assertTrue(sign.verify(aggSign, pubKey2)); + } +} diff --git a/ethereumj-core/src/test/java/org/ethereum/sharding/domain/GenesisTransitionTest.java b/ethereumj-core/src/test/java/org/ethereum/sharding/domain/GenesisTransitionTest.java index 87fadb6561..46592d87c2 100644 --- a/ethereumj-core/src/test/java/org/ethereum/sharding/domain/GenesisTransitionTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/sharding/domain/GenesisTransitionTest.java @@ -56,7 +56,7 @@ public void testInitialValidatorSet() { BeaconGenesis genesis = new BeaconGenesis(getJson(v1, v3, v4)); StateRepository stateRepository = new BeaconStateRepository(new HashMapDB<>(), new HashMapDB<>(), - new HashMapDB<>(), new HashMapDB<>()); + new HashMapDB<>(), new HashMapDB<>(), new HashMapDB<>()); ValidatorRepository validatorRepository = new PredefinedValidatorRepository(v1, v2, v3, v4); GenesisTransition transition = new GenesisTransition(validatorRepository); diff --git a/ethereumj-core/src/test/java/org/ethereum/sharding/processing/BeaconChainTest.java b/ethereumj-core/src/test/java/org/ethereum/sharding/processing/BeaconChainTest.java index 3b125612eb..36771a437b 100644 --- a/ethereumj-core/src/test/java/org/ethereum/sharding/processing/BeaconChainTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/sharding/processing/BeaconChainTest.java @@ -32,6 +32,7 @@ import org.junit.Test; import java.math.BigInteger; +import java.util.ArrayList; import java.util.Collections; import java.util.Random; import java.util.concurrent.Future; @@ -183,7 +184,7 @@ static Helper newInstance() { Helper inst = new Helper(); inst.store = new IndexedBeaconStore(new HashMapDB<>(), new HashMapDB<>()); inst.repository = new BeaconStateRepository(new HashMapDB<>(), new HashMapDB<>(), - new HashMapDB<>(), new HashMapDB<>()); + new HashMapDB<>(), new HashMapDB<>(), new HashMapDB<>()); inst.beaconChain = (BeaconChainImpl) BeaconChainFactory.create( new DummyFlusher(), inst.store, inst.repository, new NoTransition(), new NoTransition()); inst.beaconChain.scoreFunction = (block, state) -> BigInteger.valueOf(block.getMainChainRef()[0]); @@ -208,7 +209,7 @@ Beacon createBlock(Beacon parent, long score) { BeaconState parentState = repository.get(parent.getStateHash()); Beacon newBlock = new Beacon(parent.getHash(), - randaoReveal, mainChainRef, null, parent.getSlotNumber() + 1); + randaoReveal, mainChainRef, null, parent.getSlotNumber() + 1, new ArrayList<>()); BeaconState newState = beaconChain.transitionFunction.applyBlock(newBlock, parentState); newBlock.setStateHash(newState.getHash()); diff --git a/ethereumj-core/src/test/java/org/ethereum/sharding/processing/consensus/StateTransitionTest.java b/ethereumj-core/src/test/java/org/ethereum/sharding/processing/consensus/StateTransitionTest.java index 53b9a13b30..688930520b 100644 --- a/ethereumj-core/src/test/java/org/ethereum/sharding/processing/consensus/StateTransitionTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/sharding/processing/consensus/StateTransitionTest.java @@ -6,6 +6,7 @@ import org.ethereum.sharding.domain.Validator; import org.ethereum.sharding.processing.db.TrieValidatorSet; import org.ethereum.sharding.processing.db.ValidatorSet; +import org.ethereum.sharding.processing.state.ActiveState; import org.ethereum.sharding.processing.state.BeaconState; import org.ethereum.sharding.processing.state.Committee; import org.ethereum.sharding.processing.state.Crosslink; @@ -14,6 +15,7 @@ import org.ethereum.sharding.processing.state.Finality; import org.junit.Test; +import java.util.ArrayList; import java.util.Random; import static org.ethereum.crypto.HashUtil.randomHash; @@ -32,10 +34,10 @@ public void testBasics() { StateTransition transitionFunction = new BeaconStateTransition( dynastyTransition(validatorTransition(validator)), finalityTransition()); - Beacon b1 = new Beacon(new byte[32], new byte[32], new byte[32], new byte[32], 1L); - Beacon b2 = new Beacon(new byte[32], new byte[32], new byte[32], new byte[32], 63L); - Beacon b3 = new Beacon(new byte[32], new byte[32], new byte[32], new byte[32], 64L); - Beacon b4 = new Beacon(new byte[32], new byte[32], new byte[32], new byte[32], 72L); + Beacon b1 = new Beacon(new byte[32], new byte[32], new byte[32], new byte[32], 1L, new ArrayList<>()); + Beacon b2 = new Beacon(new byte[32], new byte[32], new byte[32], new byte[32], 63L, new ArrayList<>()); + Beacon b3 = new Beacon(new byte[32], new byte[32], new byte[32], new byte[32], 64L, new ArrayList<>()); + Beacon b4 = new Beacon(new byte[32], new byte[32], new byte[32], new byte[32], 72L, new ArrayList<>()); assertEquals(getOrigin(), transitionFunction.applyBlock(b1, getOrigin())); assertEquals(getOrigin(), transitionFunction.applyBlock(b2, getOrigin())); @@ -72,7 +74,7 @@ BeaconState getExpected(Beacon block, Validator validator) { dynasty, finality, new Crosslink[0] ); - return new BeaconState(crystallized); + return new BeaconState(crystallized, getOrigin().getActiveState()); } BeaconState getOrigin() { @@ -81,7 +83,7 @@ BeaconState getOrigin() { Finality finality = Finality.empty(); CrystallizedState crystallized = new CrystallizedState(0L, dynasty, finality, new Crosslink[0]); - return new BeaconState(crystallized); + return new BeaconState(crystallized, ActiveState.createEmpty()); } Validator getRandomValidator() { diff --git a/ethereumj-core/src/test/java/org/ethereum/sharding/processing/db/IndexedBeaconStoreTest.java b/ethereumj-core/src/test/java/org/ethereum/sharding/processing/db/IndexedBeaconStoreTest.java index 4f20eddec0..abc527d9f4 100644 --- a/ethereumj-core/src/test/java/org/ethereum/sharding/processing/db/IndexedBeaconStoreTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/sharding/processing/db/IndexedBeaconStoreTest.java @@ -22,6 +22,7 @@ import org.junit.Test; import java.math.BigInteger; +import java.util.ArrayList; import java.util.Random; import static org.junit.Assert.assertArrayEquals; @@ -227,8 +228,8 @@ private Beacon createBlock(Beacon parent) { rnd.nextBytes(mainChainRef); rnd.nextBytes(stateHash); - return new Beacon(parent == null ? new byte[32] : parent.getHash(), - randaoReveal, mainChainRef, stateHash, parent == null ? 0 : parent.getSlotNumber() + 1); + return new Beacon(parent == null ? new byte[32] : parent.getHash(), randaoReveal, + mainChainRef, stateHash, parent == null ? 0 : parent.getSlotNumber() + 1, new ArrayList<>()); } static class StoreHelper { diff --git a/ethereumj-core/src/test/java/org/ethereum/sharding/util/BitfieldTest.java b/ethereumj-core/src/test/java/org/ethereum/sharding/util/BitfieldTest.java new file mode 100644 index 0000000000..3ee53328c9 --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/sharding/util/BitfieldTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) [2016] [ ] + * 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 . + */ +package org.ethereum.sharding.util; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test for {@link Bitfield} + */ +public class BitfieldTest { + + @Test + public void testSize() { + Bitfield bitfield = Bitfield.createEmpty(13); + assertEquals(16, bitfield.size()); + + Bitfield bitfield2 = Bitfield.createEmpty(113); + assertEquals(120, bitfield2.size()); + } + + @Test + public void testBasics() { + Bitfield bitfield = Bitfield.createEmpty(35); + assertEquals(0, Bitfield.calcVotes(bitfield)); + + bitfield = Bitfield.markVote(bitfield, 32); + assertEquals(1, Bitfield.calcVotes(bitfield)); + for (int i = 0; i < 35; ++i) { + if (i != 32) { + assertFalse(Bitfield.hasVoted(bitfield, i)); + } else { + assertTrue(Bitfield.hasVoted(bitfield, i)); + } + } + + bitfield = Bitfield.markVote(bitfield, 0); + assertEquals(2, Bitfield.calcVotes(bitfield)); + assertTrue(Bitfield.hasVoted(bitfield, 0)); + assertFalse(Bitfield.hasVoted(bitfield, 1)); + assertTrue(Bitfield.hasVoted(bitfield, 32)); + } + + @Test + public void testSerialize() { + Bitfield bitfield = Bitfield.createEmpty(125); + bitfield = Bitfield.markVote(bitfield, 124); + bitfield = Bitfield.markVote(bitfield, 116); + byte[] serialized = bitfield.getData(); + + Bitfield restored = new Bitfield(serialized); + assertTrue(Bitfield.hasVoted(restored, 124)); + assertTrue(Bitfield.hasVoted(restored, 116)); + assertEquals(2, Bitfield.calcVotes(restored)); + } +} diff --git a/ethereumj-core/src/test/java/org/ethereum/sharding/validator/BeaconProposerTest.java b/ethereumj-core/src/test/java/org/ethereum/sharding/validator/BeaconProposerTest.java index 32c607f2da..679d09a8d6 100644 --- a/ethereumj-core/src/test/java/org/ethereum/sharding/validator/BeaconProposerTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/sharding/validator/BeaconProposerTest.java @@ -24,6 +24,8 @@ import org.ethereum.sharding.domain.BeaconGenesis; import org.ethereum.sharding.processing.consensus.NoTransition; import org.ethereum.sharding.processing.consensus.StateTransition; +import org.ethereum.sharding.processing.db.BeaconStore; +import org.ethereum.sharding.processing.db.IndexedBeaconStore; import org.ethereum.sharding.processing.state.BeaconState; import org.ethereum.sharding.processing.state.BeaconStateRepository; import org.ethereum.sharding.processing.state.StateRepository; @@ -105,11 +107,12 @@ static Helper newInstance() { randao.generate(1000); StateRepository repository = new BeaconStateRepository(new HashMapDB<>(), new HashMapDB<>(), - new HashMapDB<>(), new HashMapDB<>()); + new HashMapDB<>(), new HashMapDB<>(), new HashMapDB<>()); + BeaconStore store = new IndexedBeaconStore(new HashMapDB<>(), new HashMapDB<>()); StateTransition stateTransition = new NoTransition(); Helper helper = new Helper(); - helper.proposer = new BeaconProposerImpl(randao, repository, stateTransition, + helper.proposer = new BeaconProposerImpl(randao, repository, store, stateTransition, ValidatorConfig.DISABLED) { @Override