Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

[NC-1582] iBFT 2.0 ExtraData #37

Merged
merged 20 commits into from
Oct 18, 2018

Conversation

saltiniroberto
Copy link
Contributor

iBFT ExtraData class for iBFT 2.0

For the extraData iBFT 2.0 specifications, please see par. "Block Header Description" of the iBFT 2.0 Specification Document


private final BytesValue vanityData;
private final List<Signature> seals;
private final Address voteRecipient;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This address and VoteType should be encapsulated in a single object, which in turn is optional. Similar to what Chris did (or almost did?) in Clique.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the object, no optional. The object represented the 3 states of auth, drop, and empty given all 3 states have values when read or written.

Copy link
Contributor Author

@saltiniroberto saltiniroberto Oct 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair suggestion but it would be great if we could agree beforehand on whether we want an Optional vote field in ExtraData or whether we want a Vote that can be empty?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is good in that it exactly represents what is in the block header.
My concern is that a vote is only valid if both recipient and 'polarity' exist. But maybe the IbftExtraDataValidator can ensure this data struct is valid.

The smell may be that in the past, the vote type field was primitive type (i.e. was a long up until we needed it to be a vote) - We may now fail to deserialise the header, rather than failing at validation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added Vote class

input.size() > EXTRA_VANITY_LENGTH,
"Invalid BytesValue supplied - too short to produce a valid IBFT Extra Data object.");

final BytesValue vanityData = input.slice(0, EXTRA_VANITY_LENGTH);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming the VanityData will not be RLPd?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly as it is in iBFT 1.0. So, yes, we are not RLP'ing vanity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have actually decided to RLP the vanity data as it is a better approach

rlpInput.enterList(); // This accounts for the "root node" which contains IBFT data items.
final List<Address> validators = rlpInput.readList(Address::readFrom);
final Address voteRecipient = Address.readFrom(rlpInput);
final Optional<Ibft2VoteType> vote = Ibft2VoteType.readFrom(rlpInput);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the Vote Optional? Or will it always be there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the vote optional just to handle the case that the vote value is different from either AUTH or DROP.
Alternative is to throw an RLP exception if that is the case.
What do you suggest?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really know - but this is ultimately the smell - we're interpreting data rather than just deserialising. Maybe Vote should always exist, but be a long/int?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to non Optional and type changed to Vote

return new Vote(address, Ibft2VoteType.DROP);
}

public static Vote noVote() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see this be a 3rd Ibft2VoteType as currently noVotes get identified by isDrop and won't be easily filterable as a non actionable type if we use this representation in places other than the extra data.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or at least update the isDrop() function to not include noVotes & maybe add an isNoVote()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conditioned isDrop to return true if the recipient address is different from 0.
Also, added isNoVote and tests for testing these methods and the creating methods

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussion with Trent, I will change the vote field in extraData back to Optional to identify that there is no vote information in the extra data and remove the noVote method from Vote

return voteType;
}

public static void writTo(final Optional<Vote> optionalVote, final RLPOutput rlpOutput) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public static void writTo(final Optional<Vote> optionalVote, final RLPOutput rlpOutput) {
public static void writeTo(final Optional<Vote> optionalVote, final RLPOutput rlpOutput) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

public static void writTo(final Optional<Vote> optionalVote, final RLPOutput rlpOutput) {
if (optionalVote.isPresent()) {
rlpOutput.startList();
rlpOutput.writeBytesValue(optionalVote.get().recipient);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pull the optional into a local variable - or else embed in a lambda:
optionalVote.ifPresent(v -> { ...}).orElse(rlp.writeNull());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I am aware, you cannot apply orElse after ifPresent

}

public static Optional<Vote> readFrom(final RLPInput rlpInput) {
if (!rlpInput.nextIsNull()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Flip the conditional and exit early if(rlpInput.nextIsNull()) { return Optional.empty;}
the use the reset of the code outside the conditional

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

public class Ibft2ExtraDataTest {

@Test
public void correctlyCodedRoundConstitutesValidContent() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be largely testing the RLP of the round number - with some side benefits of the other fields. Which aspect of the IBFT Extra Data does using a byte array for the round prove?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That you are not coding it as scalar, but you always expect to be coded as 4 bytes regardless of the leading zeroes

assertThat(extraData.getValidators()).isEqualTo(validators);
}

@Test(expected = RLPException.class)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People seem to be a fan of using assertj's assertThrownBy rather than the junit 'expected'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


@Test
public void fullyPopulatedDataProducesCorrectlyFormedExtraDataObject() {
final List<Address> validators = Arrays.asList(Address.ECREC, Address.SHA256);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably better using custom addresses - i.e. Address.fromHexString("<>") rather than the pre-canned ones.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the validator list need to be ordered? (I personally think not, but that's my perspective).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for coding the extraData. Sorting is only relevant for the validation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed. I was using the pre-canned one because I know that they are real addresses. But it doesn't really matter

}

@Test
public void fullyPopulatedDateIsEncodedCorrectly() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'date'? or data?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

assertThat(actualExtraData).isEqualToComparingFieldByField(expectedExtraData);
}

@Test(expected = RLPException.class)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use assertj assertThatThrownBy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@saltiniroberto saltiniroberto merged commit fdd08a8 into PegaSysEng:master Oct 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants