Skip to content

Latest commit

 

History

History
75 lines (60 loc) · 4.39 KB

File metadata and controls

75 lines (60 loc) · 4.39 KB

Photos — decentralized photo storage dApp

Layer 1 (drive-registry) dApp. A custom Photos.sol contract creates and owns a drive per user via the drive-registry precompile (0x…0902), grants the user a Writer role, and anchors the album-tree root CID on-chain. Design: DESIGN.md.

Status: M7 — contract + headless flow through createLibrary, albums, blobs, and the client-computed metadata-root anchor (setRoot), plus a fully interactive UI (React 19 + Vite + Tailwind + PAPI, port 5178). State A creates a library (pick a provider → negotiate → createLibrary, M5); State B browses and manages it: create albums, upload photos with client-generated thumbnails, render a thumbnail grid, and open a photo full-res in a lightbox, recomputing + setRoot-anchoring the metadata root after each mutation (M6). The lightbox now has an in-browser crop/rotate editor that re-PUTs the edited photo to the same path (copy-on-write), regenerates its thumbnail, and re-anchors the root (M7). Next: tests + polish (M8).

Layout

contracts/        Photos.sol + vendored IDriveRegistry.sol; build.sh → src/contract/Photos.json
scripts/lib/      TS helpers — PAPI deploy/call, /negotiate, libraryOf read + setRoot (photos.ts),
                  provider /fs ops (fs-client.ts), byte-exact metadata/data root (merkle.ts)
scripts/          deploy-contract.ts, photos-flow.ts (run via tsx)
src/contract/     photos-abi.ts (vendored ABI the UI uses); Photos.json (bin, generated by build:contract)
src/              React UI — state/ (chain·network·wallet·library·album), lib/ (chain-client,
                  photos-contract read + write/setRoot, fs-client, merkle, thumbnail),
                  components/ (CreateLibraryPanel, AlbumBar, UploadButton, PhotoGrid, Lightbox),
                  pages/Library.tsx (State A create vs State B browse)

Quick start

Needs solc + resolc on PATH (see examples/contracts/README.md), and a running chain + provider.

cd user-interfaces && pnpm install

# terminals 1 & 2, from repo root:
just start-chain
just start-provider
just demo                 # register + accept the provider once

# build, deploy, run the headless flow (one namespaced entrypoint):
just photos build         # compile Photos.sol → src/contract/Photos.json
just photos deploy        # deploy; prints the contract H160
just photos flow          # deploy → createLibrary → album + photo → setRoot → verify

just photos help lists the actions. Each action also has a raw pnpm equivalent, e.g. pnpm --filter @web3-storage/photos build:contract.

Run the UI

pnpm --filter @web3-storage/photos dev      # serves http://127.0.0.1:5178 (or use the run-local-uis skill)

The UI needs the deployed contract address: it's read from localStorage['photos.contract'], then VITE_PHOTOS_CONTRACT, then NetworkConfig.photosContract. Locally, run just photos deploy (or just photos flow) and paste the printed 0x… into the in-app field (saved to localStorage).

It reads libraryOf unsigned for state detection. State A ("no library") runs the create flow (map account → negotiate terms → createLibrary{value}). State B ("drive #N") browses the library: the album bar lists/creates folders, Upload generates a downscaled JPEG thumbnail per photo (canvas, longest edge ~320px) and PUTs the full photo plus the thumbnail, the grid renders from thumbnails (kilobytes per cell), and clicking a photo opens it full-resolution in a lightbox. Every mutation recomputes the drive's metadata Merkle root client-side (@web3-storage/core's merkle.ts) and anchors it on-chain via setRoot, so the on-chain anchor shown in the header updates live. /fs requests are unsigned (dev providers run /fs auth disabled; signed requests are M8).

photos-flow asserts the contract owns the drive, the user holds a Writer role on the underlying bucket, and libraryOf(user) reports the new drive. It then drives the provider's /fs API (mkdir an album, PUT a multi-MB photo + a thumbnail), computes the drive's metadata Merkle root client-side (merkle.ts, a byte-exact port of crates/providers/storage/src/index/fs.rs), anchors it via setRoot, and verifies the locally recomputed root equals both the on-chain anchor and the provider's index_root (plus a tamper check) — proving the control plane and the integrity anchor before any UI exists.