Skip to content

Commit

Permalink
Merge pull request #37 from fee1-dead/main
Browse files Browse the repository at this point in the history
New Tool: Build a Flow SDK - Milestone 1
  • Loading branch information
psiemens authored Sep 25, 2021
2 parents f009008 + 1c40e28 commit 6fd51ca
Showing 1 changed file with 119 additions and 0 deletions.
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

0 comments on commit 6fd51ca

Please sign in to comment.