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)
}
Output:
Index ¶
- Constants
- Variables
- func CapabilitiesListToMap(keys []string, values [][]byte) map[string][]byte
- func EncodePDPCapabilities(o PDPOffering, extras map[string]string) (keys []string, values [][]byte, err error)
- func ValidatePDPOffering(o PDPOffering) error
- type Backend
- type EthClient
- type Options
- type PDPOffering
- type PDPProvider
- type PaginatedPDPProviders
- type ProductType
- type ProviderFilter
- type ProviderInfo
- type ProviderRegistrationInfo
- type Service
- func (s *Service) AddPDPProduct(ctx context.Context, offering PDPOffering, capabilities map[string]string, ...) (*WriteResult, error)
- func (s *Service) Address() common.Address
- func (s *Service) GetActiveProviderCount(ctx context.Context) (*big.Int, error)
- func (s *Service) GetPDPProvider(ctx context.Context, providerID types.BigInt) (*PDPProvider, error)
- func (s *Service) GetPDPProviderByAddress(ctx context.Context, addr common.Address) (*PDPProvider, error)
- func (s *Service) GetPDPProviders(ctx context.Context, onlyActive bool, opts types.ListOptions) (*PaginatedPDPProviders, error)
- func (s *Service) GetPDPProvidersByIDs(ctx context.Context, providerIDs []types.BigInt) ([]PDPProvider, error)
- func (s *Service) GetProvider(ctx context.Context, providerID types.BigInt) (*ProviderInfo, error)
- func (s *Service) GetProviderByAddress(ctx context.Context, addr common.Address) (*ProviderInfo, error)
- func (s *Service) GetProviderCount(ctx context.Context) (*big.Int, error)
- func (s *Service) GetProviderIDByAddress(ctx context.Context, addr common.Address) (types.BigInt, error)
- func (s *Service) GetProvidersByIDs(ctx context.Context, providerIDs []types.BigInt) ([]*ProviderInfo, error)
- func (s *Service) IsProviderActive(ctx context.Context, providerID types.BigInt) (bool, error)
- func (s *Service) IsRegisteredProvider(ctx context.Context, addr common.Address) (bool, error)
- func (s *Service) IterateAllPDPProviders(ctx context.Context, onlyActive bool) iter.Seq2[PDPProvider, error]
- func (s *Service) RegisterProvider(ctx context.Context, info ProviderRegistrationInfo, opts ...WriteOption) (*WriteResult, error)
- func (s *Service) RemoveProduct(ctx context.Context, productType ProductType, opts ...WriteOption) (*WriteResult, error)
- func (s *Service) RemoveProvider(ctx context.Context, opts ...WriteOption) (*WriteResult, error)
- func (s *Service) SelectActivePDPProviders(ctx context.Context, f ProviderFilter) ([]PDPProvider, error)
- func (s *Service) UpdatePDPProduct(ctx context.Context, offering PDPOffering, capabilities map[string]string, ...) (*WriteResult, error)
- func (s *Service) UpdateProviderInfo(ctx context.Context, name, description string, opts ...WriteOption) (*WriteResult, error)
- type ServiceProduct
- type WriteOption
- type WriteResult
Examples ¶
Constants ¶
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 ¶
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.
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.
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).
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.
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.
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).
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 ¶
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):
- serviceURL (UTF-8 bytes)
- minPieceSizeInBytes (big-endian minimal; zero encodes as 0x00)
- maxPieceSizeInBytes
- (optional) ipniPiece (single byte 0x01, omitted when false)
- (optional) ipniIpfs (single byte 0x01, omitted when false)
- storagePricePerTibPerDay
- minProvingPeriodInEpochs
- location (required UTF-8 bytes)
- 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 (*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) GetActiveProviderCount ¶
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 ¶
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 ¶
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 ¶
IsProviderActive returns true if the provider id is registered AND active.
func (*Service) IsRegisteredProvider ¶ added in v0.2.1
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.
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 must be non-empty; description may be empty to clear the current value.
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.