feat: unify pdp client and server on common interface#169
Conversation
| type ProofSetAPI interface { | ||
| CreateProofSet(ctx context.Context, recordKeeper common.Address) (common.Hash, error) | ||
| GetProofSetStatus(ctx context.Context, txHash common.Hash) (*ProofSetStatus, error) | ||
| GetProofSet(ctx context.Context, proofSetID uint64) (*ProofSet, error) | ||
| AddRoots(ctx context.Context, proofSetID uint64, roots []RootAdd) (common.Hash, error) | ||
| RemoveRoot(ctx context.Context, proofSetID uint64, rootID uint64) (common.Hash, error) | ||
| } | ||
|
|
||
| type PieceAPI interface { | ||
| AllocatePiece(ctx context.Context, allocation PieceAllocation) (*AllocatedPiece, error) | ||
| UploadPiece(ctx context.Context, upload PieceUpload) error | ||
| FindPiece(ctx context.Context, piece Piece) (cid.Cid, bool, error) | ||
| ReadPiece(ctx context.Context, piece cid.Cid) (*PieceReader, error) | ||
| } |
There was a problem hiding this comment.
Interface implemented by both the PDP HTTP Client and the PDP Service, allowing them to be used interchangeably
| @@ -0,0 +1,55 @@ | |||
| package types | |||
There was a problem hiding this comment.
Error package use for mapping errors from the PDP Service to http error codes.
f1dbf97 to
6aae9e1
Compare
6aae9e1 to
2453fd1
Compare
hannahhoward
left a comment
There was a problem hiding this comment.
Generally very good.
I put a lot of comments, but the only one that's blocking that leads to the request changes review is the conditional in the storage service, which unless I'm reading it wrong has a bug. If I'm reading it wrong, let me know! :)
|
|
||
| "github.com/storacha/piri/pkg/pdp/aggregator/aggregate" | ||
| "github.com/storacha/piri/pkg/pdp/curio" | ||
| types2 "github.com/storacha/piri/pkg/pdp/types" |
There was a problem hiding this comment.
strong preference not to use numbers in package names -- either just types, or descriptive name/letter for package rename here.
There was a problem hiding this comment.
i.e. pdptypes if you have to rename
There was a problem hiding this comment.
whoop, will address here and everywhere else.
| ) | ||
|
|
||
| func (p *PDPService) ProofSetCreate(ctx context.Context, recordKeeper common.Address) (common.Hash, error) { | ||
| func (p *PDPService) CreateProofSet(ctx context.Context, recordKeeper common.Address) (common.Hash, error) { |
There was a problem hiding this comment.
it seems odd to rename a file to an old function name while changing a function name to the previous name for the file?
There was a problem hiding this comment.
Yeah, did this to make walking the code easier.
Functions were renamed with pattern: <Verb><Type>
Files were renamed with: <Type><Verb>
Files appear in the order of the type they operate on, i.e:
tree -L 1 service
service
├── contract
├── models
├── piece_allocate.go
├── piece_find.go
├── piece_read.go
├── piece_upload.go
├── proofset_create.go
├── proofset_get.go
├── proofset_list.go
├── proofset_status.go
├── root_remove.go
├── roots_add.go
├── service.go
├── types
└── util.go| ) | ||
|
|
||
| type Server struct { | ||
| pieceFinder piecefinder.PieceFinder |
There was a problem hiding this comment.
Yup they were never used anywhere, surprised the linter didn't catch this.
| receiptStore receiptstore.ReceiptStore, | ||
| ) (*PDPService, error) { | ||
| aggregator, err := aggregator.NewLocal(ds, dbPath, client, proofSet, issuer, receiptStore) | ||
| func NewRemote(cfg *Config, id principal.Signer, receiptStore receiptstore.ReceiptStore) (*PDPService, error) { |
There was a problem hiding this comment.
Ok, I must have missed this before, but having two types called "PDPService" is very confusing. How exactly is this best described as I wonder?
If I understand, this is what is needed by the higher level code that consumes PDP, and in order to work it requires a dependency which matches types.API interface in order to construct the Aggregator/PieceAdder/PieceFinder. In a single process world, this will be service.PDPService. (being consumed here by pdp.PDPService, which is confusing)
Am I wrong that what we really have is a low-ish level API (types.API -- that could be either service.PDPService or an HTTP client to a remote server) consumed by a high level API? (this service plus it's dependency pieceAdder/pieceFinder/aggregate)? And the high level API is the part that essentially ALWAYS lives in the UCAN server process?
I'm not saying fix this now, but have it in mind for the single process pr and maybe change then? I stared for at least 15 min before I realized there are two different PDPService structs
There was a problem hiding this comment.
Am I wrong that what we really have is a low-ish level API (types.API -- that could be either service.PDPService or an HTTP client to a remote server) consumed by a high level API? (this service plus it's dependency pieceAdder/pieceFinder/aggregate)? And the high level API is the part that essentially ALWAYS lives in the UCAN server process?
Nope, you are spot on!
The higher level PDP interface:
type PDP interface {
PieceAdder() pieceadder.PieceAdder
PieceFinder() piecefinder.PieceFinder
Aggregator() aggregator.Aggregator
}is used by the UCAN server/code and AWS bits.
The lower level PDP interface:
type API interface {
ProofSetAPI
PieceAPI
}
type ProofSetAPI interface {
CreateProofSet(ctx context.Context, recordKeeper common.Address) (common.Hash, error)
GetProofSetStatus(ctx context.Context, txHash common.Hash) (*ProofSetStatus, error)
GetProofSet(ctx context.Context, proofSetID uint64) (*ProofSet, error)
AddRoots(ctx context.Context, proofSetID uint64, roots []RootAdd) (common.Hash, error)
RemoveRoot(ctx context.Context, proofSetID uint64, rootID uint64) (common.Hash, error)
}
type PieceAPI interface {
AllocatePiece(ctx context.Context, allocation PieceAllocation) (*AllocatedPiece, error)
UploadPiece(ctx context.Context, upload PieceUpload) error
FindPiece(ctx context.Context, piece Piece) (cid.Cid, bool, error)
ReadPiece(ctx context.Context, piece cid.Cid) (*PieceReader, error)
}Is wrapped and used by the PieceFinder, PieceAdder, and Aggregator, under the hood this is either the HTTPClient (for curio) or the "actual" PDPServer that interacts with the chain.
I'm not saying fix this now, but have it in mind for the single process pr and maybe change then? I stared for at least 15 min before I realized there are two different PDPService structs
In hindsight, I think this interface:
type PDP interface {
PieceAdder() pieceadder.PieceAdder
PieceFinder() piecefinder.PieceFinder
Aggregator() aggregator.Aggregator
}Needs a better name, and I'd really like it to align with the Blobs interface:
type Blobs interface {
// Blobs is the storage interface for blobs.
Store() blobstore.Blobstore
// Allocations is a store for received blob allocations.
Allocations() allocationstore.AllocationStore
// Presigner provides an interface to allow signed request access to upload blobs.
Presigner() presigner.RequestPresigner
// Access provides an interface to allowing public access to download blobs.
Access() access.Access
}So that we can remove conditionals like this throughout the ucan code:
piri/pkg/service/storage/handlers/blob/accept.go
Lines 53 to 96 in d8ba0d6
|
|
||
| var pdpImpl pdp.PDP | ||
| if c.pdp == nil { | ||
| if c.pdpCfg == nil { |
There was a problem hiding this comment.
Blocking: this conditional feels broken if c.pdpCfg & c.pdp are used exclusively.
cause won't c.pdpCfg be nil when c.pdp is set? and therefore, how does pdpImpl ever get assigned c.pdp?
There was a problem hiding this comment.
e783256 to
703bd79
Compare
# Dependencies & Review This PR builds on #190 I would recommend reviewing each commit of this PR on it's own - I have done my best to keep them well scoped. # What This PR makes life easier by combining two servers into one. Instead of juggling `piri serve ucan` and `piri server pdp` in separate processes, you can now just run `piri serve full` and call it a day. The new command runs both UCAN and PDP servers in a single process. Don't worry if you're using an external PDP implementation (like Curio) - `piri serve ucan` still works standalone. Here's what the config looks like - notice how both UCAN and PDP settings live together: ```toml [identity] key_file = '/etc/piri/service.pem' [pdp] contract_address = '0x6170dE2b09b404776197485F3dc6c968Ef948505' lotus_endpoint = 'wss://lotus.spicystorage.tech/rpc/v1' owner_address = '0x7469B47e006D0660aB92AE560b27A1075EEcF97F' [repo] data_dir = '/data/piri' temp_dir = '/tmp/storage' [server] host = 'localhost' port = '3000' public_url = 'https://piri.spicystorage.tech' [ucan] proof_set = '414' [ucan.services] [ucan.services.indexer] did = 'did:web:staging.indexer.warm.storacha.network' url = 'https://staging.indexer.warm.storacha.network/claims' proof = '<proof>' [ucan.services.principal_mapping] 'did:web:staging.indexer.warm.storacha.network' = 'did:key:z6Mkr4QkdinnXQmJ9JdnzwhcEjR8nMnuVPEwREyh9jp2Pb7k' 'did:web:staging.up.warm.storacha.network' = 'did:key:z6MkpR58oZpK7L3cdZZciKT25ynGro7RZm6boFouWQ7AzF7v' [ucan.services.publisher] ipni_announce_urls = ['https://cid.contact/announce', 'https://staging.ipni.warm.storacha.network'] [ucan.services.upload] did = 'did:web:staging.up.warm.storacha.network' url = 'https://staging.up.warm.storacha.network' ``` # How This brings together work from several previous PRs (#187, #186, #169, #161, #144) to let both UCAN and PDP modules run in the same Piri process. Here's what changed under the hood: **Architecture Changes:** - Refactored the config system to be composable - configs are now split into logical sections (identity, server, repo, ucan, pdp) that can be mixed and matched - Introduced a provider pattern for core components (wallet, stores, engines, aggregators) so they can be shared between modules - Created separate fx modules (`UCANModule` and `PDPModule`) that encapsulate their respective dependencies **The Magic:** - The new `piri serve full` command uses fx (Uber's DI framework) to inject both modules at startup - Both modules share common dependencies through `CommonModules` (identity, HTTP server, databases) - Each module brings its own routes, services, and background tasks - they play nice together in the same process **Backwards Compatibility:** - `piri serve ucan` still works for operators who want to run just the UCAN server - The old `piri server pdp` command is gone (replaced by the full command) - All existing config options and environment variables are preserved
Serves as an alternative #166.
The curx of the change here is aligning the PDP HTTP Client (previously called curio client) and PDP service on a common interface. The complexity of handling the various server request and response types needed to continue supporting a curio http server has been pushed into the PDP HTTP Client.