From 4bfb381b34e699e477f7f122c7cc8da8c7778808 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Wed, 12 Dec 2018 16:26:04 +1000 Subject: [PATCH] IBFT message payload tests (#404) --- .../ibft/ibftmessagedata/CommitPayload.java | 45 +++++++- .../ibft/ibftmessagedata/InRoundPayload.java | 31 +----- .../ibft/ibftmessagedata/MessageFactory.java | 6 +- .../ibft/ibftmessagedata/NewRoundPayload.java | 36 +++++- .../{AbstractPayload.java => Payload.java} | 10 +- .../ibft/ibftmessagedata/PreparePayload.java | 44 +++++++- .../ibftmessagedata/PreparedCertificate.java | 29 ++++- .../ibft/ibftmessagedata/ProposalPayload.java | 40 ++++++- .../RoundChangeCertificate.java | 27 ++++- .../ibftmessagedata/RoundChangePayload.java | 46 ++++++-- .../ibft/ibftmessagedata/SignedData.java | 36 +++++- .../pantheon/consensus/ibft/TestHelpers.java | 28 +++++ .../ibftmessagedata/CommitPayloadTest.java | 50 +++++++++ .../ibftmessagedata/NewRoundPayloadTest.java | 93 ++++++++++++++++ .../ibftmessagedata/PreparePayloadTest.java | 48 ++++++++ .../PreparedCertificateTest.java | 82 ++++++++++++++ .../ibftmessagedata/ProposalPayloadTest.java | 44 ++++++++ .../RoundChangeCertificateTest.java | 79 ++++++++++++++ .../RoundChangePayloadTest.java | 103 ++++++++++++++++++ .../ethereum/core/BlockDataGenerator.java | 24 +++- 20 files changed, 826 insertions(+), 75 deletions(-) rename consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/{AbstractPayload.java => Payload.java} (82%) create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayloadTest.java create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayloadTest.java create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayloadTest.java create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificateTest.java create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayloadTest.java create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificateTest.java create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayloadTest.java diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayload.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayload.java index e7c48e1594..b756ca7346 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayload.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayload.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.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 final ConsensusRoundIdentifier roundIdentifier; private final Hash digest; private final Signature commitSeal; @@ -28,16 +32,15 @@ public class CommitPayload extends InRoundPayload { final ConsensusRoundIdentifier roundIdentifier, final Hash digest, final Signature commitSeal) { - super(roundIdentifier); + this.roundIdentifier = roundIdentifier; this.digest = digest; this.commitSeal = commitSeal; } public static CommitPayload readFrom(final RLPInput rlpInput) { - rlpInput.enterList(); final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput); - final Hash digest = readDigest(rlpInput); + final Hash digest = Payload.readDigest(rlpInput); final Signature commitSeal = rlpInput.readBytesValue(Signature::decode); rlpInput.leaveList(); @@ -46,7 +49,6 @@ public class CommitPayload extends InRoundPayload { @Override public void writeTo(final RLPOutput rlpOutput) { - rlpOutput.startList(); roundIdentifier.writeTo(rlpOutput); rlpOutput.writeBytesValue(digest); @@ -66,4 +68,37 @@ public class CommitPayload extends InRoundPayload { public Signature getCommitSeal() { 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(); + } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/InRoundPayload.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/InRoundPayload.java index 747145cdf0..be6ae9a8fe 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/InRoundPayload.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/InRoundPayload.java @@ -14,33 +14,6 @@ package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; -import java.util.Objects; - -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); - } +public interface InRoundPayload extends Payload { + ConsensusRoundIdentifier getRoundIdentifier(); } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/MessageFactory.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/MessageFactory.java index 6019727308..0bea2f6f3b 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/MessageFactory.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/MessageFactory.java @@ -77,21 +77,21 @@ public class MessageFactory { return createSignedMessage(payload); } - private SignedData createSignedMessage(final M payload) { + private SignedData createSignedMessage(final M payload) { final Signature signature = sign(payload, validatorKeyPair); return new SignedData<>( payload, Util.publicKeyToAddress(validatorKeyPair.getPublicKey()), signature); } - public static Hash hashForSignature(final AbstractPayload unsignedMessageData) { + public static Hash hashForSignature(final Payload unsignedMessageData) { return Hash.hash( BytesValues.concatenate( BytesValues.ofUnsignedByte(unsignedMessageData.getMessageType()), 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); } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayload.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayload.java index 70a66b6560..d582eb2be3 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayload.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayload.java @@ -18,15 +18,13 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; 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 final ConsensusRoundIdentifier roundChangeIdentifier; - private final RoundChangeCertificate roundChangeCertificate; - private final SignedData proposalPayload; public NewRoundPayload( @@ -72,6 +70,34 @@ public class NewRoundPayload extends AbstractPayload { 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 public int getMessageType() { return TYPE; diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/AbstractPayload.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/Payload.java similarity index 82% rename from consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/AbstractPayload.java rename to consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/Payload.java index e8f9a566b3..f7291ba9e7 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/AbstractPayload.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/Payload.java @@ -18,20 +18,20 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; 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(); writeTo(rlpOutput); 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()); } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayload.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayload.java index 02310b929b..f550ddf904 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayload.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayload.java @@ -18,28 +18,29 @@ import tech.pegasys.pantheon.ethereum.core.Hash; import tech.pegasys.pantheon.ethereum.rlp.RLPInput; 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 final ConsensusRoundIdentifier roundIdentifier; private final Hash digest; public PreparePayload(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { - super(roundIdentifier); + this.roundIdentifier = roundIdentifier; this.digest = digest; } public static PreparePayload readFrom(final RLPInput rlpInput) { - rlpInput.enterList(); final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput); - final Hash digest = readDigest(rlpInput); + final Hash digest = Payload.readDigest(rlpInput); rlpInput.leaveList(); - return new PreparePayload(roundIdentifier, digest); } @Override public void writeTo(final RLPOutput rlpOutput) { - rlpOutput.startList(); roundIdentifier.writeTo(rlpOutput); rlpOutput.writeBytesValue(digest); @@ -54,4 +55,35 @@ public class PreparePayload extends InRoundPayload { public Hash getDigest() { 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(); + } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificate.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificate.java index 796952a450..7fab141a6d 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificate.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificate.java @@ -16,9 +16,10 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import java.util.Collection; +import java.util.Objects; +import java.util.StringJoiner; public class PreparedCertificate { - private final SignedData proposalPayload; private final Collection> preparePayloads; @@ -55,4 +56,30 @@ public class PreparedCertificate { public Collection> getPreparePayloads() { 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(); + } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayload.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayload.java index b82490f39a..314c092eac 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayload.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayload.java @@ -19,18 +19,20 @@ import tech.pegasys.pantheon.ethereum.core.Block; import tech.pegasys.pantheon.ethereum.rlp.RLPInput; 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 final ConsensusRoundIdentifier roundIdentifier; private final Block block; public ProposalPayload(final ConsensusRoundIdentifier roundIdentifier, final Block block) { - super(roundIdentifier); + this.roundIdentifier = roundIdentifier; this.block = block; } public static ProposalPayload readFrom(final RLPInput rlpInput) { - rlpInput.enterList(); final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput); final Block block = @@ -42,7 +44,6 @@ public class ProposalPayload extends InRoundPayload { @Override public void writeTo(final RLPOutput rlpOutput) { - rlpOutput.startList(); roundIdentifier.writeTo(rlpOutput); block.writeTo(rlpOutput); @@ -57,4 +58,35 @@ public class ProposalPayload extends InRoundPayload { public int getMessageType() { 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(); + } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificate.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificate.java index 7e0d38ed27..dab2d88868 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificate.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificate.java @@ -17,11 +17,12 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import java.util.Collection; import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; import com.google.common.collect.Lists; public class RoundChangeCertificate { - private final Collection> roundChangePayloads; public RoundChangeCertificate( @@ -62,4 +63,28 @@ public class RoundChangeCertificate { 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(); + } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayload.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayload.java index 317c2b6e43..7ed5dfd5e1 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayload.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayload.java @@ -14,16 +14,15 @@ package tech.pegasys.pantheon.consensus.ibft.ibftmessagedata; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; 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.RLPOutput; +import java.util.Objects; import java.util.Optional; +import java.util.StringJoiner; -public class RoundChangePayload extends AbstractPayload { - - private static final int TYPE = IbftV2.PREPARE; - +public class RoundChangePayload implements Payload { + private static final int TYPE = IbftV2.ROUND_CHANGE; private final ConsensusRoundIdentifier roundChangeIdentifier; // The validator may not hae any prepared certificate @@ -47,16 +46,15 @@ public class RoundChangePayload extends AbstractPayload { @Override public void writeTo(final RLPOutput rlpOutput) { // RLP encode of the message data content (round identifier and prepared certificate) - BytesValueRLPOutput ibftMessage = new BytesValueRLPOutput(); - ibftMessage.startList(); - roundChangeIdentifier.writeTo(ibftMessage); + rlpOutput.startList(); + roundChangeIdentifier.writeTo(rlpOutput); if (preparedCertificate.isPresent()) { - preparedCertificate.get().writeTo(ibftMessage); + preparedCertificate.get().writeTo(rlpOutput); } else { - ibftMessage.writeNull(); + rlpOutput.writeNull(); } - ibftMessage.endList(); + rlpOutput.endList(); } public static RoundChangePayload readFrom(final RLPInput rlpInput) { @@ -80,4 +78,30 @@ public class RoundChangePayload extends AbstractPayload { public int getMessageType() { 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(); + } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/SignedData.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/SignedData.java index 6d8bb1a26f..efae9e062e 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/SignedData.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/SignedData.java @@ -20,8 +20,10 @@ import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; import tech.pegasys.pantheon.util.bytes.BytesValue; -public class SignedData { +import java.util.Objects; +import java.util.StringJoiner; +public class SignedData { protected final Address sender; protected final Signature signature; protected final M unsignedPayload; @@ -109,7 +111,7 @@ public class SignedData { return from(unsignedMessageData, signature); } - protected static SignedData from( + protected static SignedData from( final M unsignedMessageData, final Signature signature) { final Address sender = recoverSender(unsignedMessageData, signature); @@ -122,8 +124,36 @@ public class SignedData { } protected static Address recoverSender( - final AbstractPayload unsignedMessageData, final Signature signature) { + final Payload unsignedMessageData, final Signature signature) { 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(); + } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TestHelpers.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TestHelpers.java index 2fb8d9f37d..f868903165 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TestHelpers.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TestHelpers.java @@ -12,6 +12,18 @@ */ 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 static ConsensusRoundIdentifier createFrom( @@ -19,4 +31,20 @@ public class TestHelpers { return new ConsensusRoundIdentifier( 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); + } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayloadTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayloadTest.java new file mode 100644 index 0000000000..7578fd3982 --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/CommitPayloadTest.java @@ -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); + } +} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayloadTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayloadTest.java new file mode 100644 index 0000000000..a7f13f42df --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/NewRoundPayloadTest.java @@ -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 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 signedProposal = SignedData.from(proposalPayload, signature); + + final PreparePayload preparePayload = + new PreparePayload(ROUND_IDENTIFIER, Hash.fromHexStringLenient("0x8523ba6e7c5f59ae87")); + final SignedData 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 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); + } +} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayloadTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayloadTest.java new file mode 100644 index 0000000000..150ef95d2c --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparePayloadTest.java @@ -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); + } +} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificateTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificateTest.java new file mode 100644 index 0000000000..73010248b2 --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/PreparedCertificateTest.java @@ -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 signedProposalPayload = signedProposal(); + final Collection> 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 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 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 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); + } +} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayloadTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayloadTest.java new file mode 100644 index 0000000000..d30e25d016 --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/ProposalPayloadTest.java @@ -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); + } +} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificateTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificateTest.java new file mode 100644 index 0000000000..1a198b4b19 --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangeCertificateTest.java @@ -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 signedProposal = SignedData.from(proposalPayload, signature); + + final PreparePayload preparePayload = + new PreparePayload(ROUND_IDENTIFIER, Hash.fromHexStringLenient("0x8523ba6e7c5f59ae87")); + final SignedData 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); + } +} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayloadTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayloadTest.java new file mode 100644 index 0000000000..d8dc244025 --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/ibftmessagedata/RoundChangePayloadTest.java @@ -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 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 signedProposal = signedProposal(); + + final PreparePayload preparePayload = + new PreparePayload(ROUND_IDENTIFIER, Hash.fromHexStringLenient("0x8523ba6e7c5f59ae87")); + final SignedData 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 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); + } +} diff --git a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java index 0fcc9c54fe..0b9bde7bc4 100644 --- a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java @@ -167,10 +167,10 @@ public class BlockDataGenerator { .gasLimit(gasLimit) .gasUsed(gasUsed) .timestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).getEpochSecond()) - .extraData(bytes32()) + .extraData(options.getExtraData(bytes32())) .mixHash(hash()) .nonce(blockNonce) - .blockHashFunction(MainnetBlockHashFunction::createHash) + .blockHashFunction(options.getBlockHashFunction(MainnetBlockHashFunction::createHash)) .buildBlockHeader(); } @@ -308,6 +308,8 @@ public class BlockDataGenerator { private Optional stateRoot = Optional.empty(); private Optional difficulty = Optional.empty(); private Optional> transactions = Optional.empty(); + private Optional extraData = Optional.empty(); + private Optional blockHashFunction = Optional.empty(); public static BlockOptions create() { return new BlockOptions(); @@ -333,6 +335,14 @@ public class BlockDataGenerator { 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) { if (!transactions.isPresent()) { transactions = Optional.of(new ArrayList<>()); @@ -360,5 +370,15 @@ public class BlockDataGenerator { this.difficulty = Optional.of(difficulty); 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; + } } }