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

Refactor message serialization as a tokio codec. #22

Merged
merged 8 commits into from
Sep 25, 2019
Merged

Conversation

hdevalence
Copy link
Contributor

Closes #20.

This lets us transform a TCP stream into an async stream of Messages rather than having to call send/recv directly.

This allows us to organize all of the Bitcoin-Zcash specific parts of
the protocol into a subtree.
This provides a significantly cleaner API to consumers, because it
allows using adaptors that convert a TCP stream to a stream of messages,
and potentially allows more efficient message handling.
@hdevalence
Copy link
Contributor Author

(This is still a draft because there was one bit of cleanup I wanted to do on the try_read_ functions).

@hdevalence hdevalence marked this pull request as ready for review September 24, 2019 23:15
@hdevalence hdevalence mentioned this pull request Sep 24, 2019
// XXX(HACK): this is inefficient and does an extra allocation.
// instead, we should have a size estimator for the message, reserve
// that much space, write the header (with zeroed checksum), then the body,
// then write the computed checksum in-place. for now, just do an extra alloc.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could record this refactor in an issue but I think saving an extra alloc is much less important than other stuff right now. However when we do get around to doing it properly, the tokio codec setup will let us perform a single allocation per message.

// ======== Decoding =========

#[derive(Debug)]
enum DecodeState {
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 tracks the decoder state; since decode can be called multiple times (see below) we need to track what phase (header/body) we're in and absorb the header contents (the decoder is responsible for removing parsed data from the buffer).

type Error = Error;

#[instrument(skip(src))]
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

decode returns

  • Err to signal an error;
  • Ok(None) to signal insufficient data;
  • Ok(Some(msg)) when an entire item is ready.

);

// Reserve buffer space for the expected body and the following header.
src.reserve(body_len + HEADER_LEN);
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 tokio docs recommend reserving body_len + HEADER_LEN, so that no more allocations are required until after reading the next header.

// Now that we know we have the full body, split off the body,
// and reset the decoder state for the next message.
let body = src.split_to(body_len);
self.state = DecodeState::Head;
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 we remove the body, we have to reset the decoder state to DecodeState::Head or we'll try to read over the next message header as a body with the same type and checksum as the current message, causing a checksum error (I forgot this step at first 😓 )

Copy link
Contributor

Choose a reason for hiding this comment

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

This would be good to have as a comment inline, just in case.

Copy link
Contributor

@dconnolly dconnolly left a comment

Choose a reason for hiding this comment

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

codec.rs is quite large, if there's a way to break it up somehow that would be nice.

zebra-network/src/network.rs Show resolved Hide resolved
zebra-network/src/protocol/codec.rs Show resolved Hide resolved
zebra-network/src/protocol/codec.rs Outdated Show resolved Hide resolved
zebra-network/src/protocol/codec.rs Show resolved Hide resolved
@hdevalence
Copy link
Contributor Author

Fixed the constant placement and moved the header encoding to be prior to the body encoding. I agree that the file is larger than desirable but I'm not sure there's a great way to split up its contents.

This is no longer required because the body reader methods have access
to the version via the codec state.
Copy link
Contributor

@dconnolly dconnolly left a comment

Choose a reason for hiding this comment

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

🚚

@hdevalence hdevalence merged commit fe95ad3 into main Sep 25, 2019
@hdevalence hdevalence deleted the tokio-codec branch September 25, 2019 21:59
skyl added a commit to skyl/zebra that referenced this pull request Sep 25, 2024
…ion#22)

## What

Make it so that creators can become stars/fans of eachother, "subscribe"
and creators who `can_stream` can go live and their subscribers can join
the "webinars" but also request the stage.

## Why

Seems like it would be really cool.
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.

Change message encoding / decoding to be a Tokio codec.
2 participants