client

package
Version: v0.7.5 Latest Latest
Warning

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

Go to latest
Published: Aug 9, 2021 License: Apache-2.0, BSD-2-Clause Imports: 19 Imported by: 4

Documentation

Overview

Package client implements a very simple wrapper for GoShimmer's web API.

Index

Constants

View Source
const (

	// RouteDiagnosticMessages is the API route for message diagnostics
	RouteDiagnosticMessages = routeDiagnostics + "/messages"
	// RouteDiagnosticsFirstWeakMessageReferences is the API route for first weak message diagnostics
	RouteDiagnosticsFirstWeakMessageReferences = RouteDiagnosticMessages + "/firstweakreferences"
	// RouteDiagnosticsMessageRank is the API route for message diagnostics with a rank filter
	RouteDiagnosticsMessageRank = RouteDiagnosticMessages + "/rank/:rank"
	// RouteDiagnosticsUtxoDag is the API route for Utxo Dag diagnostics
	RouteDiagnosticsUtxoDag = routeDiagnostics + "/utxodag"
	// RouteDiagnosticsBranches is the API route for branches diagnostics
	RouteDiagnosticsBranches = routeDiagnostics + "/branches"
	// RouteDiagnosticsLazyBookedBranches is the API route for booked branches diagnostics
	RouteDiagnosticsLazyBookedBranches = RouteDiagnosticsBranches + "/lazybooked"
	// RouteDiagnosticsInvalidBranches is the API route for invalid branches diagnostics
	RouteDiagnosticsInvalidBranches = RouteDiagnosticsBranches + "/invalid"
	// RouteDiagnosticsTips is the API route for tips diagnostics
	RouteDiagnosticsTips = routeDiagnostics + "/tips"
	// RouteDiagnosticsStrongTips is the API route for strong tips diagnostics
	RouteDiagnosticsStrongTips = RouteDiagnosticsTips + "/strong"
	// RouteDiagnosticsWeakTips is the API route for weak tips diagnostics
	RouteDiagnosticsWeakTips = RouteDiagnosticsTips + "/weak"
	// RouteDiagnosticsDRNG is the API route for DRNG diagnostics
	RouteDiagnosticsDRNG = routeDiagnostics + "/drng"
)

Variables

View Source
var (
	// ErrBadRequest defines the "bad request" error.
	ErrBadRequest = errors.New("bad request")
	// ErrInternalServerError defines the "internal server error" error.
	ErrInternalServerError = errors.New("internal server error")
	// ErrNotFound defines the "not found" error.
	ErrNotFound = errors.New("not found")
	// ErrUnauthorized defines the "unauthorized" error.
	ErrUnauthorized = errors.New("unauthorized")
	// ErrUnknownError defines the "unknown error" error.
	ErrUnknownError = errors.New("unknown error")
	// ErrNotImplemented defines the "operation not implemented/supported/available" error.
	ErrNotImplemented = errors.New("operation not implemented/supported/available")
)

Functions

This section is empty.

Types

type BasicAuth added in v0.3.0

type BasicAuth struct {
	Enabled  bool   `json:"enabled,omitempty"`
	Username string `json:"username,omitempty"`
	Password string `json:"password,omitempty"`
}

BasicAuth defines the basic-auth struct.

func (BasicAuth) Credentials added in v0.3.0

func (b BasicAuth) Credentials() (username, password string)

Credentials returns the username and password of a given BasicAuth.

func (BasicAuth) IsEnabled added in v0.3.0

func (b BasicAuth) IsEnabled() bool

IsEnabled returns the enabled state of a given BasicAuth.

type GoShimmerAPI

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

GoShimmerAPI is an API wrapper over the web API of GoShimmer.

func NewGoShimmerAPI

func NewGoShimmerAPI(baseURL string, setters ...Option) *GoShimmerAPI

NewGoShimmerAPI returns a new *GoShimmerAPI with the given baseURL and options.

func (*GoShimmerAPI) AddManualPeers added in v0.5.8

func (api *GoShimmerAPI) AddManualPeers(peers []*manualpeering.KnownPeerToAdd) error

AddManualPeers adds the provided list of peers to the manual peering layer.

func (*GoShimmerAPI) BaseURL added in v0.2.0

func (api *GoShimmerAPI) BaseURL() string

BaseURL returns the baseURL of the API.

func (*GoShimmerAPI) BroadcastCollectiveBeacon added in v0.2.0

func (api *GoShimmerAPI) BroadcastCollectiveBeacon(payload []byte) (string, error)

BroadcastCollectiveBeacon sends the given collective beacon (payload) by creating a message in the backend.

func (*GoShimmerAPI) Data added in v0.2.0

func (api *GoShimmerAPI) Data(data []byte) (string, error)

Data sends the given data (payload) by creating a message in the backend.

func (*GoShimmerAPI) GetAddressOutputs added in v0.6.0

func (api *GoShimmerAPI) GetAddressOutputs(base58EncodedAddress string) (*jsonmodels.GetAddressResponse, error)

GetAddressOutputs gets the spent and unspent outputs of an address.

func (*GoShimmerAPI) GetAddressUnspentOutputs added in v0.6.0

func (api *GoShimmerAPI) GetAddressUnspentOutputs(base58EncodedAddress string) (*jsonmodels.GetAddressResponse, error)

GetAddressUnspentOutputs gets the unspent outputs of an address.

func (*GoShimmerAPI) GetAllMana added in v0.5.0

func (api *GoShimmerAPI) GetAllMana() (*jsonmodels.GetAllManaResponse, error)

GetAllMana returns the mana perception of the node in the network.

func (*GoShimmerAPI) GetAllowedManaPledgeNodeIDs added in v0.5.0

func (api *GoShimmerAPI) GetAllowedManaPledgeNodeIDs() (*jsonmodels.AllowedManaPledgeResponse, error)

GetAllowedManaPledgeNodeIDs returns the list of allowed mana pledge IDs.

func (*GoShimmerAPI) GetAutopeeringNeighbors added in v0.5.8

func (api *GoShimmerAPI) GetAutopeeringNeighbors(knownPeers bool) (*jsonmodels.GetNeighborsResponse, error)

GetAutopeeringNeighbors gets the chosen/accepted neighbors. If knownPeers is set, also all known peers to the node are returned additionally.

func (*GoShimmerAPI) GetBranch added in v0.6.0

func (api *GoShimmerAPI) GetBranch(base58EncodedBranchID string) (*jsonmodels.Branch, error)

GetBranch gets the branch information.

func (*GoShimmerAPI) GetBranchChildren added in v0.6.0

func (api *GoShimmerAPI) GetBranchChildren(base58EncodedBranchID string) (*jsonmodels.GetBranchChildrenResponse, error)

GetBranchChildren gets the children of a branch.

func (*GoShimmerAPI) GetBranchConflicts added in v0.6.0

func (api *GoShimmerAPI) GetBranchConflicts(base58EncodedBranchID string) (*jsonmodels.GetBranchConflictsResponse, error)

GetBranchConflicts gets the conflict branches of a branch.

func (*GoShimmerAPI) GetCommittee added in v0.2.0

func (api *GoShimmerAPI) GetCommittee() (*jsonmodels.CommitteeResponse, error)

GetCommittee gets the current committee.

func (*GoShimmerAPI) GetConsensusEventLogs added in v0.5.0

func (api *GoShimmerAPI) GetConsensusEventLogs(nodeIDs []string) (*jsonmodels.GetEventLogsResponse, error)

GetConsensusEventLogs returns the consensus event logs or the nodeIDs specified.

func (*GoShimmerAPI) GetDiagnosticsBranches added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsBranches() (*csv.Reader, error)

GetDiagnosticsBranches runs diagnostics over branches. Returns csv with the following fields:

ID,ConflictSet,IssuanceTime,SolidTime,OpinionFormedTime,Liked,MonotonicallyLiked,InclusionState,Finalized,
LazyBooked,TransactionLiked

func (*GoShimmerAPI) GetDiagnosticsDRNG added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsDRNG() (*csv.Reader, error)

GetDiagnosticsDRNG runs diagnostics for DRNG Returns csv with the following fields:

ID,IssuerID,IssuerPublicKey,IssuanceTime,ArrivalTime,SolidTime,ScheduledTime,BookedTime,OpinionFormedTime,
dRNGPayloadType,InstanceID,Round,PreviousSignature,Signature,DistributedPK

func (*GoShimmerAPI) GetDiagnosticsFirstWeakMessageReferences added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsFirstWeakMessageReferences() (*csv.Reader, error)

GetDiagnosticsFirstWeakMessageReferences runs diagnostics over weak references only. Returns CSV with the following fields:

ID IssuerID IssuerPublicKey IssuanceTime ArrivalTime SolidTime ScheduledTime BookedTime OpinionFormedTime
FinalizedTime StrongParents WeakParents StrongApprovers WeakApprovers BranchID InclusionState Scheduled Booked
Eligible Invalid Finalized Rank IsPastMarker PastMarkers PMHI PMLI FutureMarkers FMHI FMLI PayloadType TransactionID
PayloadOpinionFormed TimestampOpinionFormed MessageOpinionFormed MessageOpinionTriggered TimestampOpinion
TimestampLoK

func (*GoShimmerAPI) GetDiagnosticsInvalidBranches added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsInvalidBranches() (*csv.Reader, error)

GetDiagnosticsInvalidBranches runs diagnostics over invalid branches. Returns csv with the following fields:

ID,ConflictSet,IssuanceTime,SolidTime,OpinionFormedTime,Liked,MonotonicallyLiked,InclusionState,Finalized,
LazyBooked,TransactionLiked

func (*GoShimmerAPI) GetDiagnosticsLazyBookedBranches added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsLazyBookedBranches() (*csv.Reader, error)

GetDiagnosticsLazyBookedBranches runs diagnostics over lazy booked branches. Returns csv with the following fields:

ID,ConflictSet,IssuanceTime,SolidTime,OpinionFormedTime,Liked,MonotonicallyLiked,InclusionState,Finalized,
LazyBooked,TransactionLiked

func (*GoShimmerAPI) GetDiagnosticsMessages added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsMessages() (*csv.Reader, error)

GetDiagnosticsMessages runs full message diagnostics Returns CSV with the following fields:

ID IssuerID IssuerPublicKey IssuanceTime ArrivalTime SolidTime ScheduledTime BookedTime OpinionFormedTime
FinalizedTime StrongParents WeakParents StrongApprovers WeakApprovers BranchID InclusionState Scheduled ScheduledBypass Booked
Eligible Invalid Finalized Rank IsPastMarker PastMarkers PMHI PMLI FutureMarkers FMHI FMLI PayloadType TransactionID
PayloadOpinionFormed TimestampOpinionFormed MessageOpinionFormed MessageOpinionTriggered TimestampOpinion
TimestampLoK

func (*GoShimmerAPI) GetDiagnosticsMessagesByRank added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsMessagesByRank(rank uint64) (*csv.Reader, error)

GetDiagnosticsMessagesByRank run diagnostics for messages whose markers are equal or above a certain rank Returns CSV with the following fields:

ID IssuerID IssuerPublicKey IssuanceTime ArrivalTime SolidTime ScheduledTime BookedTime OpinionFormedTime
FinalizedTime StrongParents WeakParents StrongApprovers WeakApprovers BranchID InclusionState Scheduled Booked
Eligible Invalid Finalized Rank IsPastMarker PastMarkers PMHI PMLI FutureMarkers FMHI FMLI PayloadType TransactionID
PayloadOpinionFormed TimestampOpinionFormed MessageOpinionFormed MessageOpinionTriggered TimestampOpinion
TimestampLoK

func (*GoShimmerAPI) GetDiagnosticsStrongTips added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsStrongTips() (*csv.Reader, error)

GetDiagnosticsStrongTips runs diagnostics over strong tips Returns csv with the following fields:

tipType,ID,IssuerID,IssuerPublicKey,IssuanceTime,ArrivalTime,SolidTime,ScheduledTime,BookedTime,OpinionFormedTime,
FinalizedTime,StrongParents,WeakParents,StrongApprovers,WeakApprovers,BranchID,InclusionState,Scheduled,Booked,
Eligible,Invalid,Finalized,Rank,IsPastMarker,PastMarkers,PMHI,PMLI,FutureMarkers,FMHI,FMLI,PayloadType,TransactionID,
PayloadOpinionFormed,TimestampOpinionFormed,MessageOpinionFormed,MessageOpinionTriggered,TimestampOpinion,TimestampLoK

func (*GoShimmerAPI) GetDiagnosticsTips added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsTips() (*csv.Reader, error)

GetDiagnosticsTips runs diagnostics over tips Returns csv with the following fields:

tipType,ID,IssuerID,IssuerPublicKey,IssuanceTime,ArrivalTime,SolidTime,ScheduledTime,BookedTime,OpinionFormedTime,
FinalizedTime,StrongParents,WeakParents,StrongApprovers,WeakApprovers,BranchID,InclusionState,Scheduled,Booked,
Eligible,Invalid,Finalized,Rank,IsPastMarker,PastMarkers,PMHI,PMLI,FutureMarkers,FMHI,FMLI,PayloadType,TransactionID,
PayloadOpinionFormed,TimestampOpinionFormed,MessageOpinionFormed,MessageOpinionTriggered,TimestampOpinion,TimestampLoK

func (*GoShimmerAPI) GetDiagnosticsUtxoDag added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsUtxoDag() (*csv.Reader, error)

GetDiagnosticsUtxoDag runs diagnostics over utxo dag. Returns csv with the following fields:

ID,IssuanceTime,SolidTime,OpinionFormedTime,AccessManaPledgeID,ConsensusManaPledgeID,Inputs,Outputs,Attachments,
BranchID,BranchLiked,BranchMonotonicallyLiked,Conflicting,InclusionState,Finalized,LazyBooked,Liked,LoK,FCOB1Time,
FCOB2Time

func (*GoShimmerAPI) GetDiagnosticsWeakTips added in v0.7.0

func (api *GoShimmerAPI) GetDiagnosticsWeakTips() (*csv.Reader, error)

GetDiagnosticsWeakTips runs diagnostics over weak tips Returns csv with the following fields:

tipType,ID,IssuerID,IssuerPublicKey,IssuanceTime,ArrivalTime,SolidTime,ScheduledTime,BookedTime,OpinionFormedTime,
FinalizedTime,StrongParents,WeakParents,StrongApprovers,WeakApprovers,BranchID,InclusionState,Scheduled,Booked,
Eligible,Invalid,Finalized,Rank,IsPastMarker,PastMarkers,PMHI,PMLI,FutureMarkers,FMHI,FMLI,PayloadType,TransactionID,
PayloadOpinionFormed,TimestampOpinionFormed,MessageOpinionFormed,MessageOpinionTriggered,TimestampOpinion,TimestampLoK

func (*GoShimmerAPI) GetMana added in v0.5.0

func (api *GoShimmerAPI) GetMana(shortNodeID string) (*jsonmodels.GetManaResponse, error)

GetMana returns the access and consensus mana a node has based on its shortNodeID.

func (*GoShimmerAPI) GetManaFullNodeID added in v0.5.0

func (api *GoShimmerAPI) GetManaFullNodeID(fullNodeID string) (*jsonmodels.GetManaResponse, error)

GetManaFullNodeID returns the access and consensus mana of the node specified in the argument. Note, that for the node to understand which nodeID we are referring to, short node ID is not sufficient.

func (*GoShimmerAPI) GetManaPercentile added in v0.5.0

func (api *GoShimmerAPI) GetManaPercentile(fullNodeID string) (*jsonmodels.GetPercentileResponse, error)

GetManaPercentile returns the mana percentile for access and consensus mana of a node.

func (*GoShimmerAPI) GetManualPeers added in v0.6.4

func (api *GoShimmerAPI) GetManualPeers(opts ...manualpeering.GetPeersOption) (
	peers []*manualpeering.KnownPeer, err error)

GetManualPeers gets the list of connected neighbors from the manual peering layer.

func (*GoShimmerAPI) GetMessage added in v0.5.3

func (api *GoShimmerAPI) GetMessage(base58EncodedID string) (*jsonmodels.Message, error)

GetMessage is the handler for the /messages/:messageID endpoint.

func (*GoShimmerAPI) GetMessageMetadata added in v0.5.3

func (api *GoShimmerAPI) GetMessageMetadata(base58EncodedID string) (*jsonmodels.MessageMetadata, error)

GetMessageMetadata is the handler for the /messages/:messageID/metadata endpoint.

func (*GoShimmerAPI) GetNHighestAccessMana added in v0.5.0

func (api *GoShimmerAPI) GetNHighestAccessMana(n int) (*jsonmodels.GetNHighestResponse, error)

GetNHighestAccessMana returns the N highest access mana holders in the network, sorted in descending order.

func (*GoShimmerAPI) GetNHighestConsensusMana added in v0.5.0

func (api *GoShimmerAPI) GetNHighestConsensusMana(n int) (*jsonmodels.GetNHighestResponse, error)

GetNHighestConsensusMana returns the N highest consensus mana holders in the network, sorted in descending order.

func (*GoShimmerAPI) GetOnlineAccessMana added in v0.5.0

func (api *GoShimmerAPI) GetOnlineAccessMana() (*jsonmodels.GetOnlineResponse, error)

GetOnlineAccessMana returns the sorted list of online access mana of nodes.

func (*GoShimmerAPI) GetOnlineConsensusMana added in v0.5.0

func (api *GoShimmerAPI) GetOnlineConsensusMana() (*jsonmodels.GetOnlineResponse, error)

GetOnlineConsensusMana returns the sorted list of online consensus mana of nodes.

func (*GoShimmerAPI) GetOutput added in v0.6.0

func (api *GoShimmerAPI) GetOutput(base58EncodedOutputID string) (*jsonmodels.Output, error)

GetOutput gets the output corresponding to OutputID.

func (*GoShimmerAPI) GetOutputConsumers added in v0.6.0

func (api *GoShimmerAPI) GetOutputConsumers(base58EncodedOutputID string) (*jsonmodels.GetOutputConsumersResponse, error)

GetOutputConsumers gets the consumers of the output corresponding to OutputID.

func (*GoShimmerAPI) GetOutputMetadata added in v0.6.0

func (api *GoShimmerAPI) GetOutputMetadata(base58EncodedOutputID string) (*jsonmodels.OutputMetadata, error)

GetOutputMetadata gets the metadata of the output corresponding to OutputID.

func (*GoShimmerAPI) GetOwnMana added in v0.5.0

func (api *GoShimmerAPI) GetOwnMana() (*jsonmodels.GetManaResponse, error)

GetOwnMana returns the access and consensus mana of the node this api client is communicating with.

func (*GoShimmerAPI) GetPastConsensusManaVector added in v0.5.0

func (api *GoShimmerAPI) GetPastConsensusManaVector(t int64) (*jsonmodels.PastConsensusManaVectorResponse, error)

GetPastConsensusManaVector returns the consensus base mana vector of a time in the past.

func (*GoShimmerAPI) GetPastConsensusVectorMetadata added in v0.5.0

func (api *GoShimmerAPI) GetPastConsensusVectorMetadata() (*jsonmodels.PastConsensusVectorMetadataResponse, error)

GetPastConsensusVectorMetadata returns the consensus base mana vector metadata of a time in the past.

func (*GoShimmerAPI) GetPending added in v0.5.0

func (api *GoShimmerAPI) GetPending(outputID string) (*jsonmodels.PendingResponse, error)

GetPending returns the mana (bm2) that will be pledged by spending the output specified.

func (*GoShimmerAPI) GetRandomness added in v0.2.0

func (api *GoShimmerAPI) GetRandomness() (*jsonmodels.RandomnessResponse, error)

GetRandomness gets the current randomness.

func (*GoShimmerAPI) GetTransaction added in v0.6.0

func (api *GoShimmerAPI) GetTransaction(base58EncodedTransactionID string) (*jsonmodels.Transaction, error)

GetTransaction gets the transaction of the corresponding to TransactionID.

func (*GoShimmerAPI) GetTransactionAttachments added in v0.6.0

func (api *GoShimmerAPI) GetTransactionAttachments(base58EncodedTransactionID string) (*jsonmodels.GetTransactionAttachmentsResponse, error)

GetTransactionAttachments gets the attachments (messageIDs) of the transaction corresponding to TransactionID.

func (*GoShimmerAPI) GetTransactionConsensusMetadata added in v0.6.0

func (api *GoShimmerAPI) GetTransactionConsensusMetadata(base58EncodedTransactionID string) (*jsonmodels.TransactionConsensusMetadata, error)

GetTransactionConsensusMetadata gets the consensus metadata of the transaction corresponding to TransactionID.

func (*GoShimmerAPI) GetTransactionInclusionState added in v0.6.0

func (api *GoShimmerAPI) GetTransactionInclusionState(base58EncodedTransactionID string) (*jsonmodels.TransactionInclusionState, error)

GetTransactionInclusionState gets inclusion state of the transaction corresponding to TransactionID.

func (*GoShimmerAPI) GetTransactionMetadata added in v0.6.0

func (api *GoShimmerAPI) GetTransactionMetadata(base58EncodedTransactionID string) (*jsonmodels.TransactionMetadata, error)

GetTransactionMetadata gets metadata of the transaction corresponding to TransactionID.

func (*GoShimmerAPI) HealthCheck added in v0.7.3

func (api *GoShimmerAPI) HealthCheck() error

HealthCheck checks whether the node is running and healthy.

func (*GoShimmerAPI) Info added in v0.2.0

func (api *GoShimmerAPI) Info() (*jsonmodels.InfoResponse, error)

Info gets the info of the node.

func (*GoShimmerAPI) Missing added in v0.2.4

func (api *GoShimmerAPI) Missing() (*jsonmodels.MissingResponse, error)

Missing returns all the missing messages and their count.

func (*GoShimmerAPI) PastConeExist added in v0.2.2

func (api *GoShimmerAPI) PastConeExist(base58EncodedMessageID string) (*jsonmodels.PastconeResponse, error)

PastConeExist checks that all of the messages in the past cone of a message are existing on the node down to the genesis. Returns the number of messages in the past cone as well.

func (*GoShimmerAPI) PostAddressUnspentOutputs added in v0.6.0

func (api *GoShimmerAPI) PostAddressUnspentOutputs(base58EncodedAddresses []string) (*jsonmodels.PostAddressesUnspentOutputsResponse, error)

PostAddressUnspentOutputs gets the unspent outputs of several addresses.

func (*GoShimmerAPI) PostTransaction added in v0.6.0

func (api *GoShimmerAPI) PostTransaction(transactionBytes []byte) (*jsonmodels.PostTransactionResponse, error)

PostTransaction sends the transaction(bytes) to the Tangle and returns its transaction ID.

func (*GoShimmerAPI) RemoveManualPeers added in v0.5.8

func (api *GoShimmerAPI) RemoveManualPeers(keys []ed25519.PublicKey) error

RemoveManualPeers remove the provided list of peers from the manual peering layer.

func (*GoShimmerAPI) SendFaucetRequest added in v0.2.0

func (api *GoShimmerAPI) SendFaucetRequest(base58EncodedAddr string, powTarget int, pledgeIDs ...string) (*jsonmodels.FaucetResponse, error)

SendFaucetRequest requests funds from faucet nodes by sending a faucet request payload message.

func (*GoShimmerAPI) SendPayload added in v0.2.0

func (api *GoShimmerAPI) SendPayload(payload []byte) (string, error)

SendPayload send a message with the given payload.

func (*GoShimmerAPI) ToggleSpammer

func (api *GoShimmerAPI) ToggleSpammer(enable bool, mpm int) (*jsonmodels.SpammerResponse, error)

ToggleSpammer toggles the node internal spammer.

type Option added in v0.3.0

type Option func(*GoShimmerAPI)

Option is a function which sets the given option.

func WithBasicAuth added in v0.3.0

func WithBasicAuth(username, password string) Option

WithBasicAuth returns a new BasicAuth.

func WithHTTPClient added in v0.3.0

func WithHTTPClient(c http.Client) Option

WithHTTPClient sets the http Client.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto
y or Y : Canonical URL