Documentation
¶
Index ¶
- Constants
- Variables
- func CreateSignerKeyPair(format enum.KeyFormat) (principal.Signer, []byte, error)
- func JSONFileToPEM(path string) ([]byte, error)
- func MarshalJSONKey(signer principal.Signer) ([]byte, error)
- func MarshalPEMKey(signer principal.Signer) ([]byte, error)
- func PEMFileToJSON(path string) ([]byte, error)
- func PrincipalSignerFromFile(path string) (principal.Signer, error)
- func PrintHero(id did.DID)
- func RequiredIntFlag(strFlag *cli.Int64Flag) *cli.Int64Flag
- func RequiredStringFlag(strFlag *cli.StringFlag) *cli.StringFlag
- func RequiredUintFlag(strFlag *cli.Uint64Flag) *cli.Uint64Flag
- type JsonKey
Constants ¶
View Source
const WalletDir = "wallet"
Variables ¶
View Source
var ClientCmd = &cli.Command{ Name: "client", Aliases: []string{"c"}, Usage: "test a running storage node as a client", Subcommands: []*cli.Command{ { Name: "upload", Aliases: []string{"ba"}, Usage: "invoke a blob allocation", Flags: append([]cli.Flag{ &cli.StringFlag{ Name: "space-did", Aliases: []string{"sd"}, Usage: "did for the space to use", EnvVars: []string{"PIRI_CLIENT_SPACE_DID"}, Required: true, }, &cli.StringFlag{ Name: "blob", Aliases: []string{"b"}, Usage: "blob to upload", EnvVars: []string{"PIRI_CLIENT_BLOB_FILE"}, Required: true, }, }, ClientSetupFlags...), Action: func(cCtx *cli.Context) error { client, err := getClient(cCtx) if err != nil { return err } spaceDid, err := did.Parse(cCtx.String("space-did")) if err != nil { return fmt.Errorf("parsing space did: %w", err) } blobFile, err := os.Open(cCtx.String("blob")) if err != nil { return fmt.Errorf("opening blob file: %w", err) } blobData, err := io.ReadAll(blobFile) if err != nil { return fmt.Errorf("reading blob file: %w", err) } digest, err := sha256.Hasher.Sum(blobData) if err != nil { return fmt.Errorf("calculating blob digest: %w", err) } address, err := client.BlobAllocate(spaceDid, digest.Bytes(), uint64(len(blobData)), cidlink.Link{Cid: cid.NewCidV1(cid.Raw, digest.Bytes())}) if err != nil { return fmt.Errorf("invocing blob allocation: %w", err) } if address != nil { fmt.Printf("now uploading to: %s\n", address.URL.String()) req, err := http.NewRequest(http.MethodPut, address.URL.String(), bytes.NewReader(blobData)) if err != nil { return fmt.Errorf("uploading blob: %w", err) } req.Header = address.Headers res, err := http.DefaultClient.Do(req) if err != nil { return fmt.Errorf("sending blob: %w", err) } if res.StatusCode >= 300 || res.StatusCode < 200 { resData, err := io.ReadAll(res.Body) if err != nil { return fmt.Errorf("reading response body: %w", err) } return fmt.Errorf("unsuccessful put, status: %s, message: %s", res.Status, string(resData)) } } blobResult, err := client.BlobAccept(spaceDid, digest.Bytes(), uint64(len(blobData)), cidlink.Link{Cid: cid.NewCidV1(cid.Raw, digest.Bytes())}) if err != nil { return fmt.Errorf("accepting blob: %w", err) } fmt.Printf("uploaded blob available at: %s\n", blobResult.LocationCommitment.Location[0].String()) if blobResult.PDPAccept != nil { fmt.Printf("submitted for PDP aggregation: %s\n", blobResult.PDPAccept.Piece.Link().String()) } return nil }, }, { Name: "pdp-info", Aliases: []string{"pi"}, Usage: "get piece information", Flags: append([]cli.Flag{ &cli.StringFlag{ Name: "piece", Aliases: []string{"pc"}, Usage: "pieceCID to get information on", EnvVars: []string{"PIRI_PIECE_CID"}, Required: true, }, &cli.Uint64Flag{ Name: "size", Aliases: []string{"ps"}, Usage: "optional size if passing a piece cid v1 of data", EnvVars: []string{"PIRI_PIECE_SIZE"}, }, }, ClientSetupFlags...), Action: func(cCtx *cli.Context) error { client, err := getClient(cCtx) if err != nil { return err } pieceStr := cCtx.String("piece") pieceCid, err := cid.Decode(pieceStr) if err != nil { return fmt.Errorf("decoding cid: %w", err) } pieceLink, err := piece.FromLink(cidlink.Link{Cid: pieceCid}) if err != nil { if !cCtx.IsSet("size") { return ErrMustBePieceLinkOrHaveSize } pieceLink, err = piece.FromV1LinkAndSize(cidlink.Link{Cid: pieceCid}, cCtx.Uint64("size")) if err != nil { return fmt.Errorf("parsing as pieceCID v1: %w", err) } } ok, err := client.PDPInfo(pieceLink) if err != nil { return fmt.Errorf("getting pdp info: %w", err) } asJSON, err := json.MarshalIndent(ok, "", " ") if err != nil { return fmt.Errorf("marshaling info to json: %w", err) } fmt.Print(string(asJSON)) return nil }, }, }, }
View Source
var ClientSetupFlags = []cli.Flag{ KeyFileFlag, NodeDIDFlag, NodeURLFlag, ProofFlag, }
View Source
var CurioURLFlag = &cli.StringFlag{ Name: "curio-url", Aliases: []string{"c"}, Usage: "URL of a running instance of curio", EnvVars: []string{"PIRI_CURIO_URL"}, }
View Source
var DelegationCmd = &cli.Command{ Name: "delegation", Aliases: []string{"dg"}, Usage: "Delegation tools.", Subcommands: []*cli.Command{ { Name: "generate", Aliases: []string{"gen"}, Usage: "Generate a new storage delegation", Flags: []cli.Flag{ KeyFileFlag, &cli.StringFlag{ Name: "client-did", Aliases: []string{"d"}, Usage: "did for a client", EnvVars: []string{"PIRI_CLIENT_DID"}, Required: true, }, }, Action: func(cCtx *cli.Context) error { id, err := PrincipalSignerFromFile(cCtx.String("key-file")) if err != nil { return fmt.Errorf("parsing private key: %w", err) } clientDid, err := did.Parse(cCtx.String("client-did")) if err != nil { return fmt.Errorf("parsing client-did: %w", err) } dlg, err := delegation.Delegate( id, clientDid, []ucan.Capability[ucan.NoCaveats]{ ucan.NewCapability( blob.AllocateAbility, id.DID().String(), ucan.NoCaveats{}, ), ucan.NewCapability( blob.AcceptAbility, id.DID().String(), ucan.NoCaveats{}, ), ucan.NewCapability( pdp.InfoAbility, id.DID().String(), ucan.NoCaveats{}, ), ucan.NewCapability( replica.AllocateAbility, id.DID().String(), ucan.NoCaveats{}, ), }, delegation.WithNoExpiration(), ) if err != nil { return fmt.Errorf("generating delegation: %w", err) } dlgStr, err := delegation.Format(dlg) if err != nil { return fmt.Errorf("formatting delegation: %w", err) } fmt.Println(dlgStr) return nil }, }, }, }
View Source
var ErrMustBePieceLinkOrHaveSize = errors.New("passing pieceCID v1 requires a size to be present")
View Source
var IdentityCmd = &cli.Command{ Name: "identity", Aliases: []string{"id"}, UsageText: "storage identity [generate|parse]", Description: ` This command provides a set of subcommands for working with decentralized identities. Specifically for generating and managing Ed25519 keys used in DID (Decentralized Identifier) systems. - Generate new Ed25519 key pairs and encode them in either PEM or JSON format - Convert between PEM and JSON key formats - Extract DIDs from keys - Comprehensive round-trip testing to ensure format conversions maintain key integrity Usage: - Generate a new key in JSON format storage identity generate --type=JSON > my-key.json - Generate a new key in PEM format storage identity generate --type=PEM > my-key.pem - Convert from JSON to PEM storage identity parse my-key.json > my-key.pem - Convert from PEM to JSON storage identity parse my-key.pem > my-key.json `, Usage: "Identity tools.", Subcommands: []*cli.Command{ parseCmd, generateCmd, }, }
IdentityCmd is the main command for identity-related operations
View Source
var KeyFileFlag = &cli.PathFlag{ Name: "key-file", Usage: "Path to a file containing ed25519 private key, typically created by the id gen command.", EnvVars: []string{"PIRI_PRIVATE_KEY"}, Required: true, TakesFile: true, }
View Source
var NodeDIDFlag = &cli.StringFlag{ Name: "node-did", Aliases: []string{"nd"}, Usage: "did for the storage node", EnvVars: []string{"PIRI_NODE_DID"}, Required: true, }
View Source
var NodeURLFlag = &cli.StringFlag{ Name: "node-url", Aliases: []string{"nu"}, Usage: "url for the storage node", EnvVars: []string{"PIRI_NODE_URL"}, Required: true, }
View Source
var ProofFlag = &cli.StringFlag{ Name: "proof", Aliases: []string{"p"}, Usage: "CAR file containing a storage proof delegation", EnvVars: []string{"PIRI_CLIENT_PROOF"}, Required: true, }
View Source
var ProofSetCmd = &cli.Command{ Name: "proofset", Aliases: []string{"ps"}, Usage: "proofset tools.", Subcommands: []*cli.Command{ { Name: "create", Aliases: []string{"c"}, Usage: "Generate a new proofset", Flags: []cli.Flag{ KeyFileFlag, RequiredStringFlag(CurioURLFlag), &cli.StringFlag{ Name: "record-keeper", Aliases: []string{"rk"}, Usage: "Hex address of the record keeper", EnvVars: []string{"PIRI_RECORD_KEEPER_CONTRACT"}, Required: true, }, }, Action: func(cCtx *cli.Context) error { id, err := PrincipalSignerFromFile(cCtx.String("key-file")) if err != nil { return fmt.Errorf("parsing private key: %w", err) } curioURL, err := url.Parse(cCtx.String("curio-url")) if err != nil { return fmt.Errorf("parsing curio URL: %w", err) } curioAuth, err := curio.CreateCurioJWTAuthHeader("storacha", id) if err != nil { return fmt.Errorf("generating curio jwt: %w", err) } client := curio.New(http.DefaultClient, curioURL, curioAuth) statusRef, err := client.CreateProofSet(cCtx.Context, curio.CreateProofSet{ RecordKeeper: cCtx.String("record-keeper"), }) if err != nil { return fmt.Errorf("creating proof set: %w", err) } fmt.Printf("proof set being created, check status at %s\n", filepath.Join(curioURL.String(), statusRef.URL)) return nil }, }, { Name: "status", Aliases: []string{"cs"}, Usage: "check on progress creating a proofset", Flags: []cli.Flag{ KeyFileFlag, RequiredStringFlag(CurioURLFlag), &cli.StringFlag{ Name: "ref-url", Aliases: []string{"ru"}, Usage: "Ref URL from create command", EnvVars: []string{"PIRI_REF_URL"}, Required: true, }, }, Action: func(cCtx *cli.Context) error { curioURL, err := url.Parse(cCtx.String("curio-url")) if err != nil { return fmt.Errorf("parsing curio URL: %w", err) } id, err := PrincipalSignerFromFile(cCtx.String("key-file")) if err != nil { return fmt.Errorf("parsing private key: %w", err) } curioAuth, err := curio.CreateCurioJWTAuthHeader("storacha", id) if err != nil { return fmt.Errorf("generating curio jwt: %w", err) } client := curio.New(http.DefaultClient, curioURL, curioAuth) status, err := client.ProofSetCreationStatus(cCtx.Context, curio.StatusRef{ URL: cCtx.String("ref-url"), }) if err != nil { return fmt.Errorf("getting proof set status: %w", err) } jsonStatus, err := json.MarshalIndent(status, "", " ") if err != nil { return fmt.Errorf("rendering json: %w", err) } fmt.Print(string(jsonStatus)) return nil }, }, { Name: "get", Aliases: []string{"g"}, Usage: "get a proofs set", Flags: []cli.Flag{ KeyFileFlag, RequiredStringFlag(CurioURLFlag), RequiredUintFlag(ProofSetFlag), }, Action: func(cCtx *cli.Context) error { curioURL, err := url.Parse(cCtx.String("curio-url")) if err != nil { return fmt.Errorf("parsing curio URL: %w", err) } id, err := PrincipalSignerFromFile(cCtx.String("key-file")) if err != nil { return fmt.Errorf("parsing private key: %w", err) } curioAuth, err := curio.CreateCurioJWTAuthHeader("storacha", id) if err != nil { return fmt.Errorf("generating curio jwt: %w", err) } client := curio.New(http.DefaultClient, curioURL, curioAuth) proofSet, err := client.GetProofSet(cCtx.Context, cCtx.Uint64("pdp-proofset")) if err != nil { return fmt.Errorf("getting proof set status: %w", err) } jsonProofSet, err := json.MarshalIndent(proofSet, "", " ") if err != nil { return fmt.Errorf("rendering json: %w", err) } fmt.Print(string(jsonProofSet)) return nil }, }, }, }
View Source
var ProofSetFlag = &cli.Uint64Flag{ Name: "pdp-proofset", Aliases: []string{"pdp"}, Usage: "Proofset to use with PDP", EnvVars: []string{"PIRI_PDP_PROOFSET"}, }
View Source
var ServeCmd = &cli.Command{ Name: "serve", Subcommands: []*cli.Command{ pdpCmd, }, }
View Source
var StartCmd = &cli.Command{ Name: "start", Usage: "Start the piri node daemon.", Flags: []cli.Flag{ KeyFileFlag, CurioURLFlag, &cli.IntFlag{ Name: "port", Aliases: []string{"p"}, Value: 3000, Usage: "Port to bind the server to.", EnvVars: []string{"PIRI_PORT"}, }, &cli.StringFlag{ Name: "data-dir", Aliases: []string{"d"}, Usage: "Root directory to store data in.", EnvVars: []string{"PIRI_DATA_DIR"}, }, &cli.StringFlag{ Name: "tmp-dir", Aliases: []string{"t"}, Usage: "Temporary directory data is uploaded to before being moved to data-dir.", EnvVars: []string{"PIRI_TMP_DIR"}, }, &cli.StringFlag{ Name: "public-url", Aliases: []string{"u"}, Usage: "URL the node is publically accessible at.", EnvVars: []string{"PIRI_PUBLIC_URL"}, }, ProofSetFlag, &cli.StringFlag{ Name: "indexing-service-proof", Usage: "A delegation that allows the node to cache claims with the indexing service.", EnvVars: []string{"PIRI_INDEXING_SERVICE_PROOF"}, }, }, Action: func(cCtx *cli.Context) error { id, err := PrincipalSignerFromFile(cCtx.String("key-file")) if err != nil { return err } dataDir := cCtx.String("data-dir") if dataDir == "" { homeDir, err := os.UserHomeDir() if err != nil { return fmt.Errorf("getting user home directory: %w", err) } dir, err := mkdirp(homeDir, ".storacha") if err != nil { return err } log.Errorf("Data directory is not configured, using default: %s", dir) dataDir = dir } tmpDir := cCtx.String("tmp-dir") if tmpDir == "" { dir, err := mkdirp(path.Join(os.TempDir(), "storage")) if err != nil { return err } log.Warnf("Tmp directory is not configured, using default: %s", dir) tmpDir = dir } blobStore, err := blobstore.NewFsBlobstore(path.Join(dataDir, "blobs"), path.Join(tmpDir, "blobs")) if err != nil { return fmt.Errorf("creating blob storage: %w", err) } allocsDir, err := mkdirp(dataDir, "allocation") if err != nil { return err } allocDs, err := leveldb.NewDatastore(allocsDir, nil) if err != nil { return err } claimsDir, err := mkdirp(dataDir, "claim") if err != nil { return err } claimDs, err := leveldb.NewDatastore(claimsDir, nil) if err != nil { return err } publisherDir, err := mkdirp(dataDir, "publisher") if err != nil { return err } publisherDs, err := leveldb.NewDatastore(publisherDir, nil) if err != nil { return err } receiptDir, err := mkdirp(dataDir, "receipt") if err != nil { return err } receiptDs, err := leveldb.NewDatastore(receiptDir, nil) if err != nil { return err } var pdpConfig *storage.PDPConfig var blobAddr multiaddr.Multiaddr curioURLStr := cCtx.String("curio-url") if curioURLStr != "" { curioURL, err := url.Parse(curioURLStr) if err != nil { return fmt.Errorf("parsing curio URL: %w", err) } if !cCtx.IsSet("pdp-proofset") { return errors.New("pdp-proofset must be set if curio is used") } proofSet := cCtx.Int64("pdp-proofset") aggRootDir, err := mkdirp(dataDir, "aggregator") if err != nil { return err } aggDsDir, err := mkdirp(aggRootDir, "datastore") if err != nil { return err } aggDs, err := leveldb.NewDatastore(aggDsDir, nil) if err != nil { return err } aggJobQueueDir, err := mkdirp(aggRootDir, "jobqueue") if err != nil { return err } pdpConfig = &storage.PDPConfig{ PDPDatastore: aggDs, CurioEndpoint: curioURL, ProofSet: uint64(proofSet), DatabasePath: filepath.Join(aggJobQueueDir, "jobqueue.db"), } curioAddr, err := maurl.FromURL(curioURL) if err != nil { return err } pieceAddr, err := multiaddr.NewMultiaddr("/http-path/" + url.PathEscape("piece/{blobCID}")) if err != nil { return err } blobAddr = multiaddr.Join(curioAddr, pieceAddr) } port := cCtx.Int("port") pubURLstr := cCtx.String("public-url") if pubURLstr == "" { pubURLstr = fmt.Sprintf("http://localhost:%d", port) log.Errorf("Public URL is not configured, using: %s", pubURLstr) } pubURL, err := url.Parse(pubURLstr) if err != nil { return fmt.Errorf("parsing public URL: %w", err) } var ipniAnnounceURLs []url.URL if os.Getenv("PIRI_IPNI_ANNOUNCE_URLS") != "" { var urls []string err := json.Unmarshal([]byte(os.Getenv("PIRI_IPNI_ANNOUNCE_URLS")), &urls) if err != nil { return fmt.Errorf("parsing IPNI announce URLs JSON: %w", err) } for _, s := range urls { url, err := url.Parse(s) if err != nil { return fmt.Errorf("parsing IPNI announce URL: %s: %w", s, err) } ipniAnnounceURLs = append(ipniAnnounceURLs, *url) } } else { ipniAnnounceURLs = presets.IPNIAnnounceURLs } indexingServiceDID := presets.IndexingServiceDID if os.Getenv("PIRI_INDEXING_SERVICE_DID") != "" { d, err := did.Parse(os.Getenv("PIRI_INDEXING_SERVICE_DID")) if err != nil { return fmt.Errorf("parsing indexing service DID: %w", err) } indexingServiceDID = d } indexingServiceURL := *presets.IndexingServiceURL if os.Getenv("PIRI_INDEXING_SERVICE_URL") != "" { u, err := url.Parse(os.Getenv("PIRI_INDEXING_SERVICE_URL")) if err != nil { return fmt.Errorf("parsing indexing service URL: %w", err) } indexingServiceURL = *u } uploadServiceDID := presets.UploadServiceDID if os.Getenv("PIRI_UPLOAD_SERVICE_DID") != "" { d, err := did.Parse(os.Getenv("PIRI_UPLOAD_SERVICE_DID")) if err != nil { return fmt.Errorf("parsing indexing service DID: %w", err) } uploadServiceDID = d } uploadServiceURL := *presets.UploadServiceURL if os.Getenv("PIRI_UPLOAD_SERVICE_URL") != "" { u, err := url.Parse(os.Getenv("PIRI_UPLOAD_SERVICE_URL")) if err != nil { return fmt.Errorf("parsing indexing service URL: %w", err) } uploadServiceURL = *u } var indexingServiceProofs delegation.Proofs if cCtx.String("indexing-service-proof") != "" { dlg, err := delegation.Parse(cCtx.String("indexing-service-proof")) if err != nil { return fmt.Errorf("parsing indexing service proof: %w", err) } indexingServiceProofs = append(indexingServiceProofs, delegation.FromDelegation(dlg)) } opts := []storage.Option{ storage.WithIdentity(id), storage.WithBlobstore(blobStore), storage.WithAllocationDatastore(allocDs), storage.WithClaimDatastore(claimDs), storage.WithPublisherDatastore(publisherDs), storage.WithPublicURL(*pubURL), storage.WithPublisherDirectAnnounce(ipniAnnounceURLs...), storage.WithUploadServiceConfig(uploadServiceDID, uploadServiceURL), storage.WithPublisherIndexingServiceConfig(indexingServiceDID, indexingServiceURL), storage.WithPublisherIndexingServiceProof(indexingServiceProofs...), storage.WithReceiptDatastore(receiptDs), } if pdpConfig != nil { opts = append(opts, storage.WithPDPConfig(*pdpConfig)) } if blobAddr != nil { opts = append(opts, storage.WithPublisherBlobAddress(blobAddr)) } svc, err := storage.New(opts...) if err != nil { return fmt.Errorf("creating service instance: %w", err) } err = svc.Startup(cCtx.Context) if err != nil { return fmt.Errorf("starting service: %w", err) } defer svc.Close(cCtx.Context) principalMapping := presets.PrincipalMapping if os.Getenv("PIRI_PRINCIPAL_MAPPING") != "" { var pm map[string]string err := json.Unmarshal([]byte(os.Getenv("PIRI_PRINCIPAL_MAPPING")), &pm) if err != nil { return fmt.Errorf("parsing principal mapping: %w", err) } principalMapping = pm } presolv, err := principalresolver.New(principalMapping) if err != nil { return fmt.Errorf("creating principal resolver: %w", err) } go func() { time.Sleep(time.Millisecond * 50) if err == nil { PrintHero(id.DID()) } }() err = server.ListenAndServe( fmt.Sprintf(":%d", cCtx.Int("port")), svc, ucanserver.WithPrincipalResolver(presolv.ResolveDIDKey), ) return err }, }
View Source
var VersionCmd = &cli.Command{ Name: "version", Usage: "Version information.", Action: func(cCtx *cli.Context) error { fmt.Println(build.Version) return nil }, }
View Source
var WalletCmd = &cli.Command{ Name: "wallet", Usage: "Manage wallet", Flags: []cli.Flag{ &cli.StringFlag{ Name: "data-dir", Aliases: []string{"d"}, Usage: "Root directory to store data in.", EnvVars: []string{"PIRI_DATA_DIR"}, }, }, Subcommands: []*cli.Command{ walletImport, walletList, }, }
Functions ¶
func CreateSignerKeyPair ¶
CreateSignerKeyPair generates a new key pair and returns it in the requested format
func JSONFileToPEM ¶
JSONFileToPEM converts a JSON key file to PEM format
func MarshalJSONKey ¶
MarshalJSONKey encodes a signer to JSON format
func MarshalPEMKey ¶
MarshalPEMKey encodes a signer to PEM format
func PEMFileToJSON ¶
PEMFileToJSON converts a PEM key file to JSON format
func RequiredStringFlag ¶
func RequiredStringFlag(strFlag *cli.StringFlag) *cli.StringFlag
func RequiredUintFlag ¶
func RequiredUintFlag(strFlag *cli.Uint64Flag) *cli.Uint64Flag
Types ¶
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
aggregatesubmitter
command
|
|
|
getclaim
command
|
|
|
getroot
command
|
|
|
pieceaccepter
command
|
|
|
pieceaggregator
command
|
|
|
postroot
command
|
|
|
putblob
command
|
|
Click to show internal directories.
Click to hide internal directories.