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

New Tool: Build a Flow SDK [Rust] - Milestone 1 #37

Merged
merged 1 commit into from
Sep 25, 2021
Merged
Changes from all commits
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
119 changes: 119 additions & 0 deletions submissions/issue-20/milestone-1/fee1-dead/20210919-milestone-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Design of `flow-sdk`, a Rust Flow SDK

## gRPC

The `prost` crate is used to generate rust code from protobuf source files.

A trait named `GrpcClient` is used to generalize gRPC implementations.
This makes the crate easier to test.

```rust
trait GrpcClient<I, O> {
type Output;
fn send(self, input: I) -> Self::Output;
}
```

The two generic parameters, `I` and `O` denote the request and the response object
types. For example, a `GrpcClient` capable of handling `Ping` requests will
implement the trait `GrpcClient<PingRequest, PingResponse>`.

The return type of `send` is not the same as `O`, because it could be returning a
`Future` and/or a `Result`. We still expect `Self::Output` to be related to `O` in
some way.

To make it simpler to implement, there is a trait called `FlowRequest` that is
implemented for every request type for the Flow Access API.
([src](https://github.com/fee1-dead/flow.rs/blob/master/src/requests.rs))
Note that this depends on the fact that there are no requests with the same input
type and output type (only differs in the request name). This design would need to
be reconsidered when it turns out otherwise.

Then, the crate defines a wrapper type `FlowClient` which provides helper functions
for `GrpcClient` implementations. A helper functions can be written as:

```rust
impl<Inner> FlowClient<Inner> {
pub fn fn_name<'a>(&'a mut self, args..) -> Inner::Output
where &'a mut Inner: GrpcClient<InputTy, OutputTy>
{
self.send(/* Request Construct */)
}
}
```

With the help of a macro, it becomes very simple to add new functions:
([src](https://github.com/fee1-dead/flow.rs/blob/82510b45d3b14b179192a5d2bccec87c932d7819/src/client.rs#L67-L77))

```rust
define_reqs! {
/// Shortcut for `self.send(PingRequest {})`.
pub fn ping() PingRequest => PingResponse {
PingRequest {}
}

/// Returns information of the latest block.
pub fn latest_block_header(is_sealed: bool)
GetLatestBlockHeaderRequest => BlockHeaderResponse
{
GetLatestBlockHeaderRequest { is_sealed }
}
}
```

A `FlowClient` instance can be easily created, via the `tonic` and `hyper` crate:

```rust
pub type TonicFlowClient<Service> = FlowClient<Grpc<Service>>;
pub type TonicHyperFlowClient = TonicFlowClient<Channel>;

impl TonicHyperFlowClient {
pub fn mainnet() -> Result<Self, tonic::transport::Error> {
Ok(Self {
inner: Grpc::new(
Channel::from_static("http://access.mainnet.nodes.onflow.org:9000").connect_lazy()?,
),
})
}
}
```

## User Stories

Most user stories can be implemented with associated functions for `FlowClient`s.
i.e. a function that the user can call through the Access API.

Some will require parsing JSON-Cadence Data. (such as "submit a script and parse
the response") I plan to do this with `serde-json` or a crate providing similar
functionality (probably split to a new crate that parses JSON-Cadence)

Some will require signing, which will use the `k256` crate or another better crate
with the same functionality.

Blocks:
- retrieve a block by ID - Access API
- retrieve a block by height - Access API
- retrieve the latest block - Access API

Collections:
- retrieve a collection by ID - Access API

Events:
- retrieve events by name in the block height range - Access API

Scripts:
- submit a script and parse the response - Needs parsing
- submit a script with arguments and parse the response - Needs parsing

Accounts:
- retrieve an account by address - Access API
- create a new account - A transaction with provided cadence template
- deploy a new contract to the account - A transaction with provided cadence template
- remove a contract from the account - A transaction with provided cadence template
- update an existing contract on the account - A transaction with provided cadence template

Transactions:
- retrieve a transaction by ID - Access API - Arguments of the transaction will eventually be parsed or used as plain text only
- sign a transaction (single payer, proposer, authorizer or combination of multiple) - Requires Cryptography
- submit a signed transaction - Requires Cryptography and Access API
- sign a transaction with arguments and submit it - Requires Cryptography and Access API - Arguments need to be serialized as Cadence-JSON