driver

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2025 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// Unknown is the status of a transaction that is unknown
	Unknown = driver2.Unknown
	// Pending is the status of a transaction that has been submitted to the ledger
	Pending = driver2.Pending
	// Confirmed is the status of a transaction that has been confirmed by the ledger
	Confirmed = driver2.Confirmed
	// Deleted is the status of a transaction that has been deleted due to a failure to commit
	Deleted = driver2.Deleted
)

Variables

View Source
var (
	ErrTokenDoesNotExist = errors.New("token does not exist")
)
View Source
var (
	ErrTokenRequestDoesNotExist = errors.New("token request does not exist")
)
View Source
var (
	// TxStatusMessage maps TxStatus to string
	TxStatusMessage = map[TxStatus]string{
		Unknown:   "Unknown",
		Pending:   "Pending",
		Confirmed: "Confirmed",
		Deleted:   "Deleted",
	}
)

Functions

This section is empty.

Types

type ActionType

type ActionType int

ActionType is the type of transaction

const (
	// Issue is the action type for issuing tokens.
	Issue ActionType = iota
	// Transfer is the action type for transferring tokens.
	Transfer
	// Redeem is the action type for redeeming tokens.
	Redeem
)

type AtomicWrite

type AtomicWrite interface {
	// Commit commits the current update to the database
	Commit() error

	// Rollback discards the in progress database transaction.
	// It logs but otherwise ignores errors rolling back:
	// the result is always the end of the transaction.
	Rollback()

	// AddTokenRequest binds the passed transaction id to the passed token request
	AddTokenRequest(ctx context.Context, txID string, tr []byte, applicationMetadata, publicMetadata map[string][]byte, ppHash driver.PPHash) error

	// AddMovement adds a movement record to the database transaction.
	// Each token transaction can be seen as a list of movements.
	// This operation _requires_ a TokenRequest with the same tx_id to exist
	AddMovement(ctx context.Context, records ...MovementRecord) error

	// AddTransaction adds a transaction record to the database transaction.
	// This operation _requires_ a TokenRequest with the same tx_id to exist
	AddTransaction(ctx context.Context, records ...TransactionRecord) error

	// AddValidationRecord adds a new validation records for the given params
	// This operation _requires_ a TokenRequest with the same tx_id to exist
	AddValidationRecord(ctx context.Context, txID string, meta map[string][]byte) error
}

type AuditTransactionStore added in v0.5.0

type AuditTransactionStore interface {
	// Close closes the database
	Close() error

	// BeginAtomicWrite opens an atomic database transaction. It must be committed or discarded.
	BeginAtomicWrite() (AtomicWrite, error)

	// SetStatus sets the status of a TokenRequest
	// (and with that, the associated ValidationRecord, Movement and Transaction)
	SetStatus(ctx context.Context, txID string, status TxStatus, message string) error

	// GetStatus returns the status of a given transaction.
	// It returns an error if the transaction is not found
	GetStatus(ctx context.Context, txID string) (TxStatus, string, error)

	// QueryTransactions returns a list of transactions that match the passed params
	QueryTransactions(ctx context.Context, params QueryTransactionsParams, pagination driver.Pagination) (*driver.PageIterator[*TransactionRecord], error)

	// QueryMovements returns a list of movement records
	QueryMovements(ctx context.Context, params QueryMovementsParams) ([]*MovementRecord, error)

	// QueryValidations returns an iterator over the validation records matching the passed params
	QueryValidations(ctx context.Context, params QueryValidationRecordsParams) (ValidationRecordsIterator, error)

	// QueryTokenRequests returns an iterator over the token requests matching the passed params
	QueryTokenRequests(ctx context.Context, params QueryTokenRequestsParams) (TokenRequestIterator, error)

	// GetTokenRequest returns the token request bound to the passed transaction id, if available.
	// It returns nil without error if the key is not found.
	GetTokenRequest(ctx context.Context, txID string) ([]byte, error)
}

AuditTransactionStore defines the interface for a database to store the audit records of token transactions.

type CertificationStore added in v0.5.0

type CertificationStore interface {
	// ExistsCertification returns true if a certification for the passed token exists,
	// false otherwise
	ExistsCertification(ctx context.Context, id *token.ID) bool

	// StoreCertifications stores the passed certifications
	StoreCertifications(ctx context.Context, certifications map[*token.ID][]byte) error

	// GetCertifications returns the certifications of the passed tokens.
	// For each token, the callback function is invoked.
	// If a token doesn't have a certification, the function returns an error
	GetCertifications(ctx context.Context, ids []*token.ID) ([][]byte, error)
	// Close closes the databases
	Close() error
}

CertificationStore defines a database to manager token certifications

type Config added in v0.5.0

type Config = driver.Config

type ConfigProvider

type ConfigProvider interface {
	UnmarshalKey(key string, rawVal interface{}) error
	GetString(key string) string
	GetBool(key string) bool
	IsSet(key string) bool
	TranslatePath(path string) string
}

type Driver added in v0.5.0

type Driver interface {
	NewTokenLock(driver.PersistenceName, ...string) (TokenLockStore, error)

	NewWallet(driver.PersistenceName, ...string) (WalletStore, error)

	NewIdentity(driver.PersistenceName, ...string) (IdentityStore, error)

	NewToken(driver.PersistenceName, ...string) (TokenStore, error)

	NewTokenNotifier(driver.PersistenceName, ...string) (TokenNotifier, error)

	NewAuditTransaction(driver.PersistenceName, ...string) (AuditTransactionStore, error)

	NewOwnerTransaction(driver.PersistenceName, ...string) (TokenTransactionStore, error)
}

type IdentityConfiguration

type IdentityConfiguration = driver.IdentityConfiguration

type IdentityStore added in v0.5.0

type IdentityStore = driver2.IdentityStoreService

type MovementDirection

type MovementDirection int

MovementDirection defines the direction of a movement.

const (
	// Sent amount transferred from.
	Sent MovementDirection = iota
	// Received amount transferred to.
	Received
	// All amount transferred to and from.
	All
)

type MovementRecord

type MovementRecord struct {
	// TxID is the transaction ID
	TxID string
	// EnrollmentID is the enrollment ID of the account that is receiving or sending
	EnrollmentID string
	// TokenType is the type of token
	TokenType token2.Type
	// Amount is positive if tokens are received. Negative otherwise
	Amount *big.Int
	// Timestamp is the time the transaction was submitted to the db
	Timestamp time.Time
	// Status is the status of the transaction
	Status TxStatus
}

MovementRecord is a record of a movement of assets. Given a Token Transaction, a movement record is created for each enrollment ID that participated in the transaction and each token type that was transferred. The movement record contains the total amount of the token type that was transferred to/from the enrollment ID in a given token transaction.

func (MovementRecord) String added in v0.5.0

func (r MovementRecord) String() string

type NamedDriver added in v0.5.0

type NamedDriver = driver2.NamedDriver[Driver]

type QueryMovementsParams

type QueryMovementsParams struct {
	// EnrollmentIDs is the enrollment IDs of the accounts to query
	EnrollmentIDs []string
	// TokenTypes is the token types to query
	TokenTypes []token2.Type
	// TxStatuses is the statuses of the transactions to query
	TxStatuses []TxStatus
	// SearchDirection is the direction of the search
	SearchDirection SearchDirection
	// MovementDirection is the direction of the movement
	MovementDirection MovementDirection
	// NumRecords is the number of records to return
	// If 0, all records are returned
	NumRecords int
}

QueryMovementsParams defines the parameters for querying movements. Movement records will be filtered by EnrollmentID, TokenFormat, and Status. SearchDirection tells if the search should start from the oldest to the newest records or vice versa. MovementDirection which amounts to consider. Sent correspond to a negative amount, Received to a positive amount, and All to both.

type QueryTokenDetailsParams

type QueryTokenDetailsParams struct {
	// WalletID is the optional identifier of the wallet owning the token
	WalletID string
	// OwnerType is the type of owner, for instance 'idemix' or 'htlc'
	OwnerType string
	// TokenType (optional) is the type of token
	TokenType token.Type
	// IDs is an optional list of specific token ids to return
	IDs []*token.ID
	// TransactionIDs selects tokens that are the output of the provided transaction ids.
	TransactionIDs []string
	// IncludeDeleted determines whether to include spent tokens. It defaults to false.
	IncludeDeleted bool
	// Spendable determines whether to include only spendable/non-spendable or any tokens. It defaults to nil (any tokens)
	Spendable SpendableFilter
	// LedgerTokenFormats selects tokens whose output on the ledger has a format in the list
	LedgerTokenFormats []token.Format
}

QueryTokenDetailsParams defines the parameters for querying token details

type QueryTokenRequestsParams

type QueryTokenRequestsParams struct {
	// Statuses is the list of transaction status to accept
	// If empty, any status is accepted
	Statuses []TxStatus
}

QueryTokenRequestsParams defines the parameters for querying token requests

type QueryTransactionsParams

type QueryTransactionsParams struct {
	// IDs is the list of transaction ids. If nil or empty, all transactions are returned
	IDs []string
	// ExcludeToSelf can be used to filter out 'change' transactions where the sender and
	// recipient have the same enrollment id.
	ExcludeToSelf bool
	// SenderWallet is the wallet of the sender
	// If empty, any sender is accepted
	// If the sender does not match but the recipient matches, the transaction is returned
	SenderWallet string
	// RecipientWallet is the wallet of the recipient
	// If empty, any recipient is accepted
	// If the recipient does not match but the sender matches, the transaction is returned
	RecipientWallet string
	// From is the start time of the query
	// If nil, the query starts from the first transaction
	From *time.Time
	// To is the end time of the query
	// If nil, the query ends at the last transaction
	To *time.Time
	// ActionTypes is the list of action types to accept
	// If empty, any action type is accepted
	ActionTypes []ActionType
	// Statuses is the list of transaction status to accept
	// If empty, any status is accepted
	Statuses []TxStatus
	// TokenTypes is the list of token types to accept
	// If empty, any token type is accepted
	TokenTypes []token2.Type
}

QueryTransactionsParams defines the parameters for querying transactions. One can filter by sender, by recipient, and by time range.

type QueryValidationRecordsParams

type QueryValidationRecordsParams struct {
	// From is the start time of the query
	// If nil, the query starts from the first transaction
	From *time.Time
	// To is the end time of the query
	// If nil, the query ends at the last transaction
	To *time.Time
	// Statuses is the list of transaction status to accept
	// If empty, any status is accepted
	Statuses []TxStatus
	// Filter defines a custom filter function.
	// If specified, this filter will be applied.
	// the filter returns true if the record must be selected, false otherwise.
	Filter func(record *ValidationRecord) bool
}

QueryValidationRecordsParams defines the parameters for querying validation records.

type SearchDirection

type SearchDirection int

SearchDirection defines the direction of a search.

const (
	// FromLast defines the direction of a search from the last key.
	FromLast SearchDirection = iota
	// FromBeginning defines the direction of a search from the first key.
	FromBeginning
)

type SpendableFilter

type SpendableFilter int
const (
	Any SpendableFilter = iota
	SpendableOnly
	NonSpendableOnly
)

type TokenDetails

type TokenDetails struct {
	// TxID is the ID of the transaction that created the token
	TxID string
	// Index is the index in the transaction
	Index uint64
	// OwnerIdentity is the serialization of the owner identity
	OwnerIdentity []byte
	// OwnerType is the deserialized type inside OwnerRaw
	OwnerType string
	// OwnerEnrollment is the enrollment id of the owner
	OwnerEnrollment string
	// Type is the type of token
	Type string
	// Amount is the Quantity converted to decimal
	Amount uint64
	// IsSpent is true if the token has been spent
	IsSpent bool
	// SpentBy is the transactionID that spent this token, if available
	SpentBy string
	// StoredAt is the moment the token was stored by this wallet
	StoredAt time.Time
}

TokenDetails provides details about an owned (spent or unspent) token

type TokenLockStore added in v0.5.0

type TokenLockStore interface {
	common.DBObject
	// Lock locks a specific token for the consumer TX
	Lock(ctx context.Context, tokenID *token.ID, consumerTxID transaction.ID) error
	// UnlockByTxID unlocks all tokens locked by the consumer TX
	UnlockByTxID(ctx context.Context, consumerTxID transaction.ID) error
	// Cleanup removes the locks such that either:
	// 1. The transaction that locked that token is valid or invalid;
	// 2. The lock is too old.
	Cleanup(ctx context.Context, leaseExpiry time.Duration) error
	// Close closes the database
	Close() error
}

TokenLockStore enforces that a token be used only by one process A housekeeping job can clean up expired locks (e.g. created_at is more than 5 minutes ago) in order to: - avoid that the table grows infinitely - unlock tokens that were locked by a process that exited unexpectedly

type TokenNotifier

type TokenNotifier driver2.Notifier

TokenNotifier is the observable version of TokenStore

type TokenNotifierDriver

type TokenNotifierDriver interface {
	// Open opens a token database with its listeners
	Open(cp ConfigProvider, tmsID token2.TMSID) (TokenNotifier, error)
}

TokenNotifierDriver is the interface for a token database driver

type TokenRecord

type TokenRecord struct {
	// TxID is the ID of the transaction that created the token
	TxID string
	// Index is the index in the transaction
	Index uint64
	// IssuerRaw represents the serialization of the issuer identity
	// if this is an IssuedToken.
	IssuerRaw []byte
	// OwnerRaw is the serialization of the owner TypedIdentity
	OwnerRaw []byte
	// OwnerType is the deserialized type inside OwnerRaw
	OwnerType string
	// OwnerIdentity is the deserialized Identity inside OwnerRaw
	OwnerIdentity []byte
	// OwnerWalletID is the identifier of the wallet that owns this token, it might be empty
	OwnerWalletID string
	// Ledger is the raw token as stored on the ledger
	Ledger []byte
	// LedgerFormat is the type of the raw token as stored on the ledger
	LedgerFormat token.Format
	// LedgerMetadata is the metadata associated to the content of Ledger
	LedgerMetadata []byte
	// Quantity is the number of units of Type carried in the token.
	// It is encoded as a string containing a number in base 16. The string has prefix “0x”.
	Quantity string
	// Type is the type of token
	Type token.Type
	// Amount is the Quantity converted to decimal
	Amount uint64
	// Owner is used to mark the token as owned by this node
	Owner bool
	// Auditor is used to mark this token as audited by this node
	Auditor bool
	// Issuer issued to mark this token as issued by this node
	Issuer bool
}

type TokenRequestIterator

type TokenRequestIterator = iterators.Iterator[*TokenRequestRecord]

TokenRequestIterator is an iterator for token requests

type TokenRequestRecord

type TokenRequestRecord struct {
	// TxID is the transaction ID
	TxID string
	// TokenRequest is the token request marshalled
	TokenRequest []byte
	// Status is the status of the transaction
	Status TxStatus
}

type TokenStore added in v0.5.0

type TokenStore interface {
	CertificationStore
	// DeleteTokens marks the passsed tokens as deleted
	DeleteTokens(ctx context.Context, deletedBy string, toDelete ...*token.ID) error
	// IsMine return true if the passed token was stored before
	IsMine(ctx context.Context, txID string, index uint64) (bool, error)
	// UnspentTokensIterator returns an iterator over all owned tokens
	UnspentTokensIterator(ctx context.Context) (driver.UnspentTokensIterator, error)
	// UnspentLedgerTokensIteratorBy returns an iterator over all unspent ledger tokens
	UnspentLedgerTokensIteratorBy(ctx context.Context) (driver.LedgerTokensIterator, error)
	// UnspentTokensIteratorBy returns an iterator over all tokens owned by the passed wallet identifier and of a given type
	UnspentTokensIteratorBy(ctx context.Context, walletID string, tokenType token.Type) (driver.UnspentTokensIterator, error)
	// SpendableTokensIteratorBy returns an iterator over all tokens owned solely by the passed wallet identifier and of a given type
	SpendableTokensIteratorBy(ctx context.Context, walletID string, typ token.Type) (driver.SpendableTokensIterator, error)
	// UnsupportedTokensIteratorBy returns the minimum information for upgrade about the tokens that are not supported
	UnsupportedTokensIteratorBy(ctx context.Context, walletID string, tokenType token.Type) (driver.UnsupportedTokensIterator, error)
	// ListUnspentTokensBy returns the list of all tokens owned by the passed identifier of a given type
	ListUnspentTokensBy(ctx context.Context, walletID string, typ token.Type) (*token.UnspentTokens, error)
	// ListUnspentTokens returns the list of all owned tokens
	ListUnspentTokens(ctx context.Context) (*token.UnspentTokens, error)
	// ListAuditTokens returns the audited tokens for the passed ids
	ListAuditTokens(ctx context.Context, ids ...*token.ID) ([]*token.Token, error)
	// ListHistoryIssuedTokens returns the list of all issued tokens
	ListHistoryIssuedTokens(ctx context.Context) (*token.IssuedTokens, error)
	// GetTokenOutputs returns the value of the tokens as they appear on the ledger for the passed ids.
	// For each token, the call-back function is invoked. The call-back function is invoked respecting the order of the passed ids.
	GetTokenOutputs(ctx context.Context, ids []*token.ID, callback driver.QueryCallbackFunc) error
	// GetTokenMetadata returns the metadata of the tokens for the passed ids.
	GetTokenMetadata(ctx context.Context, ids []*token.ID) ([][]byte, error)
	// GetTokenOutputsAndMeta retrieves both the token output, metadata, and type for the passed ids.
	GetTokenOutputsAndMeta(ctx context.Context, ids []*token.ID) ([][]byte, [][]byte, []token.Format, error)
	// GetTokens returns the owned tokens and their identifier keys for the passed ids.
	GetTokens(ctx context.Context, inputs ...*token.ID) ([]*token.Token, error)
	// WhoDeletedTokens for each id, the function return if it was deleted and by who as per the Delete function
	WhoDeletedTokens(ctx context.Context, inputs ...*token.ID) ([]string, []bool, error)
	// TransactionExists returns true if a token with that transaction id exists in the db
	TransactionExists(ctx context.Context, id string) (bool, error)
	// StorePublicParams stores the public parameters.
	// If they already exist, the function return with no error. No changes are applied.
	StorePublicParams(ctx context.Context, raw []byte) error
	// PublicParams returns the stored public parameters.
	// If not public parameters are available, it returns nil with no error
	PublicParams(ctx context.Context) ([]byte, error)
	// PublicParamsByHash returns the public parameters whose hash matches the passed one.
	// If not public parameters are available for that hash, it returns an error
	PublicParamsByHash(ctx context.Context, rawHash driver.PPHash) ([]byte, error)
	// NewTokenDBTransaction returns a new Transaction to commit atomically multiple operations
	NewTokenDBTransaction() (TokenStoreTransaction, error)
	// QueryTokenDetails provides detailed information about tokens
	QueryTokenDetails(ctx context.Context, params QueryTokenDetailsParams) ([]TokenDetails, error)
	// Balance returns the sun of the amounts of the tokens with type and EID equal to those passed as arguments.
	Balance(ctx context.Context, ownerEID string, typ token.Type) (uint64, error)
	// SetSupportedTokenFormats sets the supported token formats
	SetSupportedTokenFormats(formats []token.Format) error
}

TokenStore defines a database to store token related info

type TokenStoreTransaction added in v0.5.0

type TokenStoreTransaction interface {
	// GetToken returns the owned tokens and their identifier keys for the passed ids.
	GetToken(ctx context.Context, tokenID token.ID, includeDeleted bool) (*token.Token, []string, error)
	// Delete marks the passed token as deleted by a given identifier (idempotent)
	Delete(ctx context.Context, tokenID token.ID, deletedBy string) error
	// StoreToken stores the passed token record in relation to the passed owner identifiers, if any
	StoreToken(ctx context.Context, tr TokenRecord, owners []string) error
	// SetSpendable updates the spendable flag of the passed token
	SetSpendable(ctx context.Context, tokenID token.ID, spendable bool) error
	// SetSpendableBySupportedTokenFormats sets the spendable flag to true for all the tokens having one of the passed token type.
	// The spendable flag is set to false for the other tokens
	SetSpendableBySupportedTokenFormats(ctx context.Context, formats []token.Format) error
	// Commit commits this transaction
	Commit() error
	// Rollback rollbacks this transaction
	Rollback() error
}

type TokenTransactionStore added in v0.5.0

type TokenTransactionStore interface {
	TransactionStore
	TransactionEndorsementAckStore
}

TokenTransactionStore defines the interface for a token transaction database. This database is used to store records related to the processed token transactions.

type TransactionEndorsementAckStore added in v0.5.0

type TransactionEndorsementAckStore interface {
	// AddTransactionEndorsementAck records the signature of a given endorser for a given transaction
	AddTransactionEndorsementAck(ctx context.Context, txID string, endorser token.Identity, sigma []byte) error

	// GetTransactionEndorsementAcks returns the endorsement signatures for the given transaction id
	GetTransactionEndorsementAcks(ctx context.Context, txID string) (map[string][]byte, error)
}

type TransactionIterator

type TransactionIterator = iterators.Iterator[*TransactionRecord]

TransactionIterator is an iterator for transactions

type TransactionRecord

type TransactionRecord struct {
	// TxID is the transaction ID
	TxID string
	// ActionType is the type of action performed by this transaction record
	ActionType ActionType
	// SenderEID is the enrollment ID of the account that is sending tokens
	SenderEID string
	// RecipientEID is the enrollment ID of the account that is receiving tokens
	RecipientEID string
	// TokenType is the type of token
	TokenType token2.Type
	// Amount is positive if tokens are received. Negative otherwise
	Amount *big.Int
	// Timestamp is the time the transaction was submitted to the db
	Timestamp time.Time
	// Status is the status of the transaction
	Status TxStatus
	// ApplicationMetadata is the metadata sent by the application in the
	// transient field. It is not validated or recorded on the ledger.
	ApplicationMetadata map[string][]byte
	// PublicMetadata is the metadata that is stored on the ledger as part
	// of an Issuance or Transfer Action (for instance the HTLC hash).
	PublicMetadata map[string][]byte
}

TransactionRecord is a more finer-grained version of a movement record. Given a Token Transaction, for each token action in the Token Request, a transaction record is created for each unique enrollment ID found in the outputs. The transaction record contains the total amount of the token type that was transferred to/from that enrollment ID in that action.

func (TransactionRecord) String

func (t TransactionRecord) String() string

type TransactionStore added in v0.5.0

type TransactionStore interface {
	// Close closes the databases
	Close() error

	// BeginAtomicWrite opens an atomic database transaction. It must be committed or discarded.
	BeginAtomicWrite() (AtomicWrite, error)

	// SetStatus sets the status of a TokenRequest
	// (and with that, the associated ValidationRecord, Movement and Transaction)
	SetStatus(ctx context.Context, txID string, status TxStatus, message string) error

	// GetStatus returns the status of a given transaction.
	// It returns an error if the transaction is not found
	GetStatus(ctx context.Context, txID string) (TxStatus, string, error)

	QueryTransactions(ctx context.Context, params QueryTransactionsParams, pagination driver2.Pagination) (*driver2.PageIterator[*TransactionRecord], error)

	// QueryMovements returns a list of movement records
	QueryMovements(ctx context.Context, params QueryMovementsParams) ([]*MovementRecord, error)

	// QueryValidations returns a list of validation  records
	QueryValidations(ctx context.Context, params QueryValidationRecordsParams) (ValidationRecordsIterator, error)

	// QueryTokenRequests returns an iterator over the token requests matching the passed params
	QueryTokenRequests(ctx context.Context, params QueryTokenRequestsParams) (TokenRequestIterator, error)

	// GetTokenRequest returns the token request bound to the passed transaction id, if available.
	// It returns nil without error if the key is not found.
	GetTokenRequest(ctx context.Context, txID string) ([]byte, error)
}

type TxStatus

type TxStatus = driver2.TxStatus

TxStatus is the status of a transaction

type ValidationRecord

type ValidationRecord struct {
	// TxID is the transaction ID
	TxID string
	// TokenRequest is the token request marshalled
	TokenRequest []byte
	// Metadata is the metadata produced by the validator when evaluating the token request
	Metadata map[string][]byte
	// Timestamp is the time the transaction was submitted to the db
	Timestamp time.Time
	// Status is the status of the transaction
	Status TxStatus
}

ValidationRecord is a record that contains information about the validation of a given token request

type ValidationRecordsIterator

type ValidationRecordsIterator = iterators.Iterator[*ValidationRecord]

ValidationRecordsIterator is an iterator for transactions

type WalletID

type WalletID = driver2.WalletID

type WalletStore added in v0.5.0

type WalletStore = driver2.WalletStoreService

Jump to

Keyboard shortcuts

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