IBFT message payload tests (#404)

Jason Frame 6 years ago committed by GitHub
parent a4c50e54a6
commit 4bfb381b34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 45
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayload.java
  2. 31
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/InRoundPayload.java
  3. 6
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/MessageFactory.java
  4. 36
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayload.java
  5. 10
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/Payload.java
  6. 44
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayload.java
  7. 29
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificate.java
  8. 40
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayload.java
  9. 27
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificate.java
  10. 46
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayload.java
  11. 36
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/SignedData.java
  12. 28
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TestHelpers.java
  13. 50
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayloadTest.java
  14. 93
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayloadTest.java
  15. 48
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayloadTest.java
  16. 82
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificateTest.java
  17. 44
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayloadTest.java
  18. 79
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificateTest.java
  19. 103
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayloadTest.java
  20. 24
      ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java

@ -19,8 +19,12 @@ import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
public class CommitPayload extends InRoundPayload { import java.util.Objects;
import java.util.StringJoiner;
public class CommitPayload implements InRoundPayload {
private static final int TYPE = IbftV2.COMMIT; private static final int TYPE = IbftV2.COMMIT;
private final ConsensusRoundIdentifier roundIdentifier;
private final Hash digest; private final Hash digest;
private final Signature commitSeal; private final Signature commitSeal;
@ -28,16 +32,15 @@ public class CommitPayload extends InRoundPayload {
final ConsensusRoundIdentifier roundIdentifier, final ConsensusRoundIdentifier roundIdentifier,
final Hash digest, final Hash digest,
final Signature commitSeal) { final Signature commitSeal) {
super(roundIdentifier); this.roundIdentifier = roundIdentifier;
this.digest = digest; this.digest = digest;
this.commitSeal = commitSeal; this.commitSeal = commitSeal;
} }
public static CommitPayload readFrom(final RLPInput rlpInput) { public static CommitPayload readFrom(final RLPInput rlpInput) {
rlpInput.enterList(); rlpInput.enterList();
final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput); final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput);
final Hash digest = readDigest(rlpInput); final Hash digest = Payload.readDigest(rlpInput);
final Signature commitSeal = rlpInput.readBytesValue(Signature::decode); final Signature commitSeal = rlpInput.readBytesValue(Signature::decode);
rlpInput.leaveList(); rlpInput.leaveList();
@ -46,7 +49,6 @@ public class CommitPayload extends InRoundPayload {
@Override @Override
public void writeTo(final RLPOutput rlpOutput) { public void writeTo(final RLPOutput rlpOutput) {
rlpOutput.startList(); rlpOutput.startList();
roundIdentifier.writeTo(rlpOutput); roundIdentifier.writeTo(rlpOutput);
rlpOutput.writeBytesValue(digest); rlpOutput.writeBytesValue(digest);
@ -66,4 +68,37 @@ public class CommitPayload extends InRoundPayload {
public Signature getCommitSeal() { public Signature getCommitSeal() {
return commitSeal; return commitSeal;
} }
@Override
public ConsensusRoundIdentifier getRoundIdentifier() {
return roundIdentifier;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final CommitPayload that = (CommitPayload) o;
return Objects.equals(roundIdentifier, that.roundIdentifier)
&& Objects.equals(digest, that.digest)
&& Objects.equals(commitSeal, that.commitSeal);
}
@Override
public int hashCode() {
return Objects.hash(roundIdentifier, digest, commitSeal);
}
@Override
public String toString() {
return new StringJoiner(", ", CommitPayload.class.getSimpleName() + "[", "]")
.add("roundIdentifier=" + roundIdentifier)
.add("digest=" + digest)
.add("commitSeal=" + commitSeal)
.toString();
}
} }

@ -14,33 +14,6 @@ package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import java.util.Objects; public interface InRoundPayload extends Payload {
ConsensusRoundIdentifier getRoundIdentifier();
public abstract class InRoundPayload extends AbstractPayload {
protected final ConsensusRoundIdentifier roundIdentifier;
protected InRoundPayload(final ConsensusRoundIdentifier roundIdentifier) {
this.roundIdentifier = roundIdentifier;
}
public ConsensusRoundIdentifier getRoundIdentifier() {
return roundIdentifier;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final InRoundPayload that = (InRoundPayload) o;
return Objects.equals(roundIdentifier, that.roundIdentifier);
}
@Override
public int hashCode() {
return Objects.hash(roundIdentifier);
}
} }

@ -77,21 +77,21 @@ public class MessageFactory {
return createSignedMessage(payload); return createSignedMessage(payload);
} }
private <M extends AbstractPayload> SignedData<M> createSignedMessage(final M payload) { private <M extends Payload> SignedData<M> createSignedMessage(final M payload) {
final Signature signature = sign(payload, validatorKeyPair); final Signature signature = sign(payload, validatorKeyPair);
return new SignedData<>( return new SignedData<>(
payload, Util.publicKeyToAddress(validatorKeyPair.getPublicKey()), signature); payload, Util.publicKeyToAddress(validatorKeyPair.getPublicKey()), signature);
} }
public static Hash hashForSignature(final AbstractPayload unsignedMessageData) { public static Hash hashForSignature(final Payload unsignedMessageData) {
return Hash.hash( return Hash.hash(
BytesValues.concatenate( BytesValues.concatenate(
BytesValues.ofUnsignedByte(unsignedMessageData.getMessageType()), BytesValues.ofUnsignedByte(unsignedMessageData.getMessageType()),
unsignedMessageData.encoded())); unsignedMessageData.encoded()));
} }
private static Signature sign(final AbstractPayload unsignedMessageData, final KeyPair nodeKeys) { private static Signature sign(final Payload unsignedMessageData, final KeyPair nodeKeys) {
return SECP256K1.sign(hashForSignature(unsignedMessageData), nodeKeys); return SECP256K1.sign(hashForSignature(unsignedMessageData), nodeKeys);
} }
} }

@ -18,15 +18,13 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
import java.util.Collections; import java.util.Collections;
import java.util.Objects;
import java.util.StringJoiner;
public class NewRoundPayload extends AbstractPayload { public class NewRoundPayload implements Payload {
private static final int TYPE = IbftV2.NEW_ROUND; private static final int TYPE = IbftV2.NEW_ROUND;
private final ConsensusRoundIdentifier roundChangeIdentifier; private final ConsensusRoundIdentifier roundChangeIdentifier;
private final RoundChangeCertificate roundChangeCertificate; private final RoundChangeCertificate roundChangeCertificate;
private final SignedData<ProposalPayload> proposalPayload; private final SignedData<ProposalPayload> proposalPayload;
public NewRoundPayload( public NewRoundPayload(
@ -72,6 +70,34 @@ public class NewRoundPayload extends AbstractPayload {
return new NewRoundPayload(roundIdentifier, roundChangeCertificate, proposalPayload); return new NewRoundPayload(roundIdentifier, roundChangeCertificate, proposalPayload);
} }
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final NewRoundPayload that = (NewRoundPayload) o;
return Objects.equals(roundChangeIdentifier, that.roundChangeIdentifier)
&& Objects.equals(roundChangeCertificate, that.roundChangeCertificate)
&& Objects.equals(proposalPayload, that.proposalPayload);
}
@Override
public int hashCode() {
return Objects.hash(roundChangeIdentifier, roundChangeCertificate, proposalPayload);
}
@Override
public String toString() {
return new StringJoiner(", ", NewRoundPayload.class.getSimpleName() + "[", "]")
.add("roundChangeIdentifier=" + roundChangeIdentifier)
.add("roundChangeCertificate=" + roundChangeCertificate)
.add("proposalPayload=" + proposalPayload)
.toString();
}
@Override @Override
public int getMessageType() { public int getMessageType() {
return TYPE; return TYPE;

@ -18,20 +18,20 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValue;
public abstract class AbstractPayload { public interface Payload {
public abstract void writeTo(final RLPOutput rlpOutput); void writeTo(final RLPOutput rlpOutput);
public BytesValue encoded() { default BytesValue encoded() {
BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
writeTo(rlpOutput); writeTo(rlpOutput);
return rlpOutput.encoded(); return rlpOutput.encoded();
} }
public abstract int getMessageType(); int getMessageType();
protected static Hash readDigest(final RLPInput ibftMessageData) { static Hash readDigest(final RLPInput ibftMessageData) {
return Hash.wrap(ibftMessageData.readBytes32()); return Hash.wrap(ibftMessageData.readBytes32());
} }
} }

@ -18,28 +18,29 @@ import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
public class PreparePayload extends InRoundPayload { import java.util.Objects;
import java.util.StringJoiner;
public class PreparePayload implements InRoundPayload {
private static final int TYPE = IbftV2.PREPARE; private static final int TYPE = IbftV2.PREPARE;
private final ConsensusRoundIdentifier roundIdentifier;
private final Hash digest; private final Hash digest;
public PreparePayload(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { public PreparePayload(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) {
super(roundIdentifier); this.roundIdentifier = roundIdentifier;
this.digest = digest; this.digest = digest;
} }
public static PreparePayload readFrom(final RLPInput rlpInput) { public static PreparePayload readFrom(final RLPInput rlpInput) {
rlpInput.enterList(); rlpInput.enterList();
final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput); final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput);
final Hash digest = readDigest(rlpInput); final Hash digest = Payload.readDigest(rlpInput);
rlpInput.leaveList(); rlpInput.leaveList();
return new PreparePayload(roundIdentifier, digest); return new PreparePayload(roundIdentifier, digest);
} }
@Override @Override
public void writeTo(final RLPOutput rlpOutput) { public void writeTo(final RLPOutput rlpOutput) {
rlpOutput.startList(); rlpOutput.startList();
roundIdentifier.writeTo(rlpOutput); roundIdentifier.writeTo(rlpOutput);
rlpOutput.writeBytesValue(digest); rlpOutput.writeBytesValue(digest);
@ -54,4 +55,35 @@ public class PreparePayload extends InRoundPayload {
public Hash getDigest() { public Hash getDigest() {
return digest; return digest;
} }
@Override
public ConsensusRoundIdentifier getRoundIdentifier() {
return roundIdentifier;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final PreparePayload that = (PreparePayload) o;
return Objects.equals(roundIdentifier, that.roundIdentifier)
&& Objects.equals(digest, that.digest);
}
@Override
public int hashCode() {
return Objects.hash(roundIdentifier, digest);
}
@Override
public String toString() {
return new StringJoiner(", ", PreparePayload.class.getSimpleName() + "[", "]")
.add("roundIdentifier=" + roundIdentifier)
.add("digest=" + digest)
.toString();
}
} }

@ -16,9 +16,10 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
import java.util.Collection; import java.util.Collection;
import java.util.Objects;
import java.util.StringJoiner;
public class PreparedCertificate { public class PreparedCertificate {
private final SignedData<ProposalPayload> proposalPayload; private final SignedData<ProposalPayload> proposalPayload;
private final Collection<SignedData<PreparePayload>> preparePayloads; private final Collection<SignedData<PreparePayload>> preparePayloads;
@ -55,4 +56,30 @@ public class PreparedCertificate {
public Collection<SignedData<PreparePayload>> getPreparePayloads() { public Collection<SignedData<PreparePayload>> getPreparePayloads() {
return preparePayloads; return preparePayloads;
} }
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final PreparedCertificate that = (PreparedCertificate) o;
return Objects.equals(proposalPayload, that.proposalPayload)
&& Objects.equals(preparePayloads, that.preparePayloads);
}
@Override
public int hashCode() {
return Objects.hash(proposalPayload, preparePayloads);
}
@Override
public String toString() {
return new StringJoiner(", ", PreparedCertificate.class.getSimpleName() + "[", "]")
.add("proposalPayload=" + proposalPayload)
.add("preparePayloads=" + preparePayloads)
.toString();
}
} }

@ -19,18 +19,20 @@ import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
public class ProposalPayload extends InRoundPayload { import java.util.Objects;
import java.util.StringJoiner;
public class ProposalPayload implements InRoundPayload {
private static final int TYPE = IbftV2.PROPOSAL; private static final int TYPE = IbftV2.PROPOSAL;
private final ConsensusRoundIdentifier roundIdentifier;
private final Block block; private final Block block;
public ProposalPayload(final ConsensusRoundIdentifier roundIdentifier, final Block block) { public ProposalPayload(final ConsensusRoundIdentifier roundIdentifier, final Block block) {
super(roundIdentifier); this.roundIdentifier = roundIdentifier;
this.block = block; this.block = block;
} }
public static ProposalPayload readFrom(final RLPInput rlpInput) { public static ProposalPayload readFrom(final RLPInput rlpInput) {
rlpInput.enterList(); rlpInput.enterList();
final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput); final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput);
final Block block = final Block block =
@ -42,7 +44,6 @@ public class ProposalPayload extends InRoundPayload {
@Override @Override
public void writeTo(final RLPOutput rlpOutput) { public void writeTo(final RLPOutput rlpOutput) {
rlpOutput.startList(); rlpOutput.startList();
roundIdentifier.writeTo(rlpOutput); roundIdentifier.writeTo(rlpOutput);
block.writeTo(rlpOutput); block.writeTo(rlpOutput);
@ -57,4 +58,35 @@ public class ProposalPayload extends InRoundPayload {
public int getMessageType() { public int getMessageType() {
return TYPE; return TYPE;
} }
@Override
public ConsensusRoundIdentifier getRoundIdentifier() {
return roundIdentifier;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final ProposalPayload that = (ProposalPayload) o;
return Objects.equals(roundIdentifier, that.roundIdentifier)
&& Objects.equals(block, that.block);
}
@Override
public int hashCode() {
return Objects.hash(roundIdentifier, block);
}
@Override
public String toString() {
return new StringJoiner(", ", ProposalPayload.class.getSimpleName() + "[", "]")
.add("roundIdentifier=" + roundIdentifier)
.add("block=" + block)
.toString();
}
} }

@ -17,11 +17,12 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
public class RoundChangeCertificate { public class RoundChangeCertificate {
private final Collection<SignedData<RoundChangePayload>> roundChangePayloads; private final Collection<SignedData<RoundChangePayload>> roundChangePayloads;
public RoundChangeCertificate( public RoundChangeCertificate(
@ -62,4 +63,28 @@ public class RoundChangeCertificate {
return new RoundChangeCertificate(roundChangePayloads); return new RoundChangeCertificate(roundChangePayloads);
} }
} }
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final RoundChangeCertificate that = (RoundChangeCertificate) o;
return Objects.equals(roundChangePayloads, that.roundChangePayloads);
}
@Override
public int hashCode() {
return Objects.hash(roundChangePayloads);
}
@Override
public String toString() {
return new StringJoiner(", ", RoundChangeCertificate.class.getSimpleName() + "[", "]")
.add("roundChangePayloads=" + roundChangePayloads)
.toString();
}
} }

@ -14,16 +14,15 @@ package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.IbftV2; import tech.pegasys.pantheon.consensus.ibft.ibftmessage.IbftV2;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.StringJoiner;
public class RoundChangePayload extends AbstractPayload { public class RoundChangePayload implements Payload {
private static final int TYPE = IbftV2.ROUND_CHANGE;
private static final int TYPE = IbftV2.PREPARE;
private final ConsensusRoundIdentifier roundChangeIdentifier; private final ConsensusRoundIdentifier roundChangeIdentifier;
// The validator may not hae any prepared certificate // The validator may not hae any prepared certificate
@ -47,16 +46,15 @@ public class RoundChangePayload extends AbstractPayload {
@Override @Override
public void writeTo(final RLPOutput rlpOutput) { public void writeTo(final RLPOutput rlpOutput) {
// RLP encode of the message data content (round identifier and prepared certificate) // RLP encode of the message data content (round identifier and prepared certificate)
BytesValueRLPOutput ibftMessage = new BytesValueRLPOutput(); rlpOutput.startList();
ibftMessage.startList(); roundChangeIdentifier.writeTo(rlpOutput);
roundChangeIdentifier.writeTo(ibftMessage);
if (preparedCertificate.isPresent()) { if (preparedCertificate.isPresent()) {
preparedCertificate.get().writeTo(ibftMessage); preparedCertificate.get().writeTo(rlpOutput);
} else { } else {
ibftMessage.writeNull(); rlpOutput.writeNull();
} }
ibftMessage.endList(); rlpOutput.endList();
} }
public static RoundChangePayload readFrom(final RLPInput rlpInput) { public static RoundChangePayload readFrom(final RLPInput rlpInput) {
@ -80,4 +78,30 @@ public class RoundChangePayload extends AbstractPayload {
public int getMessageType() { public int getMessageType() {
return TYPE; return TYPE;
} }
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final RoundChangePayload that = (RoundChangePayload) o;
return Objects.equals(roundChangeIdentifier, that.roundChangeIdentifier)
&& Objects.equals(preparedCertificate, that.preparedCertificate);
}
@Override
public int hashCode() {
return Objects.hash(roundChangeIdentifier, preparedCertificate);
}
@Override
public String toString() {
return new StringJoiner(", ", RoundChangePayload.class.getSimpleName() + "[", "]")
.add("roundChangeIdentifier=" + roundChangeIdentifier)
.add("preparedCertificate=" + preparedCertificate)
.toString();
}
} }

@ -20,8 +20,10 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValue;
public class SignedData<M extends AbstractPayload> { import java.util.Objects;
import java.util.StringJoiner;
public class SignedData<M extends Payload> {
protected final Address sender; protected final Address sender;
protected final Signature signature; protected final Signature signature;
protected final M unsignedPayload; protected final M unsignedPayload;
@ -109,7 +111,7 @@ public class SignedData<M extends AbstractPayload> {
return from(unsignedMessageData, signature); return from(unsignedMessageData, signature);
} }
protected static <M extends AbstractPayload> SignedData<M> from( protected static <M extends Payload> SignedData<M> from(
final M unsignedMessageData, final Signature signature) { final M unsignedMessageData, final Signature signature) {
final Address sender = recoverSender(unsignedMessageData, signature); final Address sender = recoverSender(unsignedMessageData, signature);
@ -122,8 +124,36 @@ public class SignedData<M extends AbstractPayload> {
} }
protected static Address recoverSender( protected static Address recoverSender(
final AbstractPayload unsignedMessageData, final Signature signature) { final Payload unsignedMessageData, final Signature signature) {
return Util.signatureToAddress(signature, MessageFactory.hashForSignature(unsignedMessageData)); return Util.signatureToAddress(signature, MessageFactory.hashForSignature(unsignedMessageData));
} }
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final SignedData<?> that = (SignedData<?>) o;
return Objects.equals(sender, that.sender)
&& Objects.equals(signature, that.signature)
&& Objects.equals(unsignedPayload, that.unsignedPayload);
}
@Override
public int hashCode() {
return Objects.hash(sender, signature, unsignedPayload);
}
@Override
public String toString() {
return new StringJoiner(", ", SignedData.class.getSimpleName() + "[", "]")
.add("sender=" + sender)
.add("signature=" + signature)
.add("unsignedPayload=" + unsignedPayload)
.toString();
}
} }

@ -12,6 +12,18 @@
*/ */
package tech.pegasys.pantheon.consensus.ibft; package tech.pegasys.pantheon.consensus.ibft;
import static java.util.Collections.singletonList;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockDataGenerator;
import tech.pegasys.pantheon.ethereum.core.BlockDataGenerator.BlockOptions;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.Optional;
import com.google.common.collect.Lists;
public class TestHelpers { public class TestHelpers {
public static ConsensusRoundIdentifier createFrom( public static ConsensusRoundIdentifier createFrom(
@ -19,4 +31,20 @@ public class TestHelpers {
return new ConsensusRoundIdentifier( return new ConsensusRoundIdentifier(
initial.getSequenceNumber() + offSetSequence, initial.getRoundNumber() + offSetRound); initial.getSequenceNumber() + offSetSequence, initial.getRoundNumber() + offSetRound);
} }
public static Block createProposalBlock() {
final BytesValue extraData =
new IbftExtraData(
BytesValue.wrap(new byte[32]),
Lists.newArrayList(),
Optional.empty(),
0,
singletonList(Address.fromHexString(String.format("%020d", 1))))
.encode();
final BlockOptions blockOptions =
BlockOptions.create()
.setExtraData(extraData)
.setBlockHashFunction(IbftBlockHashing::calculateDataHashForCommittedSeal);
return new BlockDataGenerator().block(blockOptions);
}
} }

@ -0,0 +1,50 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.IbftV2;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import java.math.BigInteger;
import org.junit.Test;
public class CommitPayloadTest {
private static final ConsensusRoundIdentifier ROUND_IDENTIFIER =
new ConsensusRoundIdentifier(0x1234567890ABCDEFL, 0xFEDCBA98);
@Test
public void roundTripRlp() {
final Signature signature = Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0);
final Hash hash = Hash.fromHexStringLenient("0x8523ba6e7c5f59ae87");
final CommitPayload expectedCommitPayload =
new CommitPayload(ROUND_IDENTIFIER, hash, signature);
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
expectedCommitPayload.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
final CommitPayload commitPayload = CommitPayload.readFrom(rlpInput);
assertThat(commitPayload.getRoundIdentifier()).isEqualTo(ROUND_IDENTIFIER);
assertThat(commitPayload.getCommitSeal()).isEqualTo(signature);
assertThat(commitPayload.getDigest()).isEqualTo(hash);
assertThat(commitPayload.getMessageType()).isEqualTo(IbftV2.COMMIT);
}
}

@ -0,0 +1,93 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.TestHelpers;
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.IbftV2;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import java.math.BigInteger;
import java.util.Collections;
import java.util.Optional;
import org.assertj.core.util.Lists;
import org.junit.Test;
public class NewRoundPayloadTest {
private static final ConsensusRoundIdentifier ROUND_IDENTIFIER =
new ConsensusRoundIdentifier(0x1234567890ABCDEFL, 0xFEDCBA98);
@Test
public void roundTripRlpWithNoRoundChangePayloads() {
final Block block = TestHelpers.createProposalBlock();
final ProposalPayload proposalPayload = new ProposalPayload(ROUND_IDENTIFIER, block);
final Signature signature = Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0);
final SignedData<ProposalPayload> proposalPayloadSignedData =
SignedData.from(proposalPayload, signature);
final RoundChangeCertificate roundChangeCertificate =
new RoundChangeCertificate(Collections.emptyList());
final NewRoundPayload expectedNewRoundPayload =
new NewRoundPayload(ROUND_IDENTIFIER, roundChangeCertificate, proposalPayloadSignedData);
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
expectedNewRoundPayload.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
final NewRoundPayload newRoundPayload = NewRoundPayload.readFrom(rlpInput);
assertThat(newRoundPayload.getProposalPayload()).isEqualTo(proposalPayloadSignedData);
assertThat(newRoundPayload.getRoundChangeCertificate()).isEqualTo(roundChangeCertificate);
assertThat(newRoundPayload.getRoundChangeIdentifier()).isEqualTo(ROUND_IDENTIFIER);
assertThat(newRoundPayload.getMessageType()).isEqualTo(IbftV2.NEW_ROUND);
}
@Test
public void roundTripRlpWithRoundChangePayloads() {
final Block block = TestHelpers.createProposalBlock();
final ProposalPayload proposalPayload = new ProposalPayload(ROUND_IDENTIFIER, block);
final Signature signature = Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0);
final SignedData<ProposalPayload> signedProposal = SignedData.from(proposalPayload, signature);
final PreparePayload preparePayload =
new PreparePayload(ROUND_IDENTIFIER, Hash.fromHexStringLenient("0x8523ba6e7c5f59ae87"));
final SignedData<PreparePayload> signedPrepare = SignedData.from(preparePayload, signature);
final PreparedCertificate preparedCert =
new PreparedCertificate(signedProposal, Lists.newArrayList(signedPrepare));
final RoundChangePayload roundChangePayload =
new RoundChangePayload(ROUND_IDENTIFIER, Optional.of(preparedCert));
SignedData<RoundChangePayload> signedRoundChange =
SignedData.from(roundChangePayload, signature);
final RoundChangeCertificate roundChangeCertificate =
new RoundChangeCertificate(Lists.list(signedRoundChange));
final NewRoundPayload expectedNewRoundPayload =
new NewRoundPayload(ROUND_IDENTIFIER, roundChangeCertificate, signedProposal);
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
expectedNewRoundPayload.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
final NewRoundPayload newRoundPayload = NewRoundPayload.readFrom(rlpInput);
assertThat(newRoundPayload.getProposalPayload()).isEqualTo(signedProposal);
assertThat(newRoundPayload.getRoundChangeCertificate()).isEqualTo(roundChangeCertificate);
assertThat(newRoundPayload.getRoundChangeIdentifier()).isEqualTo(ROUND_IDENTIFIER);
assertThat(newRoundPayload.getMessageType()).isEqualTo(IbftV2.NEW_ROUND);
}
}

@ -0,0 +1,48 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.IbftV2;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import org.junit.Test;
public class PreparePayloadTest {
@Test
public void roundTripRlp() {
final Hash digest = Hash.hash(BytesValue.of(1));
final ConsensusRoundIdentifier expectedRoundIdentifier = new ConsensusRoundIdentifier(1, 1);
final PreparePayload preparePayload = new PreparePayload(expectedRoundIdentifier, digest);
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
preparePayload.writeTo(rlpOutput);
final RLPInput rlpInput = RLP.input(rlpOutput.encoded());
final PreparePayload actualPreparePayload = PreparePayload.readFrom(rlpInput);
final ConsensusRoundIdentifier actualConsensusRoundIdentifier =
actualPreparePayload.getRoundIdentifier();
final Hash actualDigest = actualPreparePayload.getDigest();
assertThat(actualConsensusRoundIdentifier)
.isEqualToComparingFieldByField(expectedRoundIdentifier);
assertThat(actualDigest).isEqualTo(digest);
assertThat(actualPreparePayload.getMessageType()).isEqualTo(IbftV2.PREPARE);
}
}

@ -0,0 +1,82 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.TestHelpers;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import org.assertj.core.util.Lists;
import org.junit.Test;
public class PreparedCertificateTest {
private static final ConsensusRoundIdentifier ROUND_IDENTIFIER =
new ConsensusRoundIdentifier(0x1234567890ABCDEFL, 0xFEDCBA98);
@Test
public void roundTripRlpWithNoPreparePayloads() {
final SignedData<ProposalPayload> signedProposalPayload = signedProposal();
final Collection<SignedData<PreparePayload>> preparePayloads = Collections.emptyList();
final PreparedCertificate preparedCert =
new PreparedCertificate(signedProposalPayload, preparePayloads);
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
preparedCert.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
PreparedCertificate actualPreparedCert = PreparedCertificate.readFrom(rlpInput);
assertThat(actualPreparedCert.getPreparePayloads())
.isEqualTo(preparedCert.getPreparePayloads());
assertThat(actualPreparedCert.getProposalPayload())
.isEqualTo(preparedCert.getProposalPayload());
}
@Test
public void roundTripRlpWithPreparePayload() {
final SignedData<ProposalPayload> signedProposalPayload = signedProposal();
final PreparePayload preparePayload =
new PreparePayload(ROUND_IDENTIFIER, Hash.fromHexStringLenient("0x8523ba6e7c5f59ae87"));
final Signature signature = Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0);
final SignedData<PreparePayload> signedPrepare = SignedData.from(preparePayload, signature);
final PreparedCertificate preparedCert =
new PreparedCertificate(signedProposalPayload, Lists.newArrayList(signedPrepare));
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
preparedCert.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
PreparedCertificate actualPreparedCert = PreparedCertificate.readFrom(rlpInput);
assertThat(actualPreparedCert.getPreparePayloads())
.isEqualTo(preparedCert.getPreparePayloads());
assertThat(actualPreparedCert.getProposalPayload())
.isEqualTo(preparedCert.getProposalPayload());
}
private SignedData<ProposalPayload> signedProposal() {
final Block block = TestHelpers.createProposalBlock();
final ProposalPayload proposalPayload = new ProposalPayload(ROUND_IDENTIFIER, block);
final Signature signature = Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0);
return SignedData.from(proposalPayload, signature);
}
}

@ -0,0 +1,44 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.TestHelpers;
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.IbftV2;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import org.junit.Test;
public class ProposalPayloadTest {
private static final ConsensusRoundIdentifier ROUND_IDENTIFIER =
new ConsensusRoundIdentifier(0x1234567890ABCDEFL, 0xFEDCBA98);
@Test
public void roundTripRlp() {
final Block block = TestHelpers.createProposalBlock();
final ProposalPayload expectedProposalPayload = new ProposalPayload(ROUND_IDENTIFIER, block);
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
expectedProposalPayload.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
final ProposalPayload proposalPayload = ProposalPayload.readFrom(rlpInput);
assertThat(proposalPayload.getRoundIdentifier()).isEqualTo(ROUND_IDENTIFIER);
assertThat(proposalPayload.getBlock()).isEqualTo(block);
assertThat(proposalPayload.getMessageType()).isEqualTo(IbftV2.PROPOSAL);
}
}

@ -0,0 +1,79 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.TestHelpers;
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.IbftV2;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import java.math.BigInteger;
import java.util.Optional;
import org.assertj.core.util.Lists;
import org.junit.Test;
public class RoundChangeCertificateTest {
private static final ConsensusRoundIdentifier ROUND_IDENTIFIER =
new ConsensusRoundIdentifier(0x1234567890ABCDEFL, 0xFEDCBA98);
@Test
public void rlpRoundTripWithNoPreparedCertificate() {
final RoundChangePayload roundChangePayload =
new RoundChangePayload(ROUND_IDENTIFIER, Optional.empty());
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
roundChangePayload.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
RoundChangePayload actualRoundChangePayload = RoundChangePayload.readFrom(rlpInput);
assertThat(actualRoundChangePayload.getPreparedCertificate()).isEqualTo(Optional.empty());
assertThat(actualRoundChangePayload.getRoundChangeIdentifier()).isEqualTo(ROUND_IDENTIFIER);
assertThat(actualRoundChangePayload.getMessageType()).isEqualTo(IbftV2.ROUND_CHANGE);
}
@Test
public void rlpRoundTripWithPreparedCertificate() {
final Block block = TestHelpers.createProposalBlock();
final ProposalPayload proposalPayload = new ProposalPayload(ROUND_IDENTIFIER, block);
final Signature signature = Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0);
SignedData<ProposalPayload> signedProposal = SignedData.from(proposalPayload, signature);
final PreparePayload preparePayload =
new PreparePayload(ROUND_IDENTIFIER, Hash.fromHexStringLenient("0x8523ba6e7c5f59ae87"));
final SignedData<PreparePayload> signedPrepare = SignedData.from(preparePayload, signature);
final PreparedCertificate preparedCert =
new PreparedCertificate(signedProposal, Lists.newArrayList(signedPrepare));
final RoundChangePayload roundChangePayload =
new RoundChangePayload(ROUND_IDENTIFIER, Optional.of(preparedCert));
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
roundChangePayload.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
RoundChangePayload actualRoundChangePayload = RoundChangePayload.readFrom(rlpInput);
assertThat(actualRoundChangePayload.getPreparedCertificate())
.isEqualTo(Optional.of(preparedCert));
assertThat(actualRoundChangePayload.getRoundChangeIdentifier()).isEqualTo(ROUND_IDENTIFIER);
assertThat(actualRoundChangePayload.getMessageType()).isEqualTo(IbftV2.ROUND_CHANGE);
}
}

@ -0,0 +1,103 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata;
import static java.util.Optional.empty;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.TestHelpers;
import tech.pegasys.pantheon.consensus.ibft.ibftmessage.IbftV2;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
import java.math.BigInteger;
import java.util.Collections;
import java.util.Optional;
import org.assertj.core.util.Lists;
import org.junit.Test;
public class RoundChangePayloadTest {
private static final ConsensusRoundIdentifier ROUND_IDENTIFIER =
new ConsensusRoundIdentifier(0x1234567890ABCDEFL, 0xFEDCBA98);
private static final Signature SIGNATURE =
Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0);
@Test
public void roundTripRlpWithNoPreparedCertificate() {
final RoundChangePayload roundChangePayload = new RoundChangePayload(ROUND_IDENTIFIER, empty());
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
roundChangePayload.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
RoundChangePayload actualRoundChangePayload = RoundChangePayload.readFrom(rlpInput);
assertThat(actualRoundChangePayload.getRoundChangeIdentifier()).isEqualTo(ROUND_IDENTIFIER);
assertThat(actualRoundChangePayload.getPreparedCertificate()).isEqualTo(Optional.empty());
assertThat(actualRoundChangePayload.getMessageType()).isEqualTo(IbftV2.ROUND_CHANGE);
}
@Test
public void roundTripRlpWithEmptyPreparedCertificate() {
final SignedData<ProposalPayload> signedProposal = signedProposal();
final PreparedCertificate preparedCertificate =
new PreparedCertificate(signedProposal, Collections.emptyList());
final RoundChangePayload roundChangePayload =
new RoundChangePayload(ROUND_IDENTIFIER, Optional.of(preparedCertificate));
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
roundChangePayload.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
RoundChangePayload actualRoundChangePayload = RoundChangePayload.readFrom(rlpInput);
assertThat(actualRoundChangePayload.getRoundChangeIdentifier()).isEqualTo(ROUND_IDENTIFIER);
assertThat(actualRoundChangePayload.getPreparedCertificate())
.isEqualTo(Optional.of(preparedCertificate));
assertThat(actualRoundChangePayload.getMessageType()).isEqualTo(IbftV2.ROUND_CHANGE);
}
@Test
public void roundTripRlpWithPreparedCertificate() {
final SignedData<ProposalPayload> signedProposal = signedProposal();
final PreparePayload preparePayload =
new PreparePayload(ROUND_IDENTIFIER, Hash.fromHexStringLenient("0x8523ba6e7c5f59ae87"));
final SignedData<PreparePayload> signedPrepare = SignedData.from(preparePayload, SIGNATURE);
final PreparedCertificate preparedCert =
new PreparedCertificate(signedProposal, Lists.newArrayList(signedPrepare));
final RoundChangePayload roundChangePayload =
new RoundChangePayload(ROUND_IDENTIFIER, Optional.of(preparedCert));
final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
roundChangePayload.writeTo(rlpOut);
final RLPInput rlpInput = RLP.input(rlpOut.encoded());
RoundChangePayload actualRoundChangePayload = RoundChangePayload.readFrom(rlpInput);
assertThat(actualRoundChangePayload.getRoundChangeIdentifier()).isEqualTo(ROUND_IDENTIFIER);
assertThat(actualRoundChangePayload.getPreparedCertificate())
.isEqualTo(Optional.of(preparedCert));
assertThat(actualRoundChangePayload.getMessageType()).isEqualTo(IbftV2.ROUND_CHANGE);
}
private SignedData<ProposalPayload> signedProposal() {
final Block block = TestHelpers.createProposalBlock();
final ProposalPayload proposalPayload = new ProposalPayload(ROUND_IDENTIFIER, block);
final Signature signature = Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0);
return SignedData.from(proposalPayload, signature);
}
}

@ -167,10 +167,10 @@ public class BlockDataGenerator {
.gasLimit(gasLimit) .gasLimit(gasLimit)
.gasUsed(gasUsed) .gasUsed(gasUsed)
.timestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).getEpochSecond()) .timestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).getEpochSecond())
.extraData(bytes32()) .extraData(options.getExtraData(bytes32()))
.mixHash(hash()) .mixHash(hash())
.nonce(blockNonce) .nonce(blockNonce)
.blockHashFunction(MainnetBlockHashFunction::createHash) .blockHashFunction(options.getBlockHashFunction(MainnetBlockHashFunction::createHash))
.buildBlockHeader(); .buildBlockHeader();
} }
@ -308,6 +308,8 @@ public class BlockDataGenerator {
private Optional<Hash> stateRoot = Optional.empty(); private Optional<Hash> stateRoot = Optional.empty();
private Optional<UInt256> difficulty = Optional.empty(); private Optional<UInt256> difficulty = Optional.empty();
private Optional<List<Transaction>> transactions = Optional.empty(); private Optional<List<Transaction>> transactions = Optional.empty();
private Optional<BytesValue> extraData = Optional.empty();
private Optional<BlockHashFunction> blockHashFunction = Optional.empty();
public static BlockOptions create() { public static BlockOptions create() {
return new BlockOptions(); return new BlockOptions();
@ -333,6 +335,14 @@ public class BlockDataGenerator {
return difficulty.orElse(defaultValue); return difficulty.orElse(defaultValue);
} }
public BytesValue getExtraData(final Bytes32 defaultValue) {
return extraData.orElse(defaultValue);
}
public BlockHashFunction getBlockHashFunction(final BlockHashFunction defaultValue) {
return blockHashFunction.orElse(defaultValue);
}
public BlockOptions addTransaction(final Transaction... tx) { public BlockOptions addTransaction(final Transaction... tx) {
if (!transactions.isPresent()) { if (!transactions.isPresent()) {
transactions = Optional.of(new ArrayList<>()); transactions = Optional.of(new ArrayList<>());
@ -360,5 +370,15 @@ public class BlockDataGenerator {
this.difficulty = Optional.of(difficulty); this.difficulty = Optional.of(difficulty);
return this; return this;
} }
public BlockOptions setExtraData(final BytesValue extraData) {
this.extraData = Optional.of(extraData);
return this;
}
public BlockOptions setBlockHashFunction(final BlockHashFunction blockHashFunction) {
this.blockHashFunction = Optional.of(blockHashFunction);
return this;
}
} }
} }

Loading…
Cancel
Save