-
Notifications
You must be signed in to change notification settings - Fork 130
[NC-1582] iBFT 2.0 ExtraData #37
[NC-1582] iBFT 2.0 ExtraData #37
Conversation
bd6d65d
to
b2a7b45
Compare
consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Ibft2ExtraData.java
Show resolved
Hide resolved
|
||
private final BytesValue vanityData; | ||
private final List<Signature> seals; | ||
private final Address voteRecipient; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added Vote class
consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Ibft2ExtraData.java
Show resolved
Hide resolved
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public static void writTo(final Optional<Vote> optionalVote, final RLPOutput rlpOutput) { | |
public static void writeTo(final Optional<Vote> optionalVote, final RLPOutput rlpOutput) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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());
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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'.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'date'? or data?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use assertj assertThatThrownBy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
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