spregistry

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 24, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

Documentation

Overview

Package spregistry provides the Storage Provider Registry service.

It queries the ServiceProviderRegistry contract to discover storage providers, their capabilities, endpoints, and endorsement status, and (when constructed with a Signer + Backend) exposes the state-changing surface needed to register, update, or remove a provider and manage its PDP product.

Provider types:

  • Endorsed: curated, high-quality providers (used as primary).
  • Approved: automated QA-checked providers (used as secondary).

Read surface

The read surface is usable with any bind.ContractCaller (including a plain *ethclient.Client) and returns decoded Go types for providers, PDP offerings, endorsement lists, and ID ↔ address mappings.

Write surface

When Options.Backend, Options.Signer, and Options.ChainID are supplied, the following state-changing methods become available. A nonce coordinator is created automatically for standalone use; the root synapse Client injects a shared coordinator across all write-capable services.

  • RegisterProvider: declares the caller as a provider and, in the same transaction, registers a PDP product. Reads REGISTRATION_FEE from the contract unless WithValue overrides.
  • UpdateProviderInfo: changes the caller's display name / description.
  • RemoveProvider: deregisters the caller.
  • AddPDPProduct / UpdatePDPProduct: registers or replaces the PDP product's capability set.
  • RemoveProduct: removes the caller's product for the given type.

All write methods validate and encode the PDP offering before broadcast, return a WriteResult carrying the transaction hash, and (under WithWait) block for the receipt; a reverted transaction surfaces as ErrTxFailed with the receipt preserved on the WriteResult for inspection.

Callers that build Service read-only receive ErrWriteNotConfigured for any write method.

Errors are returned as wrapped sentinels. Use errors.Is to check:

  • ErrNotFound: returned when the queried provider does not exist. GetProviderIDByAddress is an exception: it returns a zero ID (check with id.IsZero()) for unknown addresses, mirroring the contract convention.
  • ErrInvalidArgument: returned when required arguments are nil, zero, or otherwise malformed.
  • ErrInvalidOffering: returned by ValidatePDPOffering / write methods when a PDP offering fails structural validation.
  • ErrWriteNotConfigured: returned by write methods when Service was constructed without write dependencies.
  • ErrTxFailed: returned by write methods when the broadcast transaction reverts on-chain.

Stability

0.x phase: public API may change between minor releases.

Example

Example demonstrates looking up a storage provider by ID via spregistry.Service. In practice a Service is obtained from synapse.Client.SPRegistry.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/strahe/synapse-go/spregistry"
	"github.com/strahe/synapse-go/types"
)

func main() {
	var svc *spregistry.Service // obtained from synapse.Client.SPRegistry()

	ctx := context.Background()
	info, err := svc.GetProvider(ctx, types.NewBigInt(1))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(info.Name)
}

Index

Examples

Constants

View Source
const (
	CapServiceURL         = "serviceURL"
	CapMinPieceSize       = "minPieceSizeInBytes"
	CapMaxPieceSize       = "maxPieceSizeInBytes"
	CapStoragePrice       = "storagePricePerTibPerDay"
	CapMinProvingPeriod   = "minProvingPeriodInEpochs"
	CapLocation           = "location"
	CapPaymentToken       = "paymentTokenAddress"
	CapIPNIPiece          = "ipniPiece"
	CapIPNIIPFS           = "ipniIpfs"
	CapIPNIPeerID         = "ipniPeerId"
	CapIPNIPeerIDLegacyUC = "IPNIPeerID" // legacy key, still read

)

Canonical PDP capability keys (must match filecoin-services/service_contracts/src/ServiceProviderRegistry.sol REQUIRED_PDP_KEYS Bloom filter).

Variables

View Source
var ErrClosed = lifecycle.ErrClosed

ErrClosed is returned when a method is called after the owning Client has been closed. It aliases the shared closed-client sentinel.

View Source
var ErrInvalidArgument = errors.New("spregistry: invalid argument")

ErrInvalidArgument is returned, wrapped, when a caller passes an argument that fails local precondition checks (nil IDs, zero addresses, etc.). Use errors.Is to detect it.

View Source
var ErrInvalidOffering = errors.New("spregistry: invalid offering")

ErrInvalidOffering is returned when a PDPOffering decoded from on-chain capabilities fails validation (missing required fields, non-positive numeric parameters).

View Source
var ErrNotFound = errors.New("spregistry: not found")

ErrNotFound is returned, wrapped via fmt.Errorf with %w, when a queried provider (by id or address) is not registered. Callers should use errors.Is(err, spregistry.ErrNotFound) rather than comparing for nil results.

View Source
var ErrTxFailed = types.ErrTxFailed

ErrTxFailed is returned when a broadcast transaction is mined with status == 0 (EVM revert). It is an alias of types.ErrTxFailed so that errors.Is(err, types.ErrTxFailed) holds for every SDK-originated transaction failure, matching the convention used by payments and sessionkey. The types.WriteResult.Receipt is preserved alongside the error for callers that need the full on-chain status.

View Source
var ErrUninitialized = errors.New("spregistry: service not initialized; use spregistry.New")

ErrUninitialized is returned when a method is invoked on a zero-value Service (one that was not constructed via New).

View Source
var ErrWriteNotConfigured = errors.New("spregistry: write operations not configured (missing signer or backend)")

ErrWriteNotConfigured is returned when a state-changing method is invoked on a Service that was constructed without a Signer / Backend. The SDK deliberately keeps writes opt-in: provider operations are an advanced path and most callers only need read access.

Functions

func CapabilitiesListToMap

func CapabilitiesListToMap(keys []string, values [][]byte) map[string][]byte

CapabilitiesListToMap zips parallel keys/values slices returned by the contract into a map.

func EncodePDPCapabilities

func EncodePDPCapabilities(o PDPOffering, extras map[string]string) (keys []string, values [][]byte, err error)

EncodePDPCapabilities produces the parallel (keys, values) byte slices expected by the ServiceProviderRegistry contract's registerProvider, addProduct, and updateProduct methods.

Canonical layout (positions 1-9):

  1. serviceURL (UTF-8 bytes)
  2. minPieceSizeInBytes (big-endian minimal; zero encodes as 0x00)
  3. maxPieceSizeInBytes
  4. (optional) ipniPiece (single byte 0x01, omitted when false)
  5. (optional) ipniIpfs (single byte 0x01, omitted when false)
  6. storagePricePerTibPerDay
  7. minProvingPeriodInEpochs
  8. location (required UTF-8 bytes)
  9. paymentTokenAddress (20 bytes)

10. any entries in extras, sorted by key for deterministic output.

Note: ipniPeerId is intentionally NOT emitted even though the decoder accepts it for backwards compatibility.

For extras values:

  • a "0x"-prefixed string is hex-decoded;
  • otherwise the raw UTF-8 bytes are used.

Extras keys are sorted alphabetically to guarantee deterministic output. On-chain storage is a key/value mapping, so this ordering is not observable to the contract.

Attempting to set a canonical PDP key through extras returns ErrInvalidOffering: doing so would either duplicate or shadow the typed field in a way the contract cannot disambiguate.

The offering itself is validated via ValidatePDPOffering before any bytes are emitted.

func ValidatePDPOffering

func ValidatePDPOffering(o PDPOffering) error

ValidatePDPOffering checks that required fields are present and have basic sane values. Optional capabilities and detailed size/range relationships are left to higher-level caller policy.

All *big.Int fields must be non-nil and non-negative. MinProvingPeriodInEpochs must additionally be strictly positive (zero proving periods are rejected by the on-chain validation). ServiceURL and Location must be non-empty.

Types

type Backend

type Backend interface {
	bind.ContractBackend
	TransactionReceipt(ctx context.Context, txHash common.Hash) (*ethtypes.Receipt, error)
	BlockNumber(ctx context.Context) (uint64, error)
}

Backend extends EthClient with the surface required for sending transactions (register/update/remove provider, add/update/remove product). The full *ethclient.Client satisfies this interface.

type EthClient

type EthClient interface {
	bind.ContractCaller
}

EthClient is the minimal RPC surface the service needs. Tests can substitute a mock that implements bind.ContractCaller.

type Options

type Options struct {
	Client  EthClient
	Address common.Address
	// ChainID is required only when writes are used.
	ChainID types.ChainID
	// Backend provides a full RPC surface for writes. When nil Service is
	// read-only; write methods return ErrWriteNotConfigured.
	Backend Backend
	// Signer signs write transactions. Optional.
	Signer signer.EVMSigner
	// NonceManager is optional. The root synapse Client injects a shared
	// coordinator across all write-capable services; standalone callers may
	// leave this nil to create one when Backend and Signer are both set.
	NonceManager *txutil.NonceManager
	// Logger is optional.
	Logger *slog.Logger
	// ReceiptWait overrides the default receipt polling timeout for
	// WithWait calls. Zero uses txutil.DefaultReceiptWaitConfig.
	ReceiptWait time.Duration
	// Lifecycle, when non-nil, ties this Service to the owning Client's
	// close state. After the Lifecycle is closed, every method returns
	// ErrClosed. Nil is allowed for standalone use.
	Lifecycle *lifecycle.Lifecycle
}

Options configures the service.

type PDPOffering

type PDPOffering struct {
	ServiceURL               string
	MinPieceSizeInBytes      *big.Int
	MaxPieceSizeInBytes      *big.Int
	StoragePricePerTiBPerDay *big.Int
	MinProvingPeriodInEpochs *big.Int
	Location                 string
	PaymentTokenAddress      common.Address

	// Optional capabilities (present only if the provider set them).
	IPNIPiece  bool
	IPNIIPFS   bool
	IPNIPeerID string // base58btc-encoded peer id; empty if absent

	// Any additional capabilities declared by the provider. Keys are
	// the raw on-chain strings; values are the raw bytes (NOT decoded).
	ExtraCapabilities map[string][]byte
}

PDPOffering is the decoded PDP product offering, derived from the capability key/value pairs returned by getProviderWithProduct.

Canonical capability keys are defined by filecoin-services' ServiceProviderRegistry.sol REQUIRED_PDP_KEYS Bloom filter.

func DecodePDPOffering

func DecodePDPOffering(caps map[string][]byte) (PDPOffering, error)

DecodePDPOffering decodes the capability map into a typed PDPOffering.

Semantics:

  • Required numeric fields (sizes, price, proving period) are big-endian unsigned integers; missing entries decode to 0.
  • Boolean flags (ipniPiece/ipniIpfs) are true only when the value is exactly 0x01 — presence alone is not sufficient.
  • paymentTokenAddress is the last 20 bytes of the value; if the value has fewer than 20 bytes it is left-padded (common.BytesToAddress).
  • ipniPeerId is base58btc-encoded for display. The uppercase legacy key "IPNIPeerID" is accepted for read only.

Missing required fields do NOT produce an error; they are zero-valued. Callers that need strict validation should check individual fields.

type PDPProvider

type PDPProvider struct {
	Info     ProviderInfo
	Product  ServiceProduct
	Offering PDPOffering
}

PDPProvider is a provider record together with its decoded PDP offering. This is the high-level view that consumers (storage package) need.

type PaginatedPDPProviders

type PaginatedPDPProviders struct {
	Providers []PDPProvider
	HasMore   bool
}

PaginatedPDPProviders is the paginated result from GetPDPProviders.

type ProductType

type ProductType uint8

ProductType mirrors the uint8 product-type enum used by the ServiceProviderRegistry contract.

const (
	// ProductTypePDP is the Proof-of-Data-Possession product (id 0).
	ProductTypePDP ProductType = 0
)

type ProviderFilter

type ProviderFilter struct {
	// PieceSizeBytes, when non-nil, retains only providers whose
	// minPieceSizeInBytes ≤ PieceSizeBytes ≤ maxPieceSizeInBytes.
	PieceSizeBytes *big.Int

	// PaymentToken, when non-nil, retains only providers that accept
	// this payment token. The zero address is a valid value here and
	// means FIL, matching the on-chain/TS semantics.
	PaymentToken *common.Address

	// ExcludeIDs is a set of provider IDs to skip. This supports the
	// replacement-with-exclusion-set pattern required for secondary
	// provider selection during multi-copy upload.
	ExcludeIDs []types.BigInt
}

ProviderFilter describes criteria for selecting active PDP providers. A zero-value ProviderFilter accepts all active providers.

All non-nil / non-zero fields are applied as AND conditions.

type ProviderInfo

type ProviderInfo struct {
	ID              types.BigInt
	ServiceProvider common.Address
	Payee           common.Address
	Name            string
	Description     string
	IsActive        bool
}

ProviderInfo is the canonical on-chain provider record.

type ProviderRegistrationInfo

type ProviderRegistrationInfo struct {
	// Payee receives rail payouts. Must be non-zero.
	Payee common.Address
	// Name is a human-readable provider name (stored on-chain).
	Name string
	// Description is free-form provider metadata (stored on-chain).
	Description string
	// PDPOffering is the PDP product registered atomically with the
	// provider. Must pass ValidatePDPOffering.
	PDPOffering PDPOffering
	// Capabilities are extra product capability key/value pairs in
	// addition to the canonical PDP keys. Values starting with "0x"
	// are hex-decoded; other values are UTF-8 encoded. Empty values
	// are invalid; use "0x01" for flag-style extra capabilities.
	Capabilities map[string]string
}

ProviderRegistrationInfo bundles the arguments accepted by Service.RegisterProvider.

Capabilities merges with PDPOffering at encoding time: canonical PDP keys (serviceURL, minPieceSizeInBytes, ...) are always emitted from the typed fields; any keys present in Capabilities are appended in sorted order AFTER the canonical keys. Do not duplicate a canonical key inside Capabilities.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service provides read access to the ServiceProviderRegistry contract, and (when constructed with a Signer + Backend) a small, typed set of state-changing provider-management methods.

func New

func New(opts Options) (*Service, error)

New creates a Service bound to the given registry address.

func (*Service) AddPDPProduct

func (s *Service) AddPDPProduct(ctx context.Context, offering PDPOffering, capabilities map[string]string, opts ...WriteOption) (*WriteResult, error)

AddPDPProduct declares the caller's PDP product after an initial registerProvider that did not include one, or after RemoveProduct. Currently only PDP products are supported by the public SDK surface.

func (*Service) Address

func (s *Service) Address() common.Address

Address returns the configured registry contract address.

func (*Service) GetActiveProviderCount

func (s *Service) GetActiveProviderCount(ctx context.Context) (*big.Int, error)

GetActiveProviderCount returns the number of providers whose IsActive flag is true.

func (*Service) GetPDPProvider

func (s *Service) GetPDPProvider(ctx context.Context, providerID types.BigInt) (*PDPProvider, error)

GetPDPProvider returns the provider + decoded PDP offering for providerID. Returns an error wrapping ErrNotFound when the provider has no PDP product registered.

func (*Service) GetPDPProviderByAddress added in v0.2.1

func (s *Service) GetPDPProviderByAddress(ctx context.Context, addr common.Address) (*PDPProvider, error)

GetPDPProviderByAddress returns the PDP provider whose service-provider address matches addr.

func (*Service) GetPDPProviders

func (s *Service) GetPDPProviders(ctx context.Context, onlyActive bool, opts types.ListOptions) (*PaginatedPDPProviders, error)

GetPDPProviders lists PDP providers with pagination. When onlyActive is true the registry filters out inactive providers BEFORE applying offset and limit (matches the contract semantics). opts.Limit must be > 0; use IterateAllPDPProviders for unbounded traversal.

func (*Service) GetPDPProvidersByIDs added in v0.2.1

func (s *Service) GetPDPProvidersByIDs(ctx context.Context, providerIDs []types.BigInt) ([]PDPProvider, error)

GetPDPProvidersByIDs returns PDP providers for the given ids using Multicall3. Failed, missing, or non-PDP entries are skipped.

func (*Service) GetProvider

func (s *Service) GetProvider(ctx context.Context, providerID types.BigInt) (*ProviderInfo, error)

GetProvider returns the provider by id. Returns an error wrapping ErrNotFound when no such provider exists (contract convention: a zero ServiceProvider address in the returned record). RPC or ABI errors are wrapped and propagated as-is.

func (*Service) GetProviderByAddress

func (s *Service) GetProviderByAddress(ctx context.Context, addr common.Address) (*ProviderInfo, error)

GetProviderByAddress returns the provider record whose serviceProvider matches addr. Returns an error wrapping ErrNotFound when no provider is registered for that address.

func (*Service) GetProviderCount

func (s *Service) GetProviderCount(ctx context.Context) (*big.Int, error)

GetProviderCount returns the total number of registered providers (active + inactive).

func (*Service) GetProviderIDByAddress

func (s *Service) GetProviderIDByAddress(ctx context.Context, addr common.Address) (types.BigInt, error)

GetProviderIDByAddress returns the provider ID for addr. Returns a zero ID when addr is not registered (contract convention).

func (*Service) GetProvidersByIDs

func (s *Service) GetProvidersByIDs(ctx context.Context, providerIDs []types.BigInt) ([]*ProviderInfo, error)

GetProvidersByIDs returns the provider records for the given ids, in the same order. Entries whose validIds flag was false come back as nil. An empty input slice returns an empty (non-nil) result slice.

func (*Service) IsProviderActive

func (s *Service) IsProviderActive(ctx context.Context, providerID types.BigInt) (bool, error)

IsProviderActive returns true if the provider id is registered AND active.

func (*Service) IsRegisteredProvider added in v0.2.1

func (s *Service) IsRegisteredProvider(ctx context.Context, addr common.Address) (bool, error)

IsRegisteredProvider returns true if addr is registered as a provider.

func (*Service) IterateAllPDPProviders

func (s *Service) IterateAllPDPProviders(ctx context.Context, onlyActive bool) iter.Seq2[PDPProvider, error]

IterateAllPDPProviders yields every PDP provider across all pages. When onlyActive is true the registry filters inactive providers before applying pagination. The iterator terminates cleanly on ctx cancellation (yielding the context error), on RPC error, or when the range body returns false.

func (*Service) RegisterProvider

func (s *Service) RegisterProvider(ctx context.Context, info ProviderRegistrationInfo, opts ...WriteOption) (*WriteResult, error)

RegisterProvider registers the caller as a storage provider and, in the same transaction, declares a PDP product. The caller is taken from the service's configured Signer; the Payee in info designates the account that receives payments (typically but not necessarily identical to the caller).

Pricing:

  • When a WithValue option is provided it must equal the current 5 FIL registration fee mirrored by the SDK.
  • Otherwise the current REGISTRATION_FEE() is read from the contract and used; an RPC error there is wrapped and returned before any transaction is broadcast.

The PDP offering is validated and encoded before broadcast: an invalid offering surfaces as ErrInvalidOffering with no on-chain side effects. Name and Description are display metadata; either may be empty, but both remain subject to their UTF-8 byte-length limits.

Returns a WriteResult carrying the broadcast tx hash. Use WithWait to block for the receipt; when the transaction reverts on-chain the receipt is preserved on the WriteResult alongside ErrTxFailed.

func (*Service) RemoveProduct

func (s *Service) RemoveProduct(ctx context.Context, productType ProductType, opts ...WriteOption) (*WriteResult, error)

RemoveProduct removes the given product type for the caller. PDP (ProductTypePDP) is the only variant exposed by the SDK today but the signature accepts the enum so callers are forward-compatible with future product types defined in the registry ABI.

func (*Service) RemoveProvider

func (s *Service) RemoveProvider(ctx context.Context, opts ...WriteOption) (*WriteResult, error)

RemoveProvider deregisters the caller from the registry. Existing data sets on the provider continue to settle; the registry merely flags the provider inactive for discovery purposes.

func (*Service) SelectActivePDPProviders

func (s *Service) SelectActivePDPProviders(ctx context.Context, f ProviderFilter) ([]PDPProvider, error)

SelectActivePDPProviders fetches all active PDP providers (handling pagination internally), applies f, and returns the survivors sorted deterministically by provider ID (ascending). This is the entry point that storage uses for provider selection; it deliberately avoids exposing raw pagination to the caller.

Filter semantics (all conditions are ANDed):

  • PieceSizeBytes: provider must satisfy minPieceSizeInBytes ≤ PieceSizeBytes ≤ maxPieceSizeInBytes. When maxPieceSizeInBytes is zero the upper bound is skipped.
  • PaymentToken: when non-nil, provider must declare exactly this payment token address. The zero address is a valid value and means FIL, matching the on-chain/TS semantics.
  • ExcludeIDs: providers whose ID appears in this slice are removed.

An error is returned if pagination exceeds maxSelectPages to guard against a misbehaving RPC that returns HasMore=true indefinitely.

func (*Service) UpdatePDPProduct

func (s *Service) UpdatePDPProduct(ctx context.Context, offering PDPOffering, capabilities map[string]string, opts ...WriteOption) (*WriteResult, error)

UpdatePDPProduct replaces the capability set associated with the caller's PDP product. The full offering + extras must be supplied; partial updates are not supported by the contract ABI.

func (*Service) UpdateProviderInfo

func (s *Service) UpdateProviderInfo(ctx context.Context, name, description string, opts ...WriteOption) (*WriteResult, error)

UpdateProviderInfo updates the caller's on-chain display metadata. Name and description may be empty to clear the current values, but both remain subject to their UTF-8 byte-length limits.

type ServiceProduct

type ServiceProduct struct {
	ProductType    ProductType
	CapabilityKeys []string
	IsActive       bool
}

ServiceProduct is the on-chain product record (pre-decoding of capability values).

type WriteOption

type WriteOption func(*writeConfig)

WriteOption tunes the behaviour of a single state-changing call.

func WithConfirmations

func WithConfirmations(n uint64) WriteOption

WithConfirmations requires N block confirmations in addition to WithWait.

func WithValue

func WithValue(v *big.Int) WriteOption

WithValue overrides the transaction's msg.value for payable calls. It is only consumed by Service.RegisterProvider; other writes ignore it. For RegisterProvider, an explicit value must equal the 5 FIL registration fee. When not provided, RegisterProvider reads the contract's REGISTRATION_FEE. A non-nil *big.Int is copied defensively before being stored on the transaction options.

func WithWait

func WithWait(timeout time.Duration) WriteOption

WithWait makes the call block until the transaction is mined, or the given timeout elapses. Zero / negative returns immediately after broadcast.

type WriteResult

type WriteResult = types.WriteResult

WriteResult is kept as an alias for backwards compatibility.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL