offchainmetadata

package
v0.70.9 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const (
	// The sync_state keys below describe the snapshot currently *in the
	// table*, not a per-source cache. There is one token_registry_entry
	// table, so there is one set of state, and all three move together on a
	// successful apply.
	//
	// TokenRegistrySyncStateKey holds that snapshot's HTTP entity tag.
	TokenRegistrySyncStateKey = "token_registry_etag"

	// MainnetTokenRegistryURL and TestnetTokenRegistryURL are the CIP-26
	// registries the Cardano Foundation and IOG publish. Both are served as
	// repository tarballs, which support conditional requests; see SyncOnce.
	MainnetTokenRegistryURL = "https://github.com/cardano-foundation/" +
		"cardano-token-registry/archive/refs/heads/master.tar.gz"
	TestnetTokenRegistryURL = "https://github.com/input-output-hk/" +
		"metadata-registry-testnet/archive/refs/heads/master.tar.gz"
)

Variables

This section is empty.

Functions

func ParseTokenRegistryEntry added in v0.70.1

func ParseTokenRegistryEntry(raw []byte) (*models.TokenRegistryEntry, error)

ParseTokenRegistryEntry decodes one CIP-26 registry mapping document.

The subject is required and must be a hex string of a policy ID, optionally followed by a hex-encoded asset name: lookups build the same string from raw on-chain bytes, so a subject that is not hex could never be matched and is rejected outright. Subjects are lower-cased for the same reason.

Individual properties are best-effort. A property whose envelope is malformed, whose value has the wrong JSON type, whose string value is blank, or whose decimals value is out of range is dropped and the remaining properties are kept. Only a document that fails to decode as JSON, or that carries no usable subject, returns an error.

Types

type Config

type Config struct {
	Logger *slog.Logger
	Store  Store
	// HTTPClient customizes fetch requests. When private addresses are not
	// allowed, New clones the client and replaces unsafe transport dial hooks.
	HTTPClient            *http.Client
	Interval              time.Duration
	RequestTimeout        time.Duration
	UserAgent             string
	IPFSGatewayURL        string
	BatchSize             int
	MaxBytes              int64
	AllowPrivateAddresses bool
}

type Fetcher

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

func New

func New(cfg Config) (*Fetcher, error)

func (*Fetcher) Start

func (f *Fetcher) Start(ctx context.Context) error

func (*Fetcher) Stop

func (f *Fetcher) Stop(ctx context.Context) error

type PoolMetadataFields added in v0.69.0

type PoolMetadataFields struct {
	Name        string
	Description string
	Ticker      string
	Homepage    string
}

PoolMetadataFields holds the decoded fields of a validated stake pool off-chain metadata document.

func ValidatePoolMetadata added in v0.69.0

func ValidatePoolMetadata(raw []byte) (*PoolMetadataFields, error)

ValidatePoolMetadata decodes and validates raw as Cardano stake-pool off-chain metadata. It mirrors cardano-api's validateAndHashStakePoolMetadata: the document must be at most 512 bytes, and must decode as a JSON object carrying required "name" (<=50 characters), "description" (<=255 characters), "ticker" (3-5 characters), and "homepage" (required, unbounded length) string fields.

A size violation is reported with the models.OffchainFetchErrBodyTooLargePrefix prefix; any other validation failure (JSON syntax, wrong JSON type, a missing/null required field, or a field failing its length constraint) is reported with the models.OffchainFetchErrDecodeErrorPrefix prefix. Callers classify the failure the same way readLimited's size errors are classified, by inspecting the returned error's message prefix.

type Store

type Store interface {
	EnsureOffchainMetadataPointers(
		ctx context.Context,
		now time.Time,
		txn types.Txn,
	) (int, error)
	GetOffchainMetadataFetchBatch(
		ctx context.Context,
		limit int,
		now time.Time,
		txn types.Txn,
	) ([]models.OffchainMetadata, error)
	SetOffchainMetadataFetchResult(
		ctx context.Context,
		doc *models.OffchainMetadata,
		txn types.Txn,
	) error
}

type TokenRegistryConfig added in v0.70.1

type TokenRegistryConfig struct {
	Logger *slog.Logger
	Store  TokenRegistryStore
	// HTTPClient customizes the download. When private addresses are not
	// allowed, NewTokenRegistrySync clones the client and replaces unsafe
	// transport dial hooks, exactly as the per-URL fetcher does.
	HTTPClient *http.Client
	// SourceURL overrides the network-derived registry source, for operators
	// running a mirror. Empty selects by Network.
	SourceURL string
	// Network selects the default registry source; anything other than
	// "mainnet" uses the IOG testnet registry.
	Network        string
	UserAgent      string
	Interval       time.Duration
	RequestTimeout time.Duration
	// MaxBytes caps the compressed download; MaxEntryBytes caps one mapping.
	MaxBytes      int64
	MaxEntryBytes int64
	// StoreLogos opts into persisting base64 logo payloads, which are
	// roughly 90% of registry bytes. Off by default.
	StoreLogos bool
	// AllowPrivateAddresses permits fetching private, loopback, and
	// link-local addresses. Leave false for the default SSRF guard.
	AllowPrivateAddresses bool
}

TokenRegistryConfig configures the CIP-26 token registry sync. Zero values fall back to the defaults above.

type TokenRegistryStore added in v0.70.1

type TokenRegistryStore interface {
	UpsertTokenRegistryEntries(
		ctx context.Context,
		entries []models.TokenRegistryEntry,
		syncedAt time.Time,
		txn types.Txn,
	) (int, error)
	PruneTokenRegistryEntriesBefore(
		ctx context.Context,
		cutoff time.Time,
		txn types.Txn,
	) (int, error)
	GetSyncState(key string, txn types.Txn) (string, error)
	SetSyncState(key, value string, txn types.Txn) error
}

TokenRegistryStore is the persistence surface the registry sync needs.

type TokenRegistrySync added in v0.70.1

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

TokenRegistrySync periodically pulls a CIP-26 token registry and upserts its mappings, so that GET /assets/{asset} can serve off-chain token metadata from local state.

The sync is holdings-agnostic by construction: every node pulls the same complete registry, so unlike per-asset lookups against a remote metadata server it reveals nothing about which assets a user holds.

func NewTokenRegistrySync added in v0.70.1

func NewTokenRegistrySync(
	cfg TokenRegistryConfig,
) (*TokenRegistrySync, error)

NewTokenRegistrySync validates cfg and returns a sync that is not yet running.

func (*TokenRegistrySync) SourceURL added in v0.70.1

func (s *TokenRegistrySync) SourceURL() string

SourceURL returns the resolved registry source.

func (*TokenRegistrySync) Start added in v0.70.1

func (s *TokenRegistrySync) Start(ctx context.Context) error

func (*TokenRegistrySync) Stop added in v0.70.1

func (s *TokenRegistrySync) Stop(ctx context.Context) error

Stop signals the worker and waits for it to exit.

The wait is not abandoned when ctx expires. Callers tear the metadata store down immediately after Stop returns (node_shutdown.go phase 3, node_lifecycle.go's live storage swap), so returning while the worker could still reach that store would hand it a closed database. An expired context downgrades to a warning and the wait continues, matching koiosparity.Observer.Stop, which releases its cache under the same constraint. Cancelling the worker aborts any in-flight download, so the remaining wait is bounded by one store write rather than by the registry transfer.

Stop is idempotent: the startup-failure rollback stack and shutdown() can both reach it for the same instance.

func (*TokenRegistrySync) SyncOnce added in v0.70.1

func (s *TokenRegistrySync) SyncOnce(ctx context.Context) (int, error)

SyncOnce performs a single registry pull and returns the number of entries written.

The mainnet registry is roughly 240MB, so an unconditional download every interval would be indefensible. SyncOnce sends the entity tag recorded by the previous successful sync as If-None-Match; an unchanged registry answers 304 and costs one request with no body. The tag is recorded only after the whole snapshot has been applied, so an interrupted sync retries in full rather than recording progress it did not make.

Jump to

Keyboard shortcuts

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