Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Commit

Permalink
feat: implement dealer api (#15)
Browse files Browse the repository at this point in the history
* feat: implement dealer api

* chore: address review comments
  • Loading branch information
vasco-santos authored Sep 7, 2023
1 parent 321d002 commit a785d80
Show file tree
Hide file tree
Showing 37 changed files with 3,608 additions and 4,287 deletions.
4 changes: 2 additions & 2 deletions .env.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

# uncomment to try out deploying the api under a custom domain.
# the value should match a hosted zone configured in route53 that your aws account has access to.
# HOSTED_ZONE=spade-proxy.web3.storage
# HOSTED_ZONE=dealer.web3.storage

# uncomment to set SENTRY_DSN
# SENTRY_DSN = ''

SPADE_PROXY_DID = ''
DID = ''

UCAN_LOG_URL = ''
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# spade-proxy
# dealer

> A proxy API which will provide the UCAN server for receiving `aggregate/*` invocations and other resources for Spade to scrape - a way to retrieve the list of aggregates ready for a deal.
> Implementation of w3filecoin dealer service which will provide the UCAN server for receiving `dealer/*` invocations and other resources for Spade to scrape - a way to retrieve the list of aggregates ready for a deal.
## Getting Started

Expand Down Expand Up @@ -69,11 +69,11 @@ Ensure the following variables are set in the env when deploying

#### `HOSTED_ZONE`

The root domain to deploy the API to. e.g `spade-proxy.web3.storage`. The value should match a hosted zone configured in route53 that your aws account has access to.
The root domain to deploy the API to. e.g `dealer.web3.storage`. The value should match a hosted zone configured in route53 that your aws account has access to.

#### `SPADE_PROXY_DID`
#### `DID`

[DID](https://www.w3.org/TR/did-core/) of the ucanto server. e.g. `did:web:spade.storage`. Optional: if omitted, a `did:key` will be derrived from `PRIVATE_KEY`
[DID](https://www.w3.org/TR/did-core/) of the ucanto server. e.g. `did:web:dealer.web3.storage`. Optional: if omitted, a `did:key` will be derrived from `PRIVATE_KEY`

### Secrets

Expand Down
Binary file added docs/architecture.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 91 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Architecture

> The dealer architecture.
## Background

[web3.storage](http://web3.storage) is a Storefront providing APIs to enable users to easily upload CAR files, while getting them available on the IPFS Network and stored in multiple locations via Filecoin Storage Providers.

A dealer facilitates storing data with Filecoin Storage Providers. It receives requests from a Storefront to get their data stored. Dealers MAY have requirements like minimum/maximum size of a Filecoin Piece. Given that Storefront actors handles files of any size, a Storefront can also rely on Aggregators to pack sets of CAR files together in order to get big Filecoin Pieces that can be queued for Storing to the Dealer. You can read more on the [w3filecoin spec](https://github.com/web3-storage/specs/blob/main/w3-filecoin.md).

The dealer relies on Spade as a broker to get their user data into Filecoin Storage Providers. Currently, Spade requires a Filecoin Piece with size between 16GiB and 32GiB to create deals with Filecoin Storage Providers. Moreover, the closer a Filecoin Piece is closer to the upper bound, the most optimal are the associated storage costs.

## High Level design

The dealer is modeled into 3 different SST Stacks that will have their infrastructure provisioned in AWS via AWS CloudFormation. These are:

- API Stack
- Processor Stack
- Data Stack

![Architecture](./architecture.jpg)

## API Stack

The dealer stack has an API that authorized _Storefront_s can use in order to:

- submit deal _offers_s to get Filecoin deals with Storage Providers.
- `offer` is added to the `offer-store` and added to the queue processor pipeline. See [offer schema](#offer-store-schema).
- once processed, it is added to the `deal-store` to enable tracking of whether given offer makes its way to Storage Providers. As a result, a receipt will be processed.

## Processor Stack

When deal _offers_s are submitted into the dealer, the dealer queues the offer to be handled in the `dealer-queue`. Once the queue consumer is called, it stores the deal added into the `deal-store` to be tracked.

TODO: CRON Job for tracking deals and self invoke `/add` to generate receipt

## Data Stack

The Data stack is responsible for the state of the dealer. It stores the received deal offers and keepts track of the submitted deal requests state over time.

| store | type | key |
|---------------|----------|-------------------------|
| offer-store | S3 | `${nowDate} {aggregate}`|
| deal-store | DynamoDB | `aggregate` |

### `offer-store` schema

```typescript
interface Offer {
// CID of the piece `bagy...content`
aggregate: PieceCID
// CID of all the segments part of the aggregate
pieces: PieceCID[]
// identifier of the tenant for the storefront `did:web:web3.storage`
// spade relies on tenant naming, so we map it here to tenant
tenant: string
// enables ordering of offers to handle.
// Being it the number of ms since epoch, also means offers that fail and are retried will have "priority"
// It can also have lower numbers to prioritize certain actors in the future
orderID: string
}
```

### `deal-store` schema

```typescript
interface Deal {
// CID of the piece `bagy...content` (primary index, partition key)
aggregate: PieceCID
// identifier of the storefront `did:web:web3.storage`
storefront: string
// encoded URI with location of the offer, e.g. 's3://${bucket}/${key}'
offer: string
// known status of the deal (a secondary index)
// Note: DynamoDB does not allow usage of status keyword
stat: DealStatus
}

type DealStatus =
| OFFERED
| APPROVED
| REJECTED

type OFFERED = 0
type APPROVED = 1
type REJECTED = 2
```
## Spade integration
Spade will read new offers available from the `offer-store` once it has availability to process more deals. Thanks to the `orderId` property, it will be able to sort submitted deals by desired priority.
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "spade-proxy",
"name": "dealer",
"version": "0.0.0",
"private": true,
"type": "module",
Expand All @@ -16,9 +16,15 @@
"clean": "rm -rf node_modules pnpm-lock.yml packages/*/{pnpm-lock.yml,.next,out,coverage,.nyc_output,worker,dist,node_modules}"
},
"devDependencies": {
"@ipld/dag-ucan": "3.4.0",
"@sentry/serverless": "^7.52.1",
"@types/git-rev-sync": "^2.0.0",
"@types/node": "^18.16.3",
"@ucanto/client": "8.0.0",
"@ucanto/principal": "8.0.0",
"@ucanto/transport": "8.0.0",
"@web3-storage/filecoin-api": "^1.4.3",
"@web3-storage/filecoin-client": "1.3.0",
"sst": "^2.8.3",
"aws-cdk-lib": "2.72.1",
"constructs": "10.1.156",
Expand Down
61 changes: 0 additions & 61 deletions packages/core/buckets/offer-store.ts

This file was deleted.

27 changes: 18 additions & 9 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
{
"name": "@spade-proxy/core",
"name": "@dealer/core",
"version": "0.0.0",
"type": "module",
"scripts": {
"test": "ava --serial --no-worker-threads --verbose --timeout=60s 'test/**.test.js'",
"test": "ava --serial --no-worker-threads --verbose --timeout=60s test/{*.test.js,**/*.test.js}",
"typecheck": "tsc --build"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.338.0",
"@aws-sdk/client-s3": "^3.338.0",
"@aws-sdk/util-dynamodb": "^3.338.0",
"@aws-sdk/client-dynamodb": "^3.398.0",
"@aws-sdk/client-sqs": "^3.398.0",
"@aws-sdk/client-s3": "^3.398.0",
"@aws-sdk/util-dynamodb": "^3.398.0",
"@ipld/dag-json": "^10.1.3",
"@ipld/dag-ucan": "3.3.2",
"@ucanto/interface": "^8.0.0",
"@ucanto/principal": "^8.0.0",
"@web3-storage/aggregate-api": "^1.0.0",
"@web3-storage/aggregate-client": "^1.0.0",
"@web3-storage/capabilities": "^7.0.0",
"p-retry": "^5.1.2"
"@ucanto/server": "^8.0.2",
"@ucanto/transport": "^8.0.0",
"@web3-storage/filecoin-api": "^1.4.3",
"@web3-storage/filecoin-client": "^1.3.0",
"@web3-storage/capabilities": "^9.2.1",
"@web3-storage/data-segment": "^3.0.1",
"p-retry": "^5.1.2",
"uint8arrays": "^4.0.6"
},
"devDependencies": {
"@types/node": "^18.16.3",
"@web-std/blob": "^3.0.4",
"ava": "^5.3.1",
"delay": "^6.0.0",
"nanoid": "^4.0.2",
"p-wait-for": "^5.0.2",
"sqs-consumer": "^7.2.2",
"sst": "^2.8.3",
"testcontainers": "^9.10.0",
"typescript": "^5.0.4"
Expand Down
31 changes: 0 additions & 31 deletions packages/core/service.ts

This file was deleted.

61 changes: 61 additions & 0 deletions packages/core/src/data/deal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { PieceLink } from '@web3-storage/data-segment'
import { parseLink } from '@ucanto/server'

export const encode = {
/**
* Encode data structure to store record and propagate pointer to it.
*/
record: function (record: Deal): EncodedDeal {
return {
aggregate: record.aggregate.toString(),
storefront: record.storefront,
stat: Status.Offered,
offer: record.offer,
insertedAt: record.insertedAt
} satisfies EncodedDeal
},
key: function (record: Deal): EncodedKey {
return {
aggregate: record.aggregate.toString(),
}
}
}

export const decode = {
/**
* Decode deal data structure from stored record.
*/
record: function (storeRecord: EncodedDeal): Deal {
return {
...storeRecord,
aggregate: parseLink(storeRecord.aggregate),
}
}
}

export enum Status {
Offered = 0,
Approved = 1,
Rejected = 2
}
export type UnixTime = number

export type Deal = {
aggregate: PieceLink
storefront: string
offer: string
stat: Status
insertedAt: UnixTime
}

export type EncodedDeal = {
aggregate: string
storefront: string
offer: string
stat: Status
insertedAt: UnixTime
}

export type EncodedKey = {
aggregate: string
}
Loading

0 comments on commit a785d80

Please sign in to comment.