Skip to content

TLV testcases#631

Merged
rustyrussell merged 5 commits into
lightning:masterfrom
rustyrussell:tlv-testcases
Jul 22, 2019
Merged

TLV testcases#631
rustyrussell merged 5 commits into
lightning:masterfrom
rustyrussell:tlv-testcases

Conversation

@rustyrussell
Copy link
Copy Markdown
Collaborator

Came across some new requirements and a few tool fixes along the way...

@rustyrussell rustyrussell requested a review from t-bast July 9, 2019 11:06
Copy link
Copy Markdown
Collaborator

@t-bast t-bast left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for this!

Copy link
Copy Markdown
Collaborator

@cfromknecht cfromknecht left a comment

Choose a reason for hiding this comment

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

Thanks for getting the ball rolling on the test vectors @rustyrussell! I'm about half way through, but the main thing i'm noticing so far is a discrepancy in the way that the varints are being encoded. CompactSize encodes any multi-byte values using little endian, while the tests are using big-endian.

Comment thread 01-messaging.md Outdated
1. Invalid stream: 0xfd01
2. Reason: type truncated

1. Invalid stream: 0xfd0001 01
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
1. Invalid stream: 0xfd0001 01
1. Invalid stream: 0xfd0100

the current value is minimally encoded, remove trailing 0x01

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for getting the ball rolling on the test vectors @rustyrussell! I'm about half way through, but the main thing i'm noticing so far is a discrepancy in the way that the varints are being encoded. CompactSize encodes any multi-byte values using little endian, while the tests are using big-endian.

You're right :( Bitcoin strikes again...

I assume we should follow (and document!) Bitcoin CompactSize endian here, since that was the rationale for not inventing our own?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It should be implicit from the test vectors, but if we want to be more specific fine by me :)

fwiw I have a series of varint test vectors, should I make a separate pr with those?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good catch @cfromknecht !
That shows I went too quickly when comparing my test suite and this PR, I'll spend more time on it today.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Unfortunately, my tests passed after I fixed the endian without fixing the data. I'll add some test vectors which only work in the correct endian.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

And we need the trailing 01, otherwise it is invalid for a different reason: no length field. Though that should be 00 not 01.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

shouldn't the check for whether the type is minimally encoded be applied before attempting to parse the length?

Comment thread 01-messaging.md Outdated
Comment thread 01-messaging.md Outdated
Comment thread 01-messaging.md Outdated
Comment thread 01-messaging.md Outdated
Comment thread 01-messaging.md Outdated
1. Invalid stream: 0x1f 00 0f 01 2a
2. Reason: valid (ignored) tlv records but invalid ordering

1. Invalid stream: 0x02 08 0000000000000231 02 08 0000000000000451
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this is a duplicate of line 613.
Maybe replace by 0x0f 01 2a 0f 01 2b to disallow duplicate of unknown odd types?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Next line tests duplicate ignored. I'll just remove this one, good spotting!

@t-bast t-bast self-requested a review July 10, 2019 11:47
@cfromknecht
Copy link
Copy Markdown
Collaborator

would also be useful to add this to the decoding failures:

Invalid stream: 0xffffffffffffffffff 00 00
Reason: type overflow

@rustyrussell
Copy link
Copy Markdown
Collaborator Author

would also be useful to add this to the decoding failures:

Invalid stream: 0xffffffffffffffffff 00 00
Reason: type overflow

I'm confused, why is that an overflow? That's just the max possible type, no? Which is odd, so that is valid (assuming unknown)?

@rustyrussell
Copy link
Copy Markdown
Collaborator Author

Please test the New Hotness, which contains all the fixes and some new vectors.

@rustyrussell rustyrussell requested a review from cfromknecht July 11, 2019 02:33
@cfromknecht
Copy link
Copy Markdown
Collaborator

I'm confused, why is that an overflow? That's just the max possible type, no? Which is odd, so that is valid (assuming unknown)?

The type is max uint64 and has length 0, which is optional and so we ignore it. The following type is 0, which wraps around and breaks monotonicity, and so is not canonical

@cfromknecht
Copy link
Copy Markdown
Collaborator

@t-bast
Copy link
Copy Markdown
Collaborator

t-bast commented Jul 11, 2019

The type is max uint64 and has length 0, which is optional and so we ignore it. The following type is 0, which wraps around and breaks monotonicity, and so is not canonical

I agree, but the error message shouldn't be type overflow, it should be tlv records must be ordered by monotonically-increasing types or something like that.

@cfromknecht
Copy link
Copy Markdown
Collaborator

@t-bast sure not married to the name

Copy link
Copy Markdown
Collaborator

@t-bast t-bast left a comment

Choose a reason for hiding this comment

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

I integrated all of those in my test suite and everything is green!
ACK 99d3440 (and good luck with the annoying spell-checking failures)

@Roasbeef
Copy link
Copy Markdown
Collaborator

Please, let's not mix endianness in the protocol. There's no reason to inherit this wart of Bitcoin which makes it that much harder to understand.

@cfromknecht
Copy link
Copy Markdown
Collaborator

I made a PR that adds test vectors for the varint scheme. Given the recent discussion i made them using a big-endian encoding, but either way it's probably good to have something like this: #640

@rustyrussell
Copy link
Copy Markdown
Collaborator Author

OK, I'm about to add @cfromknecht 's max-then-min type test, and then rebase on top of #640...

@rustyrussell
Copy link
Copy Markdown
Collaborator Author

OK, so @cfromknecht 's ffff to 0 test fails for other reasons: 0 is even and unknown, and there's no length field. I prefer tests which test one thing at a time; I'll change n2s tlv1 to be 0 type, then this can be an invalid under n2 test: 0xffffffffffffffffff 00 00 00

@rustyrussell
Copy link
Copy Markdown
Collaborator Author

"This time for sure!" Please re-retest!

@cfromknecht
Copy link
Copy Markdown
Collaborator

cfromknecht commented Jul 16, 2019

OK, so @cfromknecht 's ffff to 0 test fails for other reasons: 0 is even and unknown, and there's no length field. I prefer tests which test one thing at a time; I'll change n2s tlv1 to be 0 type, then this can be an invalid under n2 test: 0xffffffffffffffffff 00 00 00

The way i have it implemented, we check that the types are canonically ordered before parsing the length (or checking if the type is known).

I think this is also relevant to my other comment where the canonical varint check for the type is being applied after parsing the length. In our implementation we check that each varint is canonical at the time it is parsed

(I think this last bit might be resolved if the varint also passed the BigSize test vectors?)

@cfromknecht
Copy link
Copy Markdown
Collaborator

I'll proceed in updating to the latest test vectors and report back :)

@rustyrussell
Copy link
Copy Markdown
Collaborator Author

OK, so @cfromknecht 's ffff to 0 test fails for other reasons: 0 is even and unknown, and there's no length field. I prefer tests which test one thing at a time; I'll change n2s tlv1 to be 0 type, then this can be an invalid under n2 test: 0xffffffffffffffffff 00 00 00

The way i have it implemented, we check that the types are canonically ordered before parsing the length (or checking if the type is known).

I think this is also relevant to my other comment where the canonical varint check for the type is being applied after parsing the length. In our implementation we check that the varint is canonical at the time it is parsed

Ah, OK. We run our checks in the following order instead (kinda arbitrary, really):

  1. check type decodes.
  2. check length decodes.
  3. check there's enough data for length.
  4. check type ordering.
  5. lookup type.
  6. if found:
    1. Decode value. If that fails, fail.
    2. Check no bytes remain.
  7. Otherwise:
    1. If type is even, fail.
    2. if type is odd, skip.

It's nice to have implementations do it in different orders, though...

@cfromknecht
Copy link
Copy Markdown
Collaborator

cfromknecht commented Jul 16, 2019

That makes sense then :)

We do

  1. check type decodes and canonical
  2. check type ordering
  3. check length decodes and canonical
  4. lookup type
  5. if found:
    1. Decode value. If that fails, fail.
  6. Otherwise:
    1. If type is even, fail.
    2. If type is odd, skip.

The check that there's enough data for length is applied while decoding or discarding the value

rustyrussell added a commit to rustyrussell/lightning that referenced this pull request Jul 16, 2019
lightning/bolts#631

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@t-bast
Copy link
Copy Markdown
Collaborator

t-bast commented Jul 16, 2019

Tested-ACK d70fe60

Copy link
Copy Markdown
Collaborator

@cfromknecht cfromknecht left a comment

Choose a reason for hiding this comment

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

@rustyrussell @t-bast updated our implementation with the latest test vectors, I think we're about ready! Noticed a few small things, but as is our implementation is all green 🚀

Comment thread 01-messaging.md
1. Invalid stream: 0x1f 00 1f 01 2a
2. Reason: duplicate TLV type (ignored)

The following TLV stream in namespace `n2` should trigger a decoding
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i'm wondering, should this produce an error in either namespace? sending a non-canonical stream is more severe than sending an unknown required type, so failing with unknown required type on n1 would effectively downgrade the error. if we were to standardize something like:

- if stream is not canonical:
   - close channel
- if stream has unknown required type:
   - disconnect

our implementations will behave differently

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The purpose of the toy example was to just to highlight that we might need take different actions when encountering a parsing failure vs a negotiation failure.

With the current proposal feature negotiation is bundled alongside message deserialization, so we are forced to differentiate the errors at the tlv encoding level and be consistent across implementations.

Comment thread 01-messaging.md
Comment thread 01-messaging.md
@t-bast
Copy link
Copy Markdown
Collaborator

t-bast commented Jul 17, 2019

It's probably a nit, but I'm not a big fan of the name BigSize. It's not obvious at all that the Big is for big-endian especially since we're doing big-endian everywhere (so big-endian should be implicit). It sounds a lot like a BigInt type which is really something else.
What about CompactSizeBE instead? Or we don't name it at all and just mention that varint is a variable-length, unsigned integer encoding using Bitcoin's CompactSize format with big-endian encoding instead of little-endian?
WDYT?

@cfromknecht
Copy link
Copy Markdown
Collaborator

It's not obvious at all that the Big is for big-endian especially since we're doing big-endian everywhere (so big-endian should be implicit). It sounds a lot like a BigInt type which is really something else.

It's supposed to be a somewhat of a pun :P

What about CompactSizeBE instead?

Was hoping for something a little less bland tbh

Or we don't name it at all and just mention that varint is a variable-length, unsigned integer encoding using Bitcoin's CompactSize format with big-endian encoding instead of little-endian?
WDYT?

I'd prefer not to have this verbose description everywhere we want to reference it, I think we need some name just to make it easy to refer to in the spec. It was already painful enough to write it twice.

Didn't want to waste too much time on a name when drafting the proposal, and so just went with BigSize. Definitely open to more suggestions if you have any! :)

rustyrussell added a commit to rustyrussell/lightning that referenced this pull request Jul 18, 2019
lightning/bolts#631

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
rustyrussell added a commit to ElementsProject/lightning that referenced this pull request Jul 18, 2019
lightning/bolts#631

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell rustyrussell mentioned this pull request Jul 18, 2019
@t-bast
Copy link
Copy Markdown
Collaborator

t-bast commented Jul 18, 2019

I don't feel very strongly about the name and don't have a better choice than either BigSize or CompactSizeBE, I just want it to be simple to understand for spec readers without too much context.
If you and @rustyrussell like BigSize, let's go for it ;)

@niftynei niftynei added Meeting Discussion Raise at next meeting and removed Meeting Discussion Raise at next meeting labels Jul 22, 2019
For some reason (typo?) we only allowed "2", not other numbers!

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We were swallowing the unused line after `data`, but it's
normal to do:

```
1. tlvs: `n1`
2. types:
   1. type: 1 (`tlv1`)
   2. data:
     * [`tu64`:`amount_msat`]
   1. type: 2 (`tlv2`)
   2. data:
     * [`short_channel_id`:`scid`]
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We didn't explicitly say that the TLV is bad if length exceeds
the message length!

We didn't specify whether to ignore extra bytes: we should.
Similarly, contents of values must be minimal (i.e. tu64 etc).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
These are based on @t-bast's vectors from lightning#607, with a few more
cases:

1. Explicitly test encodings for 253, 254 and 255.
2. Use BigSize and make sure tests break badly if endian parsing is wrong.'
3. Test wrap-around of type encodings in stream.

Many thanks to @t-bast and @cfromknecht for their contributions and testing

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
Copy link
Copy Markdown
Collaborator Author

Rebased and squashed commits ready for merge.

@rustyrussell rustyrussell merged commit aa33af0 into lightning:master Jul 22, 2019
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.

5 participants