From 7a73c9b9f90bf4f38a9ae2a8dfb874c1cffbe7f3 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 17 Jul 2023 17:19:58 -0700 Subject: [PATCH 1/3] feat: w3-deal protocol --- w3-deal.md | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 w3-deal.md diff --git a/w3-deal.md b/w3-deal.md new file mode 100644 index 00000000..5c7206ff --- /dev/null +++ b/w3-deal.md @@ -0,0 +1,217 @@ +# Filecoin Deal Signing Protocol + +![status:wip](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) + +## Editors + +- [Irakli Gozalishvili], [Protocol Labs] + +## Authors + +- [Irakli Gozalishvili], [Protocol Labs] + +# Abstract + +This specification describes a [UCAN] protocol allowing a broker like [spade] to request signing a storage deal. + +## Language + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119](https://datatracker.ietf.org/doc/html/rfc2119). + +# Terminology + +## Roles + +There are several roles that actors can have in this protocol: + +| Name | Description | +| ----------- | ----------- | +| Storefront | [Principal] identified by [`did:web`] identifier, representing a storage aggregator like w3up | +| Broker | [Principal] that arranges filecoin deals with storage providers like [spade] | +| Agency | [Principal] submitting pieces to the _Broker_ like spade-proxy | + +### Storefront + +A _Storefront_ is a type of [principal] identified by a [`did:web`] that aggregates user data into [aggregate][] pieces and submits those to the broker to arrange deals with storage providers. + +### Broker + +A _Broker_ is a type of [principal] identified that arranges deals for the aggregates submitted by _Storefront_. + +# Protocol + +## Overview + +All the filecoin deals need to be signed by a Fil wallet, in order to avoid passing private keys to wallet _Storefront_ could delegate a capability to a sign a deal to a _Broker_ instead. + +Here we propose set of UCAN capabilities that can be invoked by authorized actors (like _Broker_) to sign deals such deals. + +## IPLD Schema + +```ipldsch +type Deal union { + | Sign "deal/sign" +} representation inline { + discriminantKey "can" +} + +type Sign struct { + with StorefrontDID + nb DealProposal +} + +# @see https://github.com/filecoin-project/go-state-types/blob/ff2ed169ff566458f2acd8b135d62e8ca27e7d0c/builtin/v9/market/deal.go#L201-L221 +# We use capital case letters because that the way go likes them +type DealProposal struct { + PieceCID &Piece + PieceSize PieceSize + VerifiedDeal bool + # Signer wallet (f0) address + Client Address + # Storage provider wallet (f0) address for whom the contract is made + Provider Address + # Label is an arbitrary client chosen label to apply to the deal + Label DealLabel + + # Nominal start epoch. Deal payment is linear between StartEpoch and EndEpoch, + # with total amount StoragePricePerEpoch * (EndEpoch - StartEpoch). + # Storage deal must appear in a sealed (proven) sector no later than StartEpoch, + # otherwise it is invalid. + StartEpoch ChainEpoch + EndEpoch ChainEpoch + StoragePricePerEpoch TokenAmount + + ProviderCollateral TokenAmount + ClientCollateral TokenAmount +} + +type StorefrontDID string + +# Piece CID is Piece / Aggregate merkle root encoded as CID +type Piece = Any + +# @see https://github.com/filecoin-project/go-state-types/blob/ff2ed169ff566458f2acd8b135d62e8ca27e7d0c/abi/piece.go#L12 +type PaddedPieceSize = Uint64 + +# @see https://github.com/filecoin-project/go-address/blob/master/address.go#L39-L40 +type Address struct { addr: string } + + +# The DealLabel is a kinded union of string or byte slice. +# It serializes to a CBOR string or CBOR byte string depending on which form it +# takes. +# The zero value is serialized as an empty CBOR string (maj type 3). +# @see https://github.com/filecoin-project/go-state-types/blob/ff2ed169ff566458f2acd8b135d62e8ca27e7d0c/builtin/v9/market/deal.go#L37C1-L44C1 +type DealLabel struct { + bs Bytes + notString bool +} + +# @see https://github.com/filecoin-project/go-state-types/blob/ff2ed169ff566458f2acd8b135d62e8ca27e7d0c/abi/chain.go#L9C1-L10 +type ChainEpoc = Int64 + +# @see https://github.com/filecoin-project/go-state-types/blob/ff2ed169ff566458f2acd8b135d62e8ca27e7d0c/abi/chain.go#L16-L22 +type TokenAmount = SerializedBigInt + +# In principal IPLD Int range is unlimited, but in practice it is +# something implementations need to worry about so it's best to +# specify +# @see https://ipld.io/design/tricky-choices/numeric-domain/#integers +type Uint64 = Int +type Int64 = Int +# Looks like an Int serialized into a string if I'm not mistaken +# @see https://github.com/filecoin-project/go-state-types/blob/master/big/int.go#L294-L303 +type SerializedBigInt = string +``` + +## Ucanto Interface + +```ts +import { Piece, PaddedPieceSize, Uint68 } from "@web3-storage/data-segment" +import { Invocation, ToString, InvocationError } from "@ucanto/interface" + +export interface Deal { + (invocation: Invocation<{ can: "deal/sign", with: StorefrontDID, nb: DealProposal }>): Result +} + +// Note that we use capital case field names for compatibility with go +export interface DealProposal { + Piece: Piece.Link + Size: Piece.PaddedSize + + VerifiedDeal: boolean + + Client: Address + Provider: Address + Label: DealLabel + + StartEpoch: ChainEpoch + EndEpoch: ChainEpoch + StoragePricePerEpoch: TokenAmount + + ProviderCollateral: TokenAmount + ClientCollateral: TokenAmount +} + +export interface Address { + addr: string +} + +export interface DealLabel { + bs: Uint8Array + notString: boolean +} + +export type ChainEpoch = Uint68 +export type TokenAmount = ToString + +export type Signature = Uint8Array + +export type SignError = + | InvocationError + | InvalidDeal // ProposalRemarshalMismatch + | SigningError // WalletSignError +``` + +## Capabilities + +### `deal/sign` + +Broker can invoke `deal/sign` capability with `DealProposal` in (`nb` field). Storefront MUST encode supplied `DealProposal` (`nb` field) into a CBOR block and then sign it with a wallet private key. + +Provider MUST response with raw bytes of the signature. + +## HTTP Interface + +Given that some actors (e.g. Spade) do not support UCANs natively they are not able to send signed invocations. As compromise protocol implementer is RECOMMENDED to expose plain HTTP API that tradeoffs some security for convenience of interop. + +Implementation MUST expose HTTP POST endpoint that accepts `application/vnd.ucan.cbor` requests with CBOR encoded as payload. These requests MUST provide `Authorization: Bearer` header with a UCAN delegation authorizing a request. Receiving principal MUST derive invocation from the provided `Authorization` and set invoked capability `nb` field to decoded CBOR block of the payload. Receiving principal MUST execute received capability and encode result of the invocation as an HTTP response. + +> Above HTTP interface could be utilize by spade to obtain signatures from w3up without having to proxy them through UCAN proxy. + +## Interaction Flow + +### Authorization per aggregate + +_Storefront_ (w3up) MAY delegate `deal/sign` UCAN capability to the _Agency_ (spade-proxy) and specify `Piece` and `Size` fields of the submitted aggregate. + +_Agency_ (spade-proxy) could also re-delegate that capability to the _Agency_ (spade) allowing it to request signature directly from _Storefront_ front. + +> ℹ️ Since ♠️ Spade does not support UCANs (yet), _Agency_ could instead create an invocation UCAN and pass it on to Spade so it could be used by spade as a plain, but short lived, JWT token for signing that specific deal. + +### Long term authorization + +_Storefront_ (w3up) MAY delegate unconstrained `deal/sign` UCAN capability to the _Agency_ (spade-proxy). By leaving out `Piece` and `Size` fields it will authorize it to sign any deals. + +_Agency_ could also re-delegate same unconstrained `deal/sign` UCAN capability to the _Agency_ (spade-proxy) allowing it to sign any deals. + +This tradeoffs increased security for convenience. + +[spade]:https://github.com/ribasushi/spade +[`did:web`]: https://w3c-ccg.github.io/did-method-web/ +[UCAN]: https://github.com/ucan-wg/spec/ +[principal]: https://github.com/ucan-wg/spec/#321-principals + +[Protocol Labs]:https://protocol.ai/ +[Irakli Gozalishvili]:https://github.com/Gozala +[aggregate]:https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0058.md#specification From 94af57157b0a875753f896b735264abb244a3fb3 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Mon, 17 Jul 2023 19:35:11 -0700 Subject: [PATCH 2/3] chore: fix typos --- w3-deal.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/w3-deal.md b/w3-deal.md index 5c7206ff..9b4b1969 100644 --- a/w3-deal.md +++ b/w3-deal.md @@ -183,7 +183,7 @@ Provider MUST response with raw bytes of the signature. ## HTTP Interface -Given that some actors (e.g. Spade) do not support UCANs natively they are not able to send signed invocations. As compromise protocol implementer is RECOMMENDED to expose plain HTTP API that tradeoffs some security for convenience of interop. +Given that some actors (e.g. Spade) do not support UCANs natively they are not able to send signed invocations. As compromise protocol implementer is RECOMMENDED to expose plain HTTP API that trade-offs some security for convenience of interop. Implementation MUST expose HTTP POST endpoint that accepts `application/vnd.ucan.cbor` requests with CBOR encoded as payload. These requests MUST provide `Authorization: Bearer` header with a UCAN delegation authorizing a request. Receiving principal MUST derive invocation from the provided `Authorization` and set invoked capability `nb` field to decoded CBOR block of the payload. Receiving principal MUST execute received capability and encode result of the invocation as an HTTP response. @@ -205,7 +205,7 @@ _Storefront_ (w3up) MAY delegate unconstrained `deal/sign` UCAN capability to th _Agency_ could also re-delegate same unconstrained `deal/sign` UCAN capability to the _Agency_ (spade-proxy) allowing it to sign any deals. -This tradeoffs increased security for convenience. +This trade-offs increased security for convenience. [spade]:https://github.com/ribasushi/spade [`did:web`]: https://w3c-ccg.github.io/did-method-web/ From ba333f8a84ef80680ec036ad9a1aeccba1f45937 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 19 Jul 2023 13:44:50 -0700 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Vasco Santos --- w3-deal.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/w3-deal.md b/w3-deal.md index 9b4b1969..eee4d389 100644 --- a/w3-deal.md +++ b/w3-deal.md @@ -44,7 +44,7 @@ A _Broker_ is a type of [principal] identified that arranges deals for the aggre All the filecoin deals need to be signed by a Fil wallet, in order to avoid passing private keys to wallet _Storefront_ could delegate a capability to a sign a deal to a _Broker_ instead. -Here we propose set of UCAN capabilities that can be invoked by authorized actors (like _Broker_) to sign deals such deals. +Here we propose set of UCAN capabilities that can be invoked by authorized actors (like _Agency_ or a _Broker_) to sign deals on behalf of the delegate (_Storefront_). ## IPLD Schema @@ -64,7 +64,7 @@ type Sign struct { # We use capital case letters because that the way go likes them type DealProposal struct { PieceCID &Piece - PieceSize PieceSize + PieceSize PaddedPieceSize VerifiedDeal bool # Signer wallet (f0) address Client Address @@ -179,7 +179,7 @@ export type SignError = Broker can invoke `deal/sign` capability with `DealProposal` in (`nb` field). Storefront MUST encode supplied `DealProposal` (`nb` field) into a CBOR block and then sign it with a wallet private key. -Provider MUST response with raw bytes of the signature. +Provider MUST respond with raw bytes of the signature. ## HTTP Interface @@ -187,7 +187,7 @@ Given that some actors (e.g. Spade) do not support UCANs natively they are not a Implementation MUST expose HTTP POST endpoint that accepts `application/vnd.ucan.cbor` requests with CBOR encoded as payload. These requests MUST provide `Authorization: Bearer` header with a UCAN delegation authorizing a request. Receiving principal MUST derive invocation from the provided `Authorization` and set invoked capability `nb` field to decoded CBOR block of the payload. Receiving principal MUST execute received capability and encode result of the invocation as an HTTP response. -> Above HTTP interface could be utilize by spade to obtain signatures from w3up without having to proxy them through UCAN proxy. +> Above HTTP interface could be utilized by spade to obtain signatures from w3up without having to proxy them through UCAN proxy. ## Interaction Flow @@ -195,7 +195,7 @@ Implementation MUST expose HTTP POST endpoint that accepts `application/vnd.ucan _Storefront_ (w3up) MAY delegate `deal/sign` UCAN capability to the _Agency_ (spade-proxy) and specify `Piece` and `Size` fields of the submitted aggregate. -_Agency_ (spade-proxy) could also re-delegate that capability to the _Agency_ (spade) allowing it to request signature directly from _Storefront_ front. +_Agency_ (spade-proxy) could also re-delegate that capability to the _Broker_ (spade) allowing it to request signature directly from _Storefront_ (w3up). > ℹ️ Since ♠️ Spade does not support UCANs (yet), _Agency_ could instead create an invocation UCAN and pass it on to Spade so it could be used by spade as a plain, but short lived, JWT token for signing that specific deal. @@ -203,7 +203,7 @@ _Agency_ (spade-proxy) could also re-delegate that capability to the _Agency_ (s _Storefront_ (w3up) MAY delegate unconstrained `deal/sign` UCAN capability to the _Agency_ (spade-proxy). By leaving out `Piece` and `Size` fields it will authorize it to sign any deals. -_Agency_ could also re-delegate same unconstrained `deal/sign` UCAN capability to the _Agency_ (spade-proxy) allowing it to sign any deals. +_Agency_ (spade-proxy) could also re-delegate same unconstrained `deal/sign` UCAN capability to the _Broker_ (spade) allowing it to sign any deals. This trade-offs increased security for convenience.