Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write empty (non-nil) struct pointers, unless (is list element and empty_elements isn't set) #206

Merged
merged 12 commits into from
Aug 6, 2018

Conversation

jaekwon
Copy link
Contributor

@jaekwon jaekwon commented Jul 19, 2018

Currently we don't write empty structs even if it's a non-nil pointer to an empty struct.
This loses information about nil vs empty structs, when say the field value is a non-nil pointer to an empty struct.
This causes all kinds of issues with Tendermint, and probably isn't the behavior that we want, and it isn't the same as goproto3 behavior either.

This PR tries to fix this.
Still to do:

  • write tests
  • test against tendermint

@jaekwon jaekwon requested a review from liamsi July 19, 2018 01:08
@liamsi
Copy link
Contributor

liamsi commented Jul 19, 2018

This causes all kinds of issues with Tendermint, and probably isn't the behavior that we want, and it isn't the same as goproto3 behavior either.

Are the issues in tendermint noticeable in failing tests? Just asking because I'm using the latest pre-release of amino in this tendermint PR:
tendermint/tendermint#1742
All tendermint tests pass there (and the pre-release does not incl. the changes made here but the struct skipping code AFAIK).

- compare to proto3 behaviour
- test for pointers to empty struct and nil pointer

Signed-off-by: Liamsi <[email protected]>
@liamsi
Copy link
Contributor

liamsi commented Jul 19, 2018

Thanks @jaekwon! I've added a very simple test-case in #209
The behaviour you wanted to change passes the test. There is a minor difference on what we end if when we encode "nothing" (see #209 for details and a change that fixes the failing test).

liamsi and others added 2 commits July 19, 2018 18:38
@jaekwon
Copy link
Contributor Author

jaekwon commented Jul 20, 2018

@liamsi Modified the fuzz tests to also include empty structs. There's one test case that still fails... we should also run the other fuzz tests to test decoding.

@jaekwon jaekwon mentioned this pull request Jul 20, 2018
@jaekwon jaekwon force-pushed the jae/writeemptyptr branch from f8b2561 to 6db2352 Compare August 5, 2018 03:39
@jaekwon jaekwon changed the title WIP Always write empty if struct field is pointer Empty structs: write_empty and empty_elements Aug 5, 2018
@jaekwon jaekwon changed the title Empty structs: write_empty and empty_elements Write empty (non-nil) struct pointers, unless (is list element and empty_elements isn't set) Aug 5, 2018
@jaekwon jaekwon changed the title Write empty (non-nil) struct pointers, unless (is list element and empty_elements isn't set) Write empty (non-nil) struct pointers, unless (is list element and empty_elements isn't set) or (write_empty is set) Aug 5, 2018
@jaekwon jaekwon changed the title Write empty (non-nil) struct pointers, unless (is list element and empty_elements isn't set) or (write_empty is set) Write empty (non-nil) struct pointers, unless (is list element and empty_elements isn't set) Aug 5, 2018
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.

Looks good to me. We could improve one error message in being more precise and we could make the "nil struct pointers not supporeted"-error a const. error. such that the caller can handle the case if desired. We can think of this in follow up PRs.

We still need to test it against tendermint, though.

binary-encode.go Outdated
// Proto3's Golang client does.
// This also makes it easier to upgrade to Amino2
// which would enable the encoding of nil structs.
return errors.New("Amino1 doesn't support nil struct pointers")
Copy link
Contributor

Choose a reason for hiding this comment

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

I propose this to be a sentinel error / constant. This would give the caller some control over the behaviour, too.
Also, I think the error message should be "Amino1 doesn't support nil struct pointers in lists"

Ok, this changed in later commit. Maybe, "nil struct pointers in lists are not supported when empty_elements field tag is set". The sentinel error might still be useful, though. We can merge this as it is and think about other cases where this makes sense too (see #191)

// * next ByteLength bytes are 0x00, and
// * - erv is not a struct pointer, or
// - field option doesn't have EmptyElements set
// (the condition below uses demorgan's law)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Demorgan's

@liamsi
Copy link
Contributor

liamsi commented Aug 5, 2018

tested against tendermint (develop). There will be a failing regression test. One has to apply the following changes for the test to pass:

Index: types/proto3_test.go
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- types/proto3_test.go	(revision 6e3c5e803322d7ae08dbbe6f885899c6204beed5)
+++ types/proto3_test.go	(date 1533489963000)
@@ -67,11 +67,6 @@
 		Height:  150,
 		Time:    &proto3.Timestamp{Seconds: seconds, Nanos: nanos},
 		NumTxs:  7,
-		// This is not fully skipped in amino (yet) although it is empty:
-		LastBlockID: &proto3.BlockID{
-			PartsHeader: &proto3.PartSetHeader{
-			},
-		},
 		TotalTxs:       100,
 		LastCommitHash: []byte("commit hash"),
 		DataHash:       []byte("data hash"),

@liamsi
Copy link
Contributor

liamsi commented Aug 6, 2018

We should document that we differ from protobuf here but that we can enforce protobuf compatible behaviour by adding the empty_elements tag to structs (referencing #203).

Also, I'm wondering if this shouldn't be the default behaviour and the tag would explicitly signal that we deviate from proto3 instead (and add the tag into tendermint types to where necessary). But I do not think it is reasonable to block this PR on this decision / discussion.

Thanks @jaekwon. Merging now. And will prep a release afterwards.

@liamsi liamsi merged commit b501f6c into develop Aug 6, 2018
liamsi added a commit that referenced this pull request Aug 17, 2018
* DecodeInt{8, 16} negative limit checks + tests (#125)

Updates #120

Also adds a test for Byteslice encoding and decoding
roundtripping and part slicing then rejoining.

* fix circleci2.0

* some metalinter issues

* skip over default values when encoding time (#178)

* do not encode empty structs, unless `amino:"write_empty"` is set (#179)

* skip over empty structs by default

* slightly more info on panics

* Fix zero time decoding (#190)

- fix decoding of skipped fields in time, or completely skipped time 
- necessary because sec=0 and ns=0 do not result in time.Time{} and vice versa

* Prepare release 0.11.0 (#193)

* Fix time decoding & encoding of arrays and structs

- top-level entry functions called with BinFieldNum:1 to properly encode e.g. arrays of structs (see non-time related test)
- add defaultValue method, different from #196 it deals with multiply nested pointers

* Removed dependency on tmlibs/common

* Always write empty if struct field is pointer

* add tests for #206

- test for pointers to empty struct and nil pointer

* fix proto3 compatibility for empty structs

* Add EmptyStruct to fuzz tests

* Revert "fix proto3 compatibility for empty structs"

* Do not allow encoding of nil struct pointers in a slice/array

* By default, 0-length list elements are decoded as nil
@liamsi liamsi deleted the jae/writeemptyptr branch October 30, 2018 18:11
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.

2 participants