-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #37 from fee1-dead/main
New Tool: Build a Flow SDK - Milestone 1
- Loading branch information
Showing
1 changed file
with
119 additions
and
0 deletions.
There are no files selected for viewing
119 changes: 119 additions & 0 deletions
119
submissions/issue-20/milestone-1/fee1-dead/20210919-milestone-1.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |