mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 23:47:57 -05:00
[NC-1582] Added ExtraData for iBFT 2.0 (#37)
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
|
||||
import tech.pegasys.pantheon.ethereum.core.Address;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPInput;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
|
||||
import tech.pegasys.pantheon.util.bytes.BytesValue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents the data structure stored in the extraData field of the BlockHeader used when
|
||||
* operating under an IBFT 2.0 consensus mechanism.
|
||||
*/
|
||||
public class Ibft2ExtraData {
|
||||
|
||||
public static final int EXTRA_VANITY_LENGTH = 32;
|
||||
|
||||
private final BytesValue vanityData;
|
||||
private final List<Signature> seals;
|
||||
private final Optional<Vote> vote;
|
||||
private final int round;
|
||||
private final List<Address> validators;
|
||||
|
||||
public Ibft2ExtraData(
|
||||
final BytesValue vanityData,
|
||||
final List<Signature> seals,
|
||||
final Optional<Vote> vote,
|
||||
final int round,
|
||||
final List<Address> validators) {
|
||||
|
||||
checkNotNull(vanityData);
|
||||
checkNotNull(seals);
|
||||
checkNotNull(validators);
|
||||
|
||||
this.vanityData = vanityData;
|
||||
this.seals = seals;
|
||||
this.round = round;
|
||||
this.validators = validators;
|
||||
this.vote = vote;
|
||||
}
|
||||
|
||||
public static Ibft2ExtraData decode(final BytesValue input) {
|
||||
checkArgument(
|
||||
input.size() > EXTRA_VANITY_LENGTH,
|
||||
"Invalid BytesValue supplied - too short to produce a valid IBFT Extra Data object.");
|
||||
|
||||
final RLPInput rlpInput = new BytesValueRLPInput(input, false);
|
||||
|
||||
rlpInput.enterList(); // This accounts for the "root node" which contains IBFT data items.
|
||||
final BytesValue vanityData = rlpInput.readBytesValue();
|
||||
final List<Address> validators = rlpInput.readList(Address::readFrom);
|
||||
final Optional<Vote> vote;
|
||||
if (rlpInput.nextIsNull()) {
|
||||
vote = Optional.empty();
|
||||
rlpInput.skipNext();
|
||||
} else {
|
||||
vote = Optional.of(Vote.readFrom(rlpInput));
|
||||
}
|
||||
final int round = rlpInput.readInt();
|
||||
final List<Signature> seals = rlpInput.readList(rlp -> Signature.decode(rlp.readBytesValue()));
|
||||
rlpInput.leaveList();
|
||||
|
||||
return new Ibft2ExtraData(vanityData, seals, vote, round, validators);
|
||||
}
|
||||
|
||||
public BytesValue encode() {
|
||||
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vanityData);
|
||||
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytesValue(validator));
|
||||
if (vote.isPresent()) {
|
||||
vote.get().writeTo(encoder);
|
||||
} else {
|
||||
encoder.writeNull();
|
||||
}
|
||||
encoder.writeInt(round);
|
||||
encoder.writeList(seals, (committer, rlp) -> rlp.writeBytesValue(committer.encodedBytes()));
|
||||
encoder.endList();
|
||||
|
||||
return encoder.encoded();
|
||||
}
|
||||
|
||||
// Accessors
|
||||
public BytesValue getVanityData() {
|
||||
return vanityData;
|
||||
}
|
||||
|
||||
public List<Signature> getSeals() {
|
||||
return seals;
|
||||
}
|
||||
|
||||
public List<Address> getValidators() {
|
||||
return validators;
|
||||
}
|
||||
|
||||
public Optional<Vote> getVote() {
|
||||
return vote;
|
||||
}
|
||||
|
||||
public int getRound() {
|
||||
return round;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import tech.pegasys.pantheon.ethereum.rlp.RLPException;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
|
||||
|
||||
public enum Ibft2VoteType {
|
||||
ADD((byte) 0xFF),
|
||||
DROP((byte) 0x00);
|
||||
|
||||
private final byte voteValue;
|
||||
|
||||
Ibft2VoteType(final byte voteValue) {
|
||||
this.voteValue = voteValue;
|
||||
}
|
||||
|
||||
public byte getVoteValue() {
|
||||
return voteValue;
|
||||
}
|
||||
|
||||
public static Ibft2VoteType readFrom(final RLPInput rlpInput) {
|
||||
byte encodedByteValue = rlpInput.readByte();
|
||||
for (final Ibft2VoteType voteType : values()) {
|
||||
if (voteType.voteValue == encodedByteValue) {
|
||||
return voteType;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RLPException("Invalid Ibft2VoteType RLP encoding");
|
||||
}
|
||||
|
||||
public void writeTo(final RLPOutput rlpOutput) {
|
||||
rlpOutput.writeByte(voteValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.Address;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.RLPOutput;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
public class Vote {
|
||||
private final Address recipient;
|
||||
private final Ibft2VoteType voteType;
|
||||
|
||||
private Vote(final Address recipient, final Ibft2VoteType voteType) {
|
||||
this.recipient = recipient;
|
||||
this.voteType = voteType;
|
||||
}
|
||||
|
||||
public static Vote authVote(final Address address) {
|
||||
return new Vote(address, Ibft2VoteType.ADD);
|
||||
}
|
||||
|
||||
public static Vote dropVote(final Address address) {
|
||||
return new Vote(address, Ibft2VoteType.DROP);
|
||||
}
|
||||
|
||||
public Address getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
public boolean isAuth() {
|
||||
return voteType.equals(Ibft2VoteType.ADD);
|
||||
}
|
||||
|
||||
public boolean isDrop() {
|
||||
return voteType.equals(Ibft2VoteType.DROP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Vote vote1 = (Vote) o;
|
||||
return recipient.equals(vote1.recipient) && voteType.equals(vote1.voteType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(recipient, voteType);
|
||||
}
|
||||
|
||||
public Ibft2VoteType getVoteType() {
|
||||
return voteType;
|
||||
}
|
||||
|
||||
public void writeTo(final RLPOutput rlpOutput) {
|
||||
rlpOutput.startList();
|
||||
rlpOutput.writeBytesValue(recipient);
|
||||
voteType.writeTo(rlpOutput);
|
||||
rlpOutput.endList();
|
||||
}
|
||||
|
||||
public static Vote readFrom(final RLPInput rlpInput) {
|
||||
|
||||
rlpInput.enterList();
|
||||
final Address recipient = Address.readFrom(rlpInput);
|
||||
final Ibft2VoteType vote = Ibft2VoteType.readFrom(rlpInput);
|
||||
rlpInput.leaveList();
|
||||
|
||||
return new Vote(recipient, vote);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
|
||||
import tech.pegasys.pantheon.ethereum.core.Address;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.RLPException;
|
||||
import tech.pegasys.pantheon.ethereum.rlp.RLPInput;
|
||||
import tech.pegasys.pantheon.util.bytes.BytesValue;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.junit.Test;
|
||||
|
||||
public class Ibft2ExtraDataTest {
|
||||
private final String RAW_HEX_ENCODING_STRING =
|
||||
"f8f1a00102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20ea9400000000000000000000000000000000000"
|
||||
+ "00001940000000000000000000000000000000000000002d794000000000000000000000000000000000000000181ff8400fedc"
|
||||
+ "baf886b841000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000"
|
||||
+ "0000000000000000000000000000000000a00b84100000000000000000000000000000000000000000000000000000000000000"
|
||||
+ "0a000000000000000000000000000000000000000000000000000000000000000100";
|
||||
|
||||
private final Ibft2ExtraData DECODED_EXTRA_DATA_FOR_RAW_HEX_ENCODING_STRING =
|
||||
getDecodedExtraDataForRawHexEncodingString();
|
||||
|
||||
private static Ibft2ExtraData getDecodedExtraDataForRawHexEncodingString() {
|
||||
final List<Address> validators =
|
||||
Arrays.asList(Address.fromHexString("1"), Address.fromHexString("2"));
|
||||
final Optional<Vote> vote = Optional.of(Vote.authVote(Address.fromHexString("1")));
|
||||
final int round = 0x00FEDCBA;
|
||||
final List<Signature> committerSeals =
|
||||
Arrays.asList(
|
||||
Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0),
|
||||
Signature.create(BigInteger.TEN, BigInteger.ONE, (byte) 0));
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = createNonEmptyVanityData();
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
return new Ibft2ExtraData(vanity_data, committerSeals, vote, round, validators);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void correctlyCodedRoundConstitutesValidContent() {
|
||||
final List<Address> validators = Lists.newArrayList();
|
||||
final Optional<Vote> vote = Optional.of(Vote.authVote(Address.fromHexString("1")));
|
||||
final int round = 0x00FEDCBA;
|
||||
final byte[] roundAsByteArray = new byte[] {(byte) 0x00, (byte) 0xFE, (byte) 0xDC, (byte) 0xBA};
|
||||
final List<Signature> committerSeals = Lists.newArrayList();
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = new byte[32];
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vanity_data);
|
||||
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytesValue(validator));
|
||||
|
||||
// encoded vote
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vote.get().getRecipient());
|
||||
vote.get().getVoteType().writeTo(encoder);
|
||||
encoder.endList();
|
||||
|
||||
// This is to verify that the decoding works correctly when the round is encoded as 4 bytes
|
||||
encoder.writeBytesValue(BytesValue.wrap(roundAsByteArray));
|
||||
encoder.writeList(
|
||||
committerSeals, (committer, rlp) -> rlp.writeBytesValue(committer.encodedBytes()));
|
||||
encoder.endList();
|
||||
|
||||
final BytesValue bufferToInject = encoder.encoded();
|
||||
|
||||
final Ibft2ExtraData extraData = Ibft2ExtraData.decode(bufferToInject);
|
||||
|
||||
assertThat(extraData.getVanityData()).isEqualTo(vanity_data);
|
||||
assertThat(extraData.getVote()).isEqualTo(vote);
|
||||
assertThat(extraData.getRound()).isEqualTo(round);
|
||||
assertThat(extraData.getSeals()).isEqualTo(committerSeals);
|
||||
assertThat(extraData.getValidators()).isEqualTo(validators);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test specifically verifies that {@link Ibft2ExtraData#decode(BytesValue)} uses {@link
|
||||
* RLPInput#readInt()} rather than {@link RLPInput#readIntScalar()} to decode the round number
|
||||
*/
|
||||
@Test
|
||||
public void incorrectlyEncodedRoundThrowsRlpException() {
|
||||
final List<Address> validators = Lists.newArrayList();
|
||||
final Optional<Vote> vote = Optional.of(Vote.authVote(Address.fromHexString("1")));
|
||||
final byte[] roundAsByteArray = new byte[] {(byte) 0xFE, (byte) 0xDC, (byte) 0xBA};
|
||||
final List<Signature> committerSeals = Lists.newArrayList();
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = new byte[32];
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vanity_data);
|
||||
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytesValue(validator));
|
||||
|
||||
// encoded vote
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vote.get().getRecipient());
|
||||
vote.get().getVoteType().writeTo(encoder);
|
||||
encoder.endList();
|
||||
|
||||
// This is to verify that the decoding throws an exception when the round number is not encoded
|
||||
// in 4 byte format
|
||||
encoder.writeBytesValue(BytesValue.wrap(roundAsByteArray));
|
||||
encoder.writeList(
|
||||
committerSeals, (committer, rlp) -> rlp.writeBytesValue(committer.encodedBytes()));
|
||||
encoder.endList();
|
||||
|
||||
final BytesValue bufferToInject = encoder.encoded();
|
||||
|
||||
assertThatThrownBy(() -> Ibft2ExtraData.decode(bufferToInject))
|
||||
.isInstanceOf(RLPException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullVoteAndListConstituteValidContent() {
|
||||
final List<Address> validators = Lists.newArrayList();
|
||||
final int round = 0x00FEDCBA;
|
||||
final List<Signature> committerSeals = Lists.newArrayList();
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = new byte[32];
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vanity_data);
|
||||
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytesValue(validator));
|
||||
|
||||
// encode an empty vote
|
||||
encoder.writeNull();
|
||||
|
||||
encoder.writeInt(round);
|
||||
encoder.writeList(
|
||||
committerSeals, (committer, rlp) -> rlp.writeBytesValue(committer.encodedBytes()));
|
||||
encoder.endList();
|
||||
|
||||
final BytesValue bufferToInject = encoder.encoded();
|
||||
|
||||
final Ibft2ExtraData extraData = Ibft2ExtraData.decode(bufferToInject);
|
||||
|
||||
assertThat(extraData.getVanityData()).isEqualTo(vanity_data);
|
||||
assertThat(extraData.getVote().isPresent()).isEqualTo(false);
|
||||
assertThat(extraData.getRound()).isEqualTo(round);
|
||||
assertThat(extraData.getSeals()).isEqualTo(committerSeals);
|
||||
assertThat(extraData.getValidators()).isEqualTo(validators);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyVoteAndListIsEncodedCorrectly() {
|
||||
final List<Address> validators = Lists.newArrayList();
|
||||
Optional<Vote> vote = Optional.empty();
|
||||
final int round = 0x00FEDCBA;
|
||||
final List<Signature> committerSeals = Lists.newArrayList();
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = new byte[32];
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
Ibft2ExtraData expectedExtraData =
|
||||
new Ibft2ExtraData(vanity_data, committerSeals, vote, round, validators);
|
||||
|
||||
Ibft2ExtraData actualExtraData = Ibft2ExtraData.decode(expectedExtraData.encode());
|
||||
|
||||
assertThat(actualExtraData).isEqualToComparingFieldByField(expectedExtraData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyListConstituteValidContent() {
|
||||
final List<Address> validators = Lists.newArrayList();
|
||||
final Optional<Vote> vote = Optional.of(Vote.dropVote(Address.fromHexString("1")));
|
||||
final int round = 0x00FEDCBA;
|
||||
final List<Signature> committerSeals = Lists.newArrayList();
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = new byte[32];
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vanity_data);
|
||||
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytesValue(validator));
|
||||
|
||||
// encoded vote
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vote.get().getRecipient());
|
||||
vote.get().getVoteType().writeTo(encoder);
|
||||
encoder.endList();
|
||||
|
||||
encoder.writeInt(round);
|
||||
encoder.writeList(
|
||||
committerSeals, (committer, rlp) -> rlp.writeBytesValue(committer.encodedBytes()));
|
||||
encoder.endList();
|
||||
|
||||
final BytesValue bufferToInject = encoder.encoded();
|
||||
|
||||
final Ibft2ExtraData extraData = Ibft2ExtraData.decode(bufferToInject);
|
||||
|
||||
assertThat(extraData.getVanityData()).isEqualTo(vanity_data);
|
||||
assertThat(extraData.getVote()).isEqualTo(vote);
|
||||
assertThat(extraData.getRound()).isEqualTo(round);
|
||||
assertThat(extraData.getSeals()).isEqualTo(committerSeals);
|
||||
assertThat(extraData.getValidators()).isEqualTo(validators);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyListsAreEncodedAndDecodedCorrectly() {
|
||||
final List<Address> validators = Lists.newArrayList();
|
||||
final Optional<Vote> vote = Optional.of(Vote.authVote(Address.fromHexString("1")));
|
||||
final int round = 0x00FEDCBA;
|
||||
final List<Signature> committerSeals = Lists.newArrayList();
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = new byte[32];
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
Ibft2ExtraData expectedExtraData =
|
||||
new Ibft2ExtraData(vanity_data, committerSeals, vote, round, validators);
|
||||
|
||||
Ibft2ExtraData actualExtraData = Ibft2ExtraData.decode(expectedExtraData.encode());
|
||||
|
||||
assertThat(actualExtraData).isEqualToComparingFieldByField(expectedExtraData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullyPopulatedDataProducesCorrectlyFormedExtraDataObject() {
|
||||
final List<Address> validators =
|
||||
Arrays.asList(Address.fromHexString("1"), Address.fromHexString("2"));
|
||||
final Optional<Vote> vote = Optional.of(Vote.authVote(Address.fromHexString("1")));
|
||||
final int round = 0x00FEDCBA;
|
||||
final List<Signature> committerSeals =
|
||||
Arrays.asList(
|
||||
Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0),
|
||||
Signature.create(BigInteger.TEN, BigInteger.ONE, (byte) 0));
|
||||
|
||||
// Create randomised vanity data.
|
||||
final byte[] vanity_bytes = createNonEmptyVanityData();
|
||||
new Random().nextBytes(vanity_bytes);
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
|
||||
encoder.startList(); // This is required to create a "root node" for all RLP'd data
|
||||
encoder.writeBytesValue(vanity_data);
|
||||
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytesValue(validator));
|
||||
|
||||
// encoded vote
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vote.get().getRecipient());
|
||||
vote.get().getVoteType().writeTo(encoder);
|
||||
encoder.endList();
|
||||
|
||||
encoder.writeInt(round);
|
||||
encoder.writeList(
|
||||
committerSeals, (committer, rlp) -> rlp.writeBytesValue(committer.encodedBytes()));
|
||||
encoder.endList();
|
||||
|
||||
final BytesValue bufferToInject = encoder.encoded();
|
||||
|
||||
final Ibft2ExtraData extraData = Ibft2ExtraData.decode(bufferToInject);
|
||||
|
||||
assertThat(extraData.getVanityData()).isEqualTo(vanity_data);
|
||||
assertThat(extraData.getVote()).isEqualTo(vote);
|
||||
assertThat(extraData.getRound()).isEqualTo(round);
|
||||
assertThat(extraData.getSeals()).isEqualTo(committerSeals);
|
||||
assertThat(extraData.getValidators()).isEqualTo(validators);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullyPopulatedDataIsEncodedAndDecodedCorrectly() {
|
||||
final List<Address> validators =
|
||||
Arrays.asList(Address.fromHexString("1"), Address.fromHexString("2"));
|
||||
final Optional<Vote> vote = Optional.of(Vote.authVote(Address.fromHexString("1")));
|
||||
final int round = 0x00FEDCBA;
|
||||
final List<Signature> committerSeals =
|
||||
Arrays.asList(
|
||||
Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0),
|
||||
Signature.create(BigInteger.TEN, BigInteger.ONE, (byte) 0));
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = createNonEmptyVanityData();
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
Ibft2ExtraData expectedExtraData =
|
||||
new Ibft2ExtraData(vanity_data, committerSeals, vote, round, validators);
|
||||
|
||||
Ibft2ExtraData actualExtraData = Ibft2ExtraData.decode(expectedExtraData.encode());
|
||||
|
||||
assertThat(actualExtraData).isEqualToComparingFieldByField(expectedExtraData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodingMatchesKnownRawHexString() {
|
||||
BytesValue expectedRawDecoding = BytesValue.fromHexString(RAW_HEX_ENCODING_STRING);
|
||||
assertThat(DECODED_EXTRA_DATA_FOR_RAW_HEX_ENCODING_STRING.encode())
|
||||
.isEqualTo(expectedRawDecoding);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodingOfKnownRawHexStringMatchesKnowExtraDataObject() {
|
||||
|
||||
final Ibft2ExtraData expectedExtraData = DECODED_EXTRA_DATA_FOR_RAW_HEX_ENCODING_STRING;
|
||||
|
||||
BytesValue rawDecoding = BytesValue.fromHexString(RAW_HEX_ENCODING_STRING);
|
||||
Ibft2ExtraData actualExtraData = Ibft2ExtraData.decode(rawDecoding);
|
||||
|
||||
assertThat(actualExtraData).isEqualToComparingFieldByField(expectedExtraData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incorrectlyStructuredRlpThrowsException() {
|
||||
final List<Address> validators = Lists.newArrayList();
|
||||
final Optional<Vote> vote = Optional.of(Vote.authVote(Address.fromHexString("1")));
|
||||
final int round = 0x00FEDCBA;
|
||||
final List<Signature> committerSeals = Lists.newArrayList();
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = new byte[32];
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vanity_data);
|
||||
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytesValue(validator));
|
||||
|
||||
// encoded vote
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vote.get().getRecipient());
|
||||
vote.get().getVoteType().writeTo(encoder);
|
||||
encoder.endList();
|
||||
|
||||
encoder.writeInt(round);
|
||||
encoder.writeList(
|
||||
committerSeals, (committer, rlp) -> rlp.writeBytesValue(committer.encodedBytes()));
|
||||
encoder.writeLong(1);
|
||||
encoder.endList();
|
||||
|
||||
final BytesValue bufferToInject = encoder.encoded();
|
||||
|
||||
assertThatThrownBy(() -> Ibft2ExtraData.decode(bufferToInject))
|
||||
.isInstanceOf(RLPException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incorrectVoteTypeThrowsException() {
|
||||
final List<Address> validators =
|
||||
Arrays.asList(Address.fromHexString("1"), Address.fromHexString("2"));
|
||||
final Address voteRecipient = Address.fromHexString("1");
|
||||
final byte voteType = (byte) 0xAA;
|
||||
final int round = 0x00FEDCBA;
|
||||
final List<Signature> committerSeals =
|
||||
Arrays.asList(
|
||||
Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0),
|
||||
Signature.create(BigInteger.TEN, BigInteger.ONE, (byte) 0));
|
||||
|
||||
// Create a byte buffer with no data.
|
||||
final byte[] vanity_bytes = new byte[32];
|
||||
final BytesValue vanity_data = BytesValue.wrap(vanity_bytes);
|
||||
|
||||
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(vanity_data);
|
||||
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytesValue(validator));
|
||||
|
||||
// encode vote
|
||||
encoder.startList();
|
||||
encoder.writeBytesValue(voteRecipient);
|
||||
encoder.writeByte(voteType);
|
||||
encoder.endList();
|
||||
|
||||
encoder.writeInt(round);
|
||||
encoder.writeList(
|
||||
committerSeals, (committer, rlp) -> rlp.writeBytesValue(committer.encodedBytes()));
|
||||
encoder.endList();
|
||||
|
||||
final BytesValue bufferToInject = encoder.encoded();
|
||||
|
||||
assertThatThrownBy(() -> Ibft2ExtraData.decode(bufferToInject))
|
||||
.isInstanceOf(RLPException.class);
|
||||
}
|
||||
|
||||
private static byte[] createNonEmptyVanityData() {
|
||||
final byte[] vanity_bytes = new byte[32];
|
||||
for (int i = 0; i < vanity_bytes.length; i++) {
|
||||
vanity_bytes[i] = (byte) (i + 1);
|
||||
}
|
||||
|
||||
return vanity_bytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.assertj.core.api.Java6Assertions.assertThat;
|
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.Address;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class VoteTest {
|
||||
@Test
|
||||
public void testStaticVoteCreationMethods() {
|
||||
assertThat(Vote.authVote(Address.fromHexString("1")).isAuth()).isEqualTo(true);
|
||||
assertThat(Vote.authVote(Address.fromHexString("1")).isDrop()).isEqualTo(false);
|
||||
|
||||
assertThat(Vote.dropVote(Address.fromHexString("1")).isAuth()).isEqualTo(false);
|
||||
assertThat(Vote.dropVote(Address.fromHexString("1")).isDrop()).isEqualTo(true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user