Skip to content

abci: update Header to match amino encoding of types.Header#2681

Closed
ebuchman wants to merge 4 commits intodevelopfrom
bucky/fix-pb2tm-header
Closed

abci: update Header to match amino encoding of types.Header#2681
ebuchman wants to merge 4 commits intodevelopfrom
bucky/fix-pb2tm-header

Conversation

@ebuchman
Copy link
Contributor

@ebuchman ebuchman commented Oct 19, 2018

Turns out the amino encoding of the types.Header didn't match the proto3 encoding of the abci.Header - ref #2682.

The problem was that the ABCI header wasn't using signed integers properly - int64 in proto files maps to uint64 in Go. Need to use sint64 in the proto files instead.

We also needed to update to amino v0.13.0 so that the Timestamp encoding would be compatible.

This PR fixes all of that - now the aminoEncoding(types.Header) and proto3Encoding(abci.Header) match.

  • Updated all relevant documentation in docs
  • Updated all code comments where relevant
  • Wrote tests
  • Updated CHANGELOG_PENDING.md

@ebuchman ebuchman changed the title types: test tm2pm on fully populated header abci: update Header to match amino encoding of types.Header Oct 20, 2018
// basic block info
Version version = 1 [(gogoproto.nullable)=false];
string chain_id = 2 [(gogoproto.customname)="ChainID"];
int64 height = 3;
Copy link
Contributor

Choose a reason for hiding this comment

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

I hope its not too late to revert this decision prelaunch, but I think height being signed is the wrong decision. Feel free to close this comment if the ship has sailed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did you read https://blog.cosmos.network/choosing-a-type-for-blockchain-height-beware-of-unsigned-integers-714804dddf1d ? Can you argue against it ? Having to add an s here made me doubt our decision for the first time, but I still think it's probably right.

Copy link
Contributor

Choose a reason for hiding this comment

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

I have read it, will respond more later. I do disagree with it, and think that not choosing uint does increase chance of error. (I'll have to double check later, but I think we even have evidence of it doing so in our project)

Copy link
Contributor

@ValarDragon ValarDragon Oct 20, 2018

Choose a reason for hiding this comment

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

uints by design mitigate classes of bugs where the height would be negative. This could potentially be an issue on input received by a peer. Ref #2683. Its better to have this be a compile time guarantee.

I don't agree with the argumented presented in the blog post. I believe it mostly stems from a sub-optimally safe implementation at the time. Note that the blog post is essentially stating that "in the event of underflow when our logic does subtraction, its harder to tell if underflow happened."

When we do things like evidenceAge := height - evidence.Height(), we should have an explicit handler for the negative case anyways. I.e. To sanitize inputs, ensure that evidence.Height() <= height. In every situation where this underflowI could occur, I assert that it ought to be handled separately regardless. (The fact that the code currently doesn't do this is a separate issue) It makes more structural sense to sanitize this case before assigning to a variable. Doing the negative check afterwards (which already is lacking in every spot I've seen) seems worse, as inputs should be sanitized ASAP.

When doing things like foo < height - 1, I also think we need separate logic for the height = 0 case. Again us not having code handling the height=0 in the current code is a second bug. If the code were written more safely / with better errors, there would be no issue switching and we would get the compile-time guarantee / input sanitization for free. The fact that we need to account for these underflows separately is something that should be treated separately anyway, its an issue with the current code that it is not. This what I think led up to the decision to make these ints, but instead we should write the logic to handle these cases explicitly. Then we get the uint compile time guarantee.

To phrase this another way, the current implementation is handling these underflow cases ungracefully (i.e. same error message), and also requiring sanitization of all received input. Instead we should write it to handle underflow gracefully, and then switch to compile-time guarantees to remove that particular input sanitation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Another option with uints: implement SafeSub, SafeAdd, SafeMul, and SafeDiv, wrappers for checked arithmetic, and lint for any usage of bare operators in the codebase - that gives both compile-time compiler guarantees of correct signage and compile-time linter prevention of accidental check omission. It also protects better against cases where inputs might be checked initially when received from a peer, but are then added or combined in ways which could result in overflow/underflow if not explicitly checked then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The linter you're proposing sounds like the "Code Jesus" proposed in https://blog.cosmos.network/choosing-a-type-for-blockchain-height-beware-of-unsigned-integers-714804dddf1d.

I've summarized some thoughts about integers in Tendermint given recent events in: #2684

// EvidenceParams contains limits on the evidence.
message EvidenceParams {
// Note: must be greater than 0
int64 max_age = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO, This really should be a uint, since we even say must be greater than 0.

@codecov-io
Copy link

Codecov Report

❗ No coverage uploaded for pull request base (develop@9d62bd0). Click here to learn what that means.
The diff coverage is 100%.

@@            Coverage Diff             @@
##             develop    #2681   +/-   ##
==========================================
  Coverage           ?   61.62%           
==========================================
  Files              ?      207           
  Lines              ?    16895           
  Branches           ?        0           
==========================================
  Hits               ?    10412           
  Misses             ?     5622           
  Partials           ?      861
Impacted Files Coverage Δ
state/state.go 91.39% <100%> (ø)

Copy link
Contributor

@melekes melekes left a comment

Choose a reason for hiding this comment

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

🍰 🌮 🍉

block.AppHash = state.AppHash
block.LastResultsHash = state.LastResultsHash

// NOTE: we can't use the state.Validators because we don't
Copy link
Contributor

Choose a reason for hiding this comment

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

this is important

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree - it's commented in the functions godoc. But maybe we should bring it back here too?

Copy link
Contributor

Choose a reason for hiding this comment

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

it's commented in the functions godoc.

where? 👀

@ebuchman
Copy link
Contributor Author

Not thrilled about having to change these all to sint64. See summary in #2682 (comment)

@ebuchman ebuchman mentioned this pull request Oct 21, 2018
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "v0.12.0-rc0"
version = "v0.13.0-rc0"
Copy link
Contributor

@liamsi liamsi Oct 22, 2018

Choose a reason for hiding this comment

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

The release candidate is created from the "prepare release PR" which doesn't contain the commit for the renaming of MarshalBinary -> MarshalBinaryLenPrefixed (tendermint/go-amino#222 got merged after the PR was opened).

Copy link
Contributor

@liamsi liamsi left a comment

Choose a reason for hiding this comment

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

This PR LGTM!

Do we want to merge it, or, do we rather do the changes you've suggested in #2684 first?

@ebuchman ebuchman mentioned this pull request Oct 22, 2018
4 tasks
@ebuchman
Copy link
Contributor Author

I think we should close this for #2687.

I don't think it makes sense to switch ABCI ints to sint. We should probably go with #2684 but I'm not sure that's in scope for this release.

@ebuchman ebuchman closed this Oct 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants