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

Add pdd protocol and multistream example as .md files for easier review and upgrade collaboratively #12

Merged
merged 6 commits into from
Jun 27, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions pdd/PDD-multistream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# PDD for MultiStream (example/use case)

## Protocol

Defined here:
- https://github.com/ipfs/specs/blob/wire/protocol/network/wire.md#multistream---self-describing-protocol-stream
- https://github.com/ipfs/specs/blob/wire/protocol/network/wire.md#multistream-selector---self-describing-protocol-stream-selector

## Protocol Compliance Tests Spec

Given the protocol spec, an implementation of the multistream-select protocol has to comply with the following scenarios:

#### 1 - push-stream

In a push-stream example (one-way stream), we have two agents:

- 'broadcast' - where messages are emited
- 'silent' - listener to the messages emited by the broadcast counterparty

Compliance test 1 (human readable format):
```
# With a connection established between silent - broadcast
< /multistream/1.0.0 # multistream header
< /bird/3.2.1 # Protocol header
< hey, how is it going? # First protocol message
```
Copy link
Member

Choose a reason for hiding this comment

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

this is super nice.

Copy link
Contributor

Choose a reason for hiding this comment

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

On Fri, Jun 12, 2015 at 04:45:57PM -0700, Juan Batiz-Benet wrote:

+# With a connection established between silent - broadcast
+< /multistream/1.0.0 # multistream header

  • < /bird/3.2.1 # Protocol header
  • < hey, how is it going? # First protocol message

This is the multistream-select view (because of the nested streams),
but for conversations like this where the streams are strictly nested
(e.g., no going back to an ancestor stream after saying something in a
child stream), you could also run this test against a non-multiplexed
(uniplexed?) multistream implementation. Elaborating on the
test-harness changes for the multiplexed transport:

  1. Spin up connection to implementation, attaching the
    multistream-select driver. We probably want a
    ‘/multistream-select/{version}’ handshake (or optimistic
    broadcast?) to make sure we're speaking the same multistream-select
    language (or at least can figure out what went wrong in a
    brodcast-to-silent situation).
  2. Send the ‘/multistream/1.0.0’ message through the
    multistream-select driver.
  3. Get a new multiplexed stream (I'm not sure which agent is
    responsible for creating the new stream) and attach the
    multistream-1.0.0 driver.
  4. Send the ‘/bird/3.2.1’ message through the multistream driver.
  5. Get a new multiplexed stream and attach the bird-3.2.1 driver.
  6. Send the ‘hey, how is it going?’ message through the bird driver.
  7. Teardown streams?

While for the uniplexed transport, we'd have:

  1. Spin up connection to implementation, attaching the
    multistream-select driver. We probably want a
    ‘/multistream-select/{version}’ handshake (or optimistic
    broadcast?) to make sure we're speaking the same multistream-select
    language (or at least can figure out what went wrong in a
    brodcast-to-silent situation).
  2. Send the ‘/multistream/1.0.0’ message through the
    multistream-select driver.
  3. Disconnect the multistream-select driver and attach the
    multistream-1.0.0 driver to the stream.
  4. Send the ‘/bird/3.2.1’ message through the multistream driver.
  5. Disconnect the multistream driver and attach the bird-3.2.1 driver
    to the stream.
  6. Send the ‘hey, how is it going?’ message through the bird driver.
  7. Teardown stream?

The test-suite's transport driver should have a IsMultiplexed() check
so it can decide which of these approaches to take (or so it can error
out if you ask it to run a multiplex-requiring transcript over a
uniplexed transport).


#### 2 - duplex-stream

In a duplex-stream example (interactive conversation), we have two agents:

- 'select' - waiting for connections, hosts several protocols from where a client can pick from
- 'interactive' - connects to a select agent and queries that agent for a specific protocol

Compliance test 2 (human readable format):
```
# With a connection established between interactive - select
< /multistream/1.0.0
> /multistream/1.0.0
> ls
< ["/dogs/0.1.0","/cats/1.2.11"]
> /mouse/1.1.0
< na
> /dogs/0.1.0
< /dogs/0.1.0
> hey
```
Copy link
Member

Choose a reason for hiding this comment

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

ideally the test could try to fuzz all possible message orderings. e.g.:

# order 1
< /multistream/1.0.0
> /multistream/1.0.0
> ls
< ["/dogs/0.1.0","/cats/1.2.11"]
> /mouse/1.1.0
< na
> /dogs/0.1.0
< /dogs/0.1.0
> hey

# order 2
> /multistream/1.0.0
< /multistream/1.0.0
> ls
< ["/dogs/0.1.0","/cats/1.2.11"]
> /mouse/1.1.0
< na
> /dogs/0.1.0
< /dogs/0.1.0
> hey

# order 3
> /multistream/1.0.0
> ls
< /multistream/1.0.0
< ["/dogs/0.1.0","/cats/1.2.11"]
> /mouse/1.1.0
< na
> /dogs/0.1.0
< /dogs/0.1.0
> hey

... and so on down ...

# another (this one is weird from an "interactivity" perspective
# but --for correctness-- it should be tested to ensure things behave
# as expected).
> /multistream/1.0.0
> ls
> /mouse/1.1.0
> /dogs/0.1.0
> hey
< /multistream/1.0.0
< ["/dogs/0.1.0","/cats/1.2.11"]
< na
< /dogs/0.1.0

not sure if this is worth worrying about or not

Copy link
Contributor

Choose a reason for hiding this comment

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

On Thu, Jun 11, 2015 at 08:55:04PM -0700, Juan Batiz-Benet wrote:

ideally the test could try to fuzz all possible message orderings…

How do you know what the possible message orderings are without a
protocol spec to tell you?


## Wire out

Since this protocol is not fully plaintext, we have to capture the messages/packets that transmited by one of the agents to make sure we get the transformations right (and therefore doing development driven by what is going on in the wire, which is defined by the Protocol (PDD ftw!))

With a first implementation written, we can capture the messages switched on the wire, so that later, we can require other implementations to conform. For the multistream scenario, tests are organized by the following:

```
tests
├── comp # Where compliance tests live
│   ├── compliance-test.js
├── impl # Where specific implementation tests live, where we can get code coverage and all that good stuff
│   ├── interactive-test.js
│   └── one-way-test.js
└── spec # Spec tests are the tests were what is passed on the wire is captured, so it can be used in the compliance tests for all the implementations
├── capture.js
├── interactive-test.js
├── one-way-test.js
└── pristine # The pristine folder were those captures live
├── broadcast.in
├── broadcast.out # A broadcast.out is the same as a silent.in, since there are only two agents in this exchange,
├── interactive.in # the reason both files exist is to avoid mind bending when it is time to use the "in/out", it could get confusing
├── interactive.out
├── select.in
├── select.out
├── silent.in
└── silent.out
```

## Protocol Compliance Test Suit
Copy link
Member

Choose a reason for hiding this comment

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

typo: Suite


The protocol compliance test suit for multistream-select can be found on `tests/comp/compliance-test.js`, each agent is tested alone with the input we have prepared on the previous step for it, once that agent replies to all the messages, we compare (diff) both the output generated and its "pristine" counterpart, expecting to get 0 differences.
81 changes: 81 additions & 0 deletions pdd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
RFC {protocol hash} - Protocol Driven Development
====================================================================================

# Abstract

# Introduction

Cross compatibility through several implementations and runtimes is historically an hard goal to achieve. Each framework/language offers different testing suits and implement a different flavour of testing (BDD, TDD, RDD, etc). We need a better way to test compatibility across different implementations.

Instead of the common API tests, we can achieve cross implementation testing by leveraging interfaces offered through the network and defined by the Protocol. We call this Protocol Driven Development.

In order for a code artefact to be PDD compatible
- Expose a connection (duplex stream) interface, may be synchronous (online, interactive) or asynchronous.
- Implement a well defined Protocol spec

## Objectives

The objectives for Protocol Driven Development are:
- Well defined process to test Protocol Spec implementations
- Standard definition of implementation requirements to comply with a certain protocol
- Automate cross implementation tests
- Have a general purpose proxy for packet/message capture

# Process

In order to achieve compliance, we have to follow four main steps:
1 - Define the desired Protocol Spec that is going to be implemented
2 - Design the compliance tests that prove that a certain implementation conforms with the spec
3 - Once an implementation is done, capture the messages traded on the wire using that implementation, so that the behaviour of both participants can be replicated without the agent
4 - Create the Protocol Compliance Tests (consisting on injecting the packets/messages generated in the last step in the other implementations and comparing outputs)

## Protocol Spec

Should define the goals, motivation, messages traded between participants and some use cases. It should not cover language or framework specifics.

## Protocol Compliance Tests Spec

Defines what are the necessary “use stories” in which the Protocol implementation must be tested to assure it complies with the Protocol Spec. For e.g:

```
# Protocol that should always ACK messages of type A and not messages of type B
> A
{< ACK}
> B
> B
> B
```

**Message Flow DSL:**
- Indentation to communicate a dependency (a ACK of A can only come after A is sent for e.g)
- [ ] for messages that might or not appear (e.g heartbeats should be passed on the wire from time to time, we know we should get some, but not sure how much and specifically when).
- { } for messages that will arrive, we just can't make sure if before of the following messages described

A test would pass if the messages transmitted by an implementation follow the expected format and order, defined by the message flow DSL. The test would fail if format and order are not respected, plus if any extra message is transmitted that is was not defined.

Tests should be deterministic, so that different implementations produce the same results:
```
┌─────────┐ ┌─────────┐ ┌───────────────┐
│input.txt│──┬─▶│go-impl │───▶│ output.go.txt │
└─────────┘ │ └─────────┘ └───────────────┘
│ ┌─────────┐ ┌───────────────┐
└─▶│node-impl├───▶│output.node.txt│
└─────────┘ └───────────────┘
```

So that a diff between two results should yield 0 results

```
$ diff output.go.txt output.node.txt
$
```

## Interchange Packet/Message Capture

Since most of these protocols define leverage some type of encoded format for messages, we have to replicate the transformations applied to those messages before being sent. The other option is capturing the messages being sent by one of the implementations, which should suffice the majority of the scenarios.

## Protocol Compliance Tests Suite

These tests offer the last step to test different implementations independently. By sending the packets/messages and evaluating their responses and comparing across different implementations, we can infer that in fact they are compatible

#### [Example use case - go-multistream and node-multistream tests](/PDD-multistream.md)