diff --git a/consensus/qbft/build.gradle b/consensus/qbft/build.gradle new file mode 100644 index 0000000000..2d890b0ff2 --- /dev/null +++ b/consensus/qbft/build.gradle @@ -0,0 +1,71 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'java-library' + +jar { + archiveBaseName = 'besu-qbft' + manifest { + attributes( + 'Specification-Title': archiveBaseName, + 'Specification-Version': project.version, + 'Implementation-Title': archiveBaseName, + 'Implementation-Version': calculateVersion() + ) + } +} + +dependencies { + implementation project(':config') + implementation project(':consensus:common') + implementation project(':crypto') + implementation project(':ethereum:api') + implementation project(':ethereum:blockcreation') + implementation project(':ethereum:core') + implementation project(':ethereum:eth') + implementation project(':ethereum:p2p') + implementation project(':ethereum:rlp') + implementation project(':services:kvstore') + + implementation 'com.google.guava:guava' + implementation 'io.vertx:vertx-core' + implementation 'org.apache.tuweni:bytes' + implementation 'org.apache.tuweni:units' + + integrationTestImplementation project(path: ':config', configuration: 'testSupportArtifacts') + integrationTestImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts') + + testImplementation 'junit:junit' + testImplementation 'org.awaitility:awaitility' + testImplementation 'org.assertj:assertj-core' + testImplementation 'org.mockito:mockito-core' + testImplementation project(path: ':crypto', configuration: 'testSupportArtifacts') + testImplementation project(path: ':config', configuration: 'testSupportArtifacts') + testImplementation project(path: ':consensus:common', configuration: 'testArtifacts') + testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts') + testImplementation project(':metrics:core') + testImplementation project(':testutil') + testImplementation project(':crypto') + + integrationTestImplementation project(path: ':crypto', configuration: 'testSupportArtifacts') + integrationTestImplementation project(':metrics:core') + integrationTestImplementation project(':testutil') + + integrationTestImplementation 'junit:junit' + integrationTestImplementation 'org.assertj:assertj-core' + integrationTestImplementation 'org.mockito:mockito-core' + + testSupportImplementation 'org.mockito:mockito-core' +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagedata/CommitMessageData.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagedata/CommitMessageData.java new file mode 100644 index 0000000000..e1391acb7f --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagedata/CommitMessageData.java @@ -0,0 +1,48 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.messagedata; + +import org.hyperledger.besu.consensus.common.bft.messagedata.AbstractBftMessageData; +import org.hyperledger.besu.consensus.qbft.messagewrappers.Commit; +import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; + +import org.apache.tuweni.bytes.Bytes; + +public class CommitMessageData extends AbstractBftMessageData { + + private static final int MESSAGE_CODE = QbftV1.COMMIT; + + private CommitMessageData(final Bytes data) { + super(data); + } + + public static CommitMessageData fromMessageData(final MessageData messageData) { + return fromMessageData( + messageData, MESSAGE_CODE, CommitMessageData.class, CommitMessageData::new); + } + + public Commit decode() { + return Commit.decode(data); + } + + public static CommitMessageData create(final Commit commit) { + return new CommitMessageData(commit.encode()); + } + + @Override + public int getCode() { + return MESSAGE_CODE; + } +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagedata/PrepareMessageData.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagedata/PrepareMessageData.java new file mode 100644 index 0000000000..695cd704b2 --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagedata/PrepareMessageData.java @@ -0,0 +1,48 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.messagedata; + +import org.hyperledger.besu.consensus.common.bft.messagedata.AbstractBftMessageData; +import org.hyperledger.besu.consensus.qbft.messagewrappers.Prepare; +import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; + +import org.apache.tuweni.bytes.Bytes; + +public class PrepareMessageData extends AbstractBftMessageData { + + private static final int MESSAGE_CODE = QbftV1.PREPARE; + + private PrepareMessageData(final Bytes data) { + super(data); + } + + public static PrepareMessageData fromMessageData(final MessageData messageData) { + return fromMessageData( + messageData, MESSAGE_CODE, PrepareMessageData.class, PrepareMessageData::new); + } + + public Prepare decode() { + return Prepare.decode(data); + } + + public static PrepareMessageData create(final Prepare preapare) { + return new PrepareMessageData(preapare.encode()); + } + + @Override + public int getCode() { + return MESSAGE_CODE; + } +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagedata/QbftV1.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagedata/QbftV1.java new file mode 100644 index 0000000000..cd9c66dd41 --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagedata/QbftV1.java @@ -0,0 +1,25 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.messagedata; + +/** Message codes for QBFT v1 messages */ +public class QbftV1 { + public static final int PROPOSAL = 0; + public static final int PREPARE = 1; + public static final int COMMIT = 2; + public static final int ROUND_CHANGE = 3; + + public static final int MESSAGE_SPACE = 4; +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagewrappers/Commit.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagewrappers/Commit.java new file mode 100644 index 0000000000..4a419ea868 --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagewrappers/Commit.java @@ -0,0 +1,44 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.messagewrappers; + +import org.hyperledger.besu.consensus.common.bft.messagewrappers.BftMessage; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.consensus.qbft.payload.CommitPayload; +import org.hyperledger.besu.consensus.qbft.payload.PayloadDeserializers; +import org.hyperledger.besu.crypto.SECP256K1.Signature; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.rlp.RLP; + +import org.apache.tuweni.bytes.Bytes; + +public class Commit extends BftMessage { + + public Commit(final SignedData payload) { + super(payload); + } + + public Signature getCommitSeal() { + return getPayload().getCommitSeal(); + } + + public Hash getDigest() { + return getPayload().getDigest(); + } + + public static Commit decode(final Bytes data) { + return new Commit(PayloadDeserializers.readSignedCommitPayloadFrom(RLP.input(data))); + } +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagewrappers/Prepare.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagewrappers/Prepare.java new file mode 100644 index 0000000000..2c5b75e14f --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/messagewrappers/Prepare.java @@ -0,0 +1,39 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.messagewrappers; + +import org.hyperledger.besu.consensus.common.bft.messagewrappers.BftMessage; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.consensus.qbft.payload.PayloadDeserializers; +import org.hyperledger.besu.consensus.qbft.payload.PreparePayload; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.rlp.RLP; + +import org.apache.tuweni.bytes.Bytes; + +public class Prepare extends BftMessage { + + public Prepare(final SignedData payload) { + super(payload); + } + + public Hash getDigest() { + return getPayload().getDigest(); + } + + public static Prepare decode(final Bytes data) { + return new Prepare(PayloadDeserializers.readSignedPreparePayloadFrom(RLP.input(data))); + } +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/CommitPayload.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/CommitPayload.java new file mode 100644 index 0000000000..64ac6b49c6 --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/CommitPayload.java @@ -0,0 +1,107 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.payload; + +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +import org.hyperledger.besu.consensus.common.bft.payload.Payload; +import org.hyperledger.besu.consensus.qbft.messagedata.QbftV1; +import org.hyperledger.besu.crypto.SECP256K1.Signature; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.rlp.RLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import java.util.Objects; +import java.util.StringJoiner; + +public class CommitPayload implements Payload { + private static final int TYPE = QbftV1.COMMIT; + private final ConsensusRoundIdentifier roundIdentifier; + private final Hash digest; + private final Signature commitSeal; + + public CommitPayload( + final ConsensusRoundIdentifier roundIdentifier, + final Hash digest, + final Signature commitSeal) { + 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 = Payload.readDigest(rlpInput); + final Signature commitSeal = rlpInput.readBytes(Signature::decode); + rlpInput.leaveList(); + + return new CommitPayload(roundIdentifier, digest, commitSeal); + } + + @Override + public void writeTo(final RLPOutput rlpOutput) { + rlpOutput.startList(); + roundIdentifier.writeTo(rlpOutput); + rlpOutput.writeBytes(digest); + rlpOutput.writeBytes(commitSeal.encodedBytes()); + rlpOutput.endList(); + } + + @Override + public int getMessageType() { + return TYPE; + } + + public Hash getDigest() { + return digest; + } + + 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/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/MessageFactory.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/MessageFactory.java new file mode 100644 index 0000000000..5323355172 --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/MessageFactory.java @@ -0,0 +1,60 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.payload; + +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +import org.hyperledger.besu.consensus.common.bft.payload.Payload; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.consensus.qbft.messagewrappers.Commit; +import org.hyperledger.besu.consensus.qbft.messagewrappers.Prepare; +import org.hyperledger.besu.crypto.NodeKey; +import org.hyperledger.besu.crypto.SECP256K1.Signature; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Util; + +import org.apache.tuweni.bytes.Bytes; + +public class MessageFactory { + + private final NodeKey nodeKey; + + public MessageFactory(final NodeKey nodeKey) { + this.nodeKey = nodeKey; + } + + public Prepare createPrepare(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { + final PreparePayload payload = new PreparePayload(roundIdentifier, digest); + return new Prepare(createSignedMessage(payload)); + } + + public Commit createCommit( + final ConsensusRoundIdentifier roundIdentifier, + final Hash digest, + final Signature commitSeal) { + final CommitPayload payload = new CommitPayload(roundIdentifier, digest, commitSeal); + return new Commit(createSignedMessage(payload)); + } + + private SignedData createSignedMessage(final M payload) { + final Signature signature = nodeKey.sign(hashForSignature(payload)); + return new SignedData<>(payload, Util.publicKeyToAddress(nodeKey.getPublicKey()), signature); + } + + public static Hash hashForSignature(final Payload unsignedMessageData) { + return Hash.hash( + Bytes.concatenate( + Bytes.of(unsignedMessageData.getMessageType()), unsignedMessageData.encoded())); + } +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/PayloadDeserializers.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/PayloadDeserializers.java new file mode 100644 index 0000000000..27120029b2 --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/PayloadDeserializers.java @@ -0,0 +1,56 @@ +/* + * Copyright 2020 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.payload; + +import org.hyperledger.besu.consensus.common.bft.payload.Payload; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.crypto.SECP256K1.Signature; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Util; +import org.hyperledger.besu.ethereum.rlp.RLPInput; + +public class PayloadDeserializers { + + public static SignedData readSignedPreparePayloadFrom(final RLPInput rlpInput) { + rlpInput.enterList(); + final PreparePayload unsignedMessageData = PreparePayload.readFrom(rlpInput); + final Signature signature = readSignature(rlpInput); + rlpInput.leaveList(); + return from(unsignedMessageData, signature); + } + + public static SignedData readSignedCommitPayloadFrom(final RLPInput rlpInput) { + rlpInput.enterList(); + final CommitPayload unsignedMessageData = CommitPayload.readFrom(rlpInput); + final Signature signature = readSignature(rlpInput); + rlpInput.leaveList(); + return from(unsignedMessageData, signature); + } + + protected static SignedData from( + final M unsignedMessageData, final Signature signature) { + final Address sender = recoverSender(unsignedMessageData, signature); + return new SignedData<>(unsignedMessageData, sender, signature); + } + + protected static Signature readSignature(final RLPInput signedMessage) { + return signedMessage.readBytes(Signature::decode); + } + + protected static Address recoverSender( + final Payload unsignedMessageData, final Signature signature) { + return Util.signatureToAddress(signature, MessageFactory.hashForSignature(unsignedMessageData)); + } +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/PreparePayload.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/PreparePayload.java new file mode 100644 index 0000000000..b7c769b5a9 --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/payload/PreparePayload.java @@ -0,0 +1,92 @@ +/* + * Copyright 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.payload; + +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +import org.hyperledger.besu.consensus.common.bft.payload.Payload; +import org.hyperledger.besu.consensus.qbft.messagedata.QbftV1; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.rlp.RLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import java.util.Objects; +import java.util.StringJoiner; + +public class PreparePayload implements Payload { + private static final int TYPE = QbftV1.PREPARE; + private final ConsensusRoundIdentifier roundIdentifier; + private final Hash digest; + + public PreparePayload(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { + this.roundIdentifier = roundIdentifier; + this.digest = digest; + } + + public static PreparePayload readFrom(final RLPInput rlpInput) { + rlpInput.enterList(); + final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(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.writeBytes(digest); + rlpOutput.endList(); + } + + @Override + public int getMessageType() { + return TYPE; + } + + 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/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/messagewrappers/CommitTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/messagewrappers/CommitTest.java new file mode 100644 index 0000000000..bf7c2096f6 --- /dev/null +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/messagewrappers/CommitTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2020 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.messagewrappers; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.consensus.qbft.messagedata.QbftV1; +import org.hyperledger.besu.consensus.qbft.payload.CommitPayload; +import org.hyperledger.besu.consensus.qbft.payload.MessageFactory; +import org.hyperledger.besu.crypto.NodeKey; +import org.hyperledger.besu.crypto.NodeKeyUtils; +import org.hyperledger.besu.crypto.SECP256K1.Signature; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Util; + +import java.math.BigInteger; + +import org.junit.Test; + +public class CommitTest { + + @Test + public void canRoundTripAPrepareMessage() { + final NodeKey nodeKey = NodeKeyUtils.generate(); + final Address addr = Util.publicKeyToAddress(nodeKey.getPublicKey()); + + final CommitPayload commitPayload = + new CommitPayload( + new ConsensusRoundIdentifier(1, 1), + Hash.ZERO, + Signature.create(BigInteger.ONE, BigInteger.ONE, (byte) 0)); + + final SignedData signedCommitPayload = + new SignedData<>( + commitPayload, addr, nodeKey.sign(MessageFactory.hashForSignature(commitPayload))); + + final Commit commitMsg = new Commit(signedCommitPayload); + + final Commit decodedPrepare = Commit.decode(commitMsg.encode()); + + assertThat(decodedPrepare.getMessageType()).isEqualTo(QbftV1.COMMIT); + assertThat(decodedPrepare.getAuthor()).isEqualTo(addr); + assertThat(decodedPrepare.getSignedPayload()) + .isEqualToComparingFieldByField(signedCommitPayload); + } +} diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/messagewrappers/PrepareTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/messagewrappers/PrepareTest.java new file mode 100644 index 0000000000..b82238b577 --- /dev/null +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/messagewrappers/PrepareTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2020 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.qbft.messagewrappers; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.consensus.qbft.messagedata.QbftV1; +import org.hyperledger.besu.consensus.qbft.payload.MessageFactory; +import org.hyperledger.besu.consensus.qbft.payload.PreparePayload; +import org.hyperledger.besu.crypto.NodeKey; +import org.hyperledger.besu.crypto.NodeKeyUtils; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Util; + +import org.junit.Test; + +public class PrepareTest { + + @Test + public void canRoundTripAPrepareMessage() { + final NodeKey nodeKey = NodeKeyUtils.generate(); + final Address addr = Util.publicKeyToAddress(nodeKey.getPublicKey()); + + final PreparePayload preparePayload = + new PreparePayload(new ConsensusRoundIdentifier(1, 1), Hash.ZERO); + + final SignedData signedPreparePayload = + new SignedData<>( + preparePayload, addr, nodeKey.sign(MessageFactory.hashForSignature(preparePayload))); + + final Prepare prepareMsg = new Prepare(signedPreparePayload); + + final Prepare decodedPrepare = Prepare.decode(prepareMsg.encode()); + + assertThat(decodedPrepare.getMessageType()).isEqualTo(QbftV1.PREPARE); + assertThat(decodedPrepare.getAuthor()).isEqualTo(addr); + assertThat(decodedPrepare.getSignedPayload()) + .isEqualToComparingFieldByField(signedPreparePayload); + } +} diff --git a/settings.gradle b/settings.gradle index e7ef062bd9..9c6fe698e4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,6 +22,7 @@ include 'consensus:clique' include 'consensus:common' include 'consensus:ibft' include 'consensus:ibftlegacy' +include 'consensus:qbft' include 'crypto' include 'enclave' include 'errorprone-checks'