share

package
v2.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 22, 2024 License: Apache-2.0 Imports: 15 Imported by: 54

README

Shares

See the celestia-app specs for shares.

Documentation

Overview

Package share is an encoding and decoding protocol that takes blobs, a struct containing arbitrary data based on a namespace and coverts them into a slice of shares, bytes 512 in length. This logic is used for constructing the original data square.

Index

Constants

View Source
const (
	// ShareSize is the size of a share in bytes.
	ShareSize = 512

	// ShareInfoBytes is the number of bytes reserved for information. The info
	// byte contains the share version and a sequence start idicator.
	ShareInfoBytes = 1

	// SequenceLenBytes is the number of bytes reserved for the sequence length
	// that is present in the first share of a sequence.
	SequenceLenBytes = 4

	// ShareVersionZero is the first share version format.
	ShareVersionZero = uint8(0)

	// ShareVersionOne is the second share version format.
	// It requires that a signer is included in the first share in the sequence.
	ShareVersionOne = uint8(1)

	// DefaultShareVersion is the defacto share version. Use this if you are
	// unsure of which version to use.
	DefaultShareVersion = ShareVersionZero

	// CompactShareReservedBytes is the number of bytes reserved for the location of
	// the first unit (transaction, ISR) in a compact share.
	// Deprecated: use ShareReservedBytes instead.
	CompactShareReservedBytes = ShareReservedBytes

	// ShareReservedBytes is the number of bytes reserved for the location of
	// the first unit (transaction, ISR) in a compact share.
	ShareReservedBytes = 4

	// FirstCompactShareContentSize is the number of bytes usable for data in
	// the first compact share of a sequence.
	FirstCompactShareContentSize = ShareSize - NamespaceSize - ShareInfoBytes - SequenceLenBytes - ShareReservedBytes

	// ContinuationCompactShareContentSize is the number of bytes usable for
	// data in a continuation compact share of a sequence.
	ContinuationCompactShareContentSize = ShareSize - NamespaceSize - ShareInfoBytes - ShareReservedBytes

	// FirstSparseShareContentSize is the number of bytes usable for data in the
	// first sparse share of a sequence.
	FirstSparseShareContentSize = ShareSize - NamespaceSize - ShareInfoBytes - SequenceLenBytes

	// ContinuationSparseShareContentSize is the number of bytes usable for data
	// in a continuation sparse share of a sequence.
	ContinuationSparseShareContentSize = ShareSize - NamespaceSize - ShareInfoBytes

	// MinSquareSize is the smallest original square width.
	MinSquareSize = 1

	// MinShareCount is the minimum number of shares allowed in the original
	// data square.
	MinShareCount = MinSquareSize * MinSquareSize

	// MaxShareVersion is the maximum value a share version can be.
	MaxShareVersion = 127

	// SignerSize is the size of the signer in bytes.
	SignerSize = 20
)
View Source
const (
	// NamespaceVersionSize is the size of a namespace version in bytes.
	NamespaceVersionSize = 1

	// VersionIndex is the index of the version in the namespace. This should
	// always be the first byte
	VersionIndex = 0

	// NamespaceIDSize is the size of a namespace ID in bytes.
	NamespaceIDSize = 28

	// NamespaceSize is the size of a namespace (version + ID) in bytes.
	NamespaceSize = NamespaceVersionSize + NamespaceIDSize

	// NamespaceVersionZero is the first namespace version.
	NamespaceVersionZero = uint8(0)

	// NamespaceVersionMax is the max namespace version.
	NamespaceVersionMax = math.MaxUint8

	// NamespaceVersionZeroPrefixSize is the number of `0` bytes that are prefixed to
	// namespace IDs for version 0.
	NamespaceVersionZeroPrefixSize = 18

	// NamespaceVersionZeroIDSize is the number of bytes available for
	// user-specified namespace ID in a namespace ID for version 0.
	NamespaceVersionZeroIDSize = NamespaceIDSize - NamespaceVersionZeroPrefixSize
)

Variables

View Source
var (
	// NamespaceVersionZeroPrefix is the prefix of a namespace ID for version 0.
	NamespaceVersionZeroPrefix = bytes.Repeat([]byte{0}, NamespaceVersionZeroPrefixSize)

	// TxNamespace is the namespace reserved for ordinary Cosmos SDK transactions.
	TxNamespace = primaryReservedNamespace(0x01)

	// IntermediateStateRootsNamespace is the namespace reserved for
	// intermediate state root data.
	IntermediateStateRootsNamespace = primaryReservedNamespace(0x02)

	// PayForBlobNamespace is the namespace reserved for PayForBlobs transactions.
	PayForBlobNamespace = primaryReservedNamespace(0x04)

	// PrimaryReservedPaddingNamespace is the namespace used for padding after all
	// primary reserved namespaces.
	PrimaryReservedPaddingNamespace = primaryReservedNamespace(0xFF)

	// MaxPrimaryReservedNamespace is the highest primary reserved namespace.
	// Namespaces lower than this are reserved for protocol use.
	MaxPrimaryReservedNamespace = primaryReservedNamespace(0xFF)

	// MinSecondaryReservedNamespace is the lowest secondary reserved namespace
	// reserved for protocol use. Namespaces higher than this are reserved for
	// protocol use.
	MinSecondaryReservedNamespace = secondaryReservedNamespace(0x00)

	// TailPaddingNamespace is the namespace reserved for tail padding. All data
	// with this namespace will be ignored.
	TailPaddingNamespace = secondaryReservedNamespace(0xFE)

	// ParitySharesNamespace is the namespace reserved for erasure coded data.
	ParitySharesNamespace = secondaryReservedNamespace(0xFF)

	// SupportedBlobNamespaceVersions is a list of namespace versions that can be specified by a user for blobs.
	SupportedBlobNamespaceVersions = []uint8{NamespaceVersionZero}
)
View Source
var SupportedShareVersions = []uint8{ShareVersionZero, ShareVersionOne}

SupportedShareVersions is a list of supported share versions.

Functions

func AvailableBytesFromCompactShares

func AvailableBytesFromCompactShares(n int) int

AvailableBytesFromCompactShares returns the maximum amount of bytes that could fit in `n` compact shares. Note that all compact shares are length prefixed. To account for this use `RawTxSize`.

func AvailableBytesFromSparseShares

func AvailableBytesFromSparseShares(n int) int

AvailableBytesFromSparseShares returns the maximum amount of bytes that could fit in `n` sparse shares

func CompactSharesNeeded

func CompactSharesNeeded(sequenceLen uint32) (sharesNeeded int)

CompactSharesNeeded returns the number of compact shares needed to store a sequence of length sequenceLen. The parameter sequenceLen is the number of bytes of transactions or intermediate state roots in a sequence.

func GetSigner

func GetSigner(share Share) []byte

GetSigner returns the signer of the share, if the share is not of type v1 and is not the first share in a sequence it returns nil

func MarshalDelimitedTx

func MarshalDelimitedTx(tx []byte) ([]byte, error)

MarshalDelimitedTx prefixes a transaction with the length of the transaction encoded as a varint.

func NewReservedBytes

func NewReservedBytes(byteIndex uint32) ([]byte, error)

NewReservedBytes returns a byte slice of length ShareReservedBytes that contains the byteIndex of the first unit that starts in a compact share.

func ParseReservedBytes

func ParseReservedBytes(reservedBytes []byte) (uint32, error)

ParseReservedBytes parses a byte slice of length ShareReservedBytes into a byteIndex.

func ParseTxs

func ParseTxs(shares []Share) ([][]byte, error)

ParseTxs collects all of the transactions from the shares provided

func RandomBlobNamespaceID

func RandomBlobNamespaceID() []byte

func RandomVerzionZeroID

func RandomVerzionZeroID() []byte

func SortBlobs

func SortBlobs(blobs []*Blob)

Sort sorts the blobs by their namespace.

func SparseSharesNeeded

func SparseSharesNeeded(sequenceLen uint32) (sharesNeeded int)

SparseSharesNeeded returns the number of shares needed to store a sequence of length sequenceLen.

func ToBytes

func ToBytes(shares []Share) (bytes [][]byte)

Types

type Blob

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

Blob (stands for binary large object) is a core type that represents data to be submitted to the Celestia network alongside an accompanying namespace and optional signer (for proving the signer of the blob)

func GenerateV0Blobs added in v2.1.0

func GenerateV0Blobs(sizes []int, sameNamespace bool) ([]*Blob, error)

GenerateV0Blobs is a test utility producing v0 share formatted blobs with the requested size and namespaces.

func NewBlob

func NewBlob(ns Namespace, data []byte, shareVersion uint8, signer []byte) (*Blob, error)

New creates a new coretypes.Blob from the provided data after performing basic stateless checks over it.

func NewBlobFromProto

func NewBlobFromProto(pb *v1.BlobProto) (*Blob, error)

NewBlobFromProto creates a new blob from the proto generated type

func NewV0Blob

func NewV0Blob(ns Namespace, data []byte) (*Blob, error)

NewV0Blob creates a new blob with share version 0

func NewV1Blob

func NewV1Blob(ns Namespace, data []byte, signer []byte) (*Blob, error)

NewV1Blob creates a new blob with share version 1

func ParseBlobs

func ParseBlobs(shares []Share) ([]*Blob, error)

ParseBlobs collects all blobs from the shares provided

func UnmarshalBlob

func UnmarshalBlob(blob []byte) (*Blob, error)

UnmarshalBlob unmarshals a blob from the proto encoded bytes

func (*Blob) Compare

func (b *Blob) Compare(other *Blob) int

Compare is used to order two blobs based on their namespace

func (*Blob) Data

func (b *Blob) Data() []byte

Data returns the data of the blob

func (*Blob) DataLen

func (b *Blob) DataLen() int

DataLen returns the length of the data of the blob

func (*Blob) IsEmpty

func (b *Blob) IsEmpty() bool

IsEmpty returns true if the blob is empty. This is an invalid construction that can only occur if using the nil value. We only check that the data is empty but this also implies that all other fields would have their zero value

func (*Blob) Marshal

func (b *Blob) Marshal() ([]byte, error)

Marshal marshals the blob to the proto encoded bytes

func (*Blob) MarshalJSON added in v2.1.0

func (b *Blob) MarshalJSON() ([]byte, error)

MarshalJSON converts blob's data to the json encoded bytes

func (*Blob) Namespace

func (b *Blob) Namespace() Namespace

Namespace returns the namespace of the blob

func (*Blob) ShareVersion

func (b *Blob) ShareVersion() uint8

ShareVersion returns the share version of the blob

func (*Blob) Signer

func (b *Blob) Signer() []byte

Signer returns the signer of the blob

func (*Blob) ToShares added in v2.1.0

func (b *Blob) ToShares() ([]Share, error)

ToShares converts blob's data back to shares.

func (*Blob) UnmarshalJSON added in v2.1.0

func (b *Blob) UnmarshalJSON(bb []byte) error

UnmarshalJSON converts json encoded data to the blob

type CompactShareCounter

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

func NewCompactShareCounter

func NewCompactShareCounter() *CompactShareCounter

NewCompactShareCounter creates a new instance of a counter which calculates the amount of compact shares a set of data will be split into.

func (*CompactShareCounter) Add

func (c *CompactShareCounter) Add(dataLen int) int

Add adds the length of the data to the counter and returns the amount of shares the counter has been increased by.

func (*CompactShareCounter) Remainder

func (c *CompactShareCounter) Remainder() int

func (*CompactShareCounter) Revert

func (c *CompactShareCounter) Revert()

Revert reverts the last Add operation. This can be called multiple times but only works the first time after an add operation.

func (*CompactShareCounter) Size

func (c *CompactShareCounter) Size() int

Size returns the amount of shares the compact share counter has counted.

type CompactShareSplitter

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

CompactShareSplitter will write raw data compactly across a progressively increasing set of shares. It is used to lazily split block data such as transactions or intermediate state roots into shares.

func NewCompactShareSplitter

func NewCompactShareSplitter(ns Namespace, shareVersion uint8) *CompactShareSplitter

NewCompactShareSplitter returns a CompactShareSplitter using the provided namespace and shareVersion.

func (*CompactShareSplitter) Count

func (css *CompactShareSplitter) Count() int

Count returns the number of shares that would be made if `Export` was invoked on this compact share splitter.

func (*CompactShareSplitter) Export

func (css *CompactShareSplitter) Export() ([]Share, error)

Export returns the underlying compact shares

func (*CompactShareSplitter) ShareRanges

func (css *CompactShareSplitter) ShareRanges(shareRangeOffset int) map[[sha256.Size]byte]Range

ShareRanges returns a map of share ranges to the corresponding tx keys. All share ranges in the map of shareRanges will be offset (i.e. incremented) by the shareRangeOffset provided. shareRangeOffset should be 0 for the first compact share sequence in the data square (transactions) but should be some non-zero number for subsequent compact share sequences (e.g. pfb txs).

func (*CompactShareSplitter) WriteTx

func (css *CompactShareSplitter) WriteTx(tx []byte) error

WriteTx adds the delimited data for the provided tx to the underlying compact share splitter.

type InfoByte

type InfoByte byte

InfoByte is a byte with the following structure: the first 7 bits are reserved for version information in big endian form (initially `0000000`). The last bit is a "sequence start indicator", that is `1` if this is the first share of a sequence and `0` if this is a continuation share.

func NewInfoByte

func NewInfoByte(version uint8, isSequenceStart bool) (InfoByte, error)

func ParseInfoByte

func ParseInfoByte(i byte) (InfoByte, error)

func (InfoByte) IsSequenceStart

func (i InfoByte) IsSequenceStart() bool

IsSequenceStart returns whether this share is the start of a sequence.

func (InfoByte) Version

func (i InfoByte) Version() uint8

Version returns the version encoded in this InfoByte. Version is expected to be between 0 and MaxShareVersion (inclusive).

type Namespace

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

func MustNewNamespace

func MustNewNamespace(version uint8, id []byte) Namespace

MustNewNamespace returns a new namespace with the provided version and id. It panics if the provided version or id are not supported.

func MustNewV0Namespace

func MustNewV0Namespace(subID []byte) Namespace

MustNewV0Namespace returns a new namespace with version 0 and the provided subID. This function panics if the provided subID would result in an invalid namespace.

func NewNamespace

func NewNamespace(version uint8, id []byte) (Namespace, error)

NewNamespace validates the provided version and id and returns a new namespace. This should be used for user specified namespaces.

func NewNamespaceFromBytes

func NewNamespaceFromBytes(bytes []byte) (Namespace, error)

NewNamespaceFromBytes returns a new namespace from the provided byte slice. This is for user specified namespaces.

func NewV0Namespace

func NewV0Namespace(subID []byte) (Namespace, error)

NewV0Namespace returns a new namespace with version 0 and the provided subID. subID must be <= 10 bytes. If subID is < 10 bytes, it will be left-padded with 0s to fill 10 bytes.

func RandomBlobNamespace

func RandomBlobNamespace() Namespace

func RandomNamespace

func RandomNamespace() Namespace

func (Namespace) AddInt added in v2.1.0

func (n Namespace) AddInt(val int) (Namespace, error)

AddInt adds arbitrary int value to namespace, treating namespace as big-endian implementation of int. It could be helpful for users to create adjacent namespaces.

func (Namespace) Bytes

func (n Namespace) Bytes() []byte

Bytes returns this namespace as a byte slice.

func (Namespace) Compare

func (n Namespace) Compare(n2 Namespace) int

func (Namespace) Equals

func (n Namespace) Equals(n2 Namespace) bool

func (Namespace) ID

func (n Namespace) ID() []byte

ID returns this namespace's ID

func (Namespace) IsEmpty

func (n Namespace) IsEmpty() bool

IsEmpty returns true if the namespace is empty

func (Namespace) IsGreaterOrEqualThan

func (n Namespace) IsGreaterOrEqualThan(n2 Namespace) bool

func (Namespace) IsGreaterThan

func (n Namespace) IsGreaterThan(n2 Namespace) bool

func (Namespace) IsLessOrEqualThan

func (n Namespace) IsLessOrEqualThan(n2 Namespace) bool

func (Namespace) IsLessThan

func (n Namespace) IsLessThan(n2 Namespace) bool

func (Namespace) IsParityShares

func (n Namespace) IsParityShares() bool

func (Namespace) IsPayForBlob

func (n Namespace) IsPayForBlob() bool

func (Namespace) IsPrimaryReserved

func (n Namespace) IsPrimaryReserved() bool

func (Namespace) IsPrimaryReservedPadding

func (n Namespace) IsPrimaryReservedPadding() bool

func (Namespace) IsReserved

func (n Namespace) IsReserved() bool

IsReserved returns true if the namespace is reserved for the Celestia state machine

func (Namespace) IsSecondaryReserved

func (n Namespace) IsSecondaryReserved() bool

func (Namespace) IsTailPadding

func (n Namespace) IsTailPadding() bool

func (Namespace) IsTx

func (n Namespace) IsTx() bool

func (Namespace) IsUsableNamespace

func (n Namespace) IsUsableNamespace() bool

IsUsableNamespace refers to the range of namespaces that are not reserved by the square protocol i.e. not parity shares or tail padding

func (Namespace) MarshalJSON added in v2.1.0

func (n Namespace) MarshalJSON() ([]byte, error)

MarshalJSON encodes namespace to the json encoded bytes.

func (Namespace) Repeat

func (n Namespace) Repeat(times int) []Namespace

func (Namespace) String added in v2.1.0

func (n Namespace) String() string

String stringifies the Namespace.

func (*Namespace) UnmarshalJSON added in v2.1.0

func (n *Namespace) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes json bytes to the namespace.

func (Namespace) ValidateForBlob added in v2.1.0

func (n Namespace) ValidateForBlob() error

ValidateForBlob verifies whether the Namespace is appropriate for blob data. A valid blob namespace must meet two conditions: it cannot be reserved for special purposes, and its version must be supported by the system. If either of these conditions is not met, an error is returned indicating the issue. This ensures that only valid namespaces are used when dealing with blob data.

func (Namespace) ValidateForData added in v2.1.0

func (n Namespace) ValidateForData() error

ValidateForData checks if the Namespace is of real/useful data.

func (Namespace) Version

func (n Namespace) Version() uint8

Version return this namespace's version

type Range

type Range struct {
	// Start is the index of the first share occupied by this range.
	Start int
	// End is the next index after the last share occupied by this range.
	End int
}

Range is an end exclusive set of share indexes.

func EmptyRange

func EmptyRange() Range

func GetShareRangeForNamespace

func GetShareRangeForNamespace(shares []Share, ns Namespace) Range

GetShareRangeForNamespace returns all shares that belong to a given namespace. It will return an empty range if the namespace could not be found. This assumes that the slice of shares are lexicographically sorted by namespace. Ranges here are always end exclusive.

func NewRange

func NewRange(start, end int) Range

func (*Range) Add

func (r *Range) Add(value int)

func (Range) IsEmpty

func (r Range) IsEmpty() bool

type Sequence

type Sequence struct {
	Namespace Namespace
	Shares    []Share
}

Sequence represents a contiguous sequence of shares that are part of the same namespace and blob. For compact shares, one share sequence exists per reserved namespace. For sparse shares, one share sequence exists per blob.

func ParseShares

func ParseShares(shares []Share, ignorePadding bool) ([]Sequence, error)

ParseShares parses the shares provided and returns a list of Sequences. If ignorePadding is true then the returned Sequences will not contain any padding sequences.

func (Sequence) RawData

func (s Sequence) RawData() (data []byte, err error)

RawData returns the raw share data of this share sequence. The raw data does not contain the namespace ID, info byte, sequence length, or reserved bytes.

func (Sequence) SequenceLen

func (s Sequence) SequenceLen() (uint32, error)

type Share

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

Share contains the raw share data (including namespace ID).

func FromBytes

func FromBytes(bytes [][]byte) (shares []Share, err error)

func NamespacePaddingShare

func NamespacePaddingShare(ns Namespace, shareVersion uint8) (Share, error)

NamespacePaddingShare returns a share that acts as padding. Namespace padding shares follow a blob so that the next blob may start at an index that conforms to blob share commitment rules. The ns and shareVersion parameters provided should be the namespace and shareVersion of the blob that precedes this padding in the data square.

func NamespacePaddingShares

func NamespacePaddingShares(ns Namespace, shareVersion uint8, n int) ([]Share, error)

NamespacePaddingShares returns n namespace padding shares.

func NewShare

func NewShare(data []byte) (*Share, error)

NewShare creates a new share from the raw data, validating it's size and versioning

func RandShares added in v2.1.0

func RandShares(total int) ([]Share, error)

RandShares generates total amount of shares and fills them with random data.

func RandSharesWithNamespace added in v2.1.0

func RandSharesWithNamespace(namespace Namespace, namespacedAmount, total int) ([]Share, error)

RandSharesWithNamespace is the same as RandShares, but sets the same namespace for all shares.

func ReservedPaddingShare

func ReservedPaddingShare() Share

ReservedPaddingShare returns a share that acts as padding. Reserved padding shares follow all significant shares in the reserved namespace so that the first blob can start at an index that conforms to non-interactive default rules.

func ReservedPaddingShares

func ReservedPaddingShares(n int) []Share

ReservedPaddingShares returns n reserved padding shares.

func TailPaddingShare

func TailPaddingShare() Share

TailPaddingShare is a share that is used to pad a data square to the desired square size. Tail padding shares follow the last blob share in the data square.

func TailPaddingShares

func TailPaddingShares(n int) []Share

TailPaddingShares returns n tail padding shares.

func (*Share) CheckVersionSupported

func (s *Share) CheckVersionSupported() error

CheckVersionSupported checks if the share version is supported

func (*Share) InfoByte

func (s *Share) InfoByte() InfoByte

InfoByte returns the byte after the namespace used for indicating versioning and whether the share is the first in it's sequence or a continuation

func (Share) IsCompactShare

func (s Share) IsCompactShare() bool

IsCompactShare returns true if this is a compact share.

func (*Share) IsPadding

func (s *Share) IsPadding() bool

IsPadding returns whether this *share is padding or not.

func (*Share) IsSequenceStart

func (s *Share) IsSequenceStart() bool

IsSequenceStart returns true if this is the first share in a sequence.

func (Share) MarshalJSON added in v2.1.0

func (s Share) MarshalJSON() ([]byte, error)

MarshalJSON encodes share to the json encoded bytes.

func (*Share) Namespace

func (s *Share) Namespace() Namespace

Namespace returns the shares namespace

func (*Share) RawData

func (s *Share) RawData() []byte

RawData returns the raw share data. The raw share data does not contain the namespace ID, info byte, sequence length and if they exist: the reserved bytes and signer.

func (*Share) RawDataUsingReserved

func (s *Share) RawDataUsingReserved() (rawData []byte, err error)

RawDataUsingReserved returns the raw share data while taking reserved bytes into account.

func (*Share) SequenceLen

func (s *Share) SequenceLen() uint32

SequenceLen returns the sequence length of this share. It returns 0 if this is a continuation share because then it doesn't contain a sequence length.

func (*Share) ToBytes

func (s *Share) ToBytes() []byte

ToBytes returns the underlying bytes of the share

func (*Share) UnmarshalJSON added in v2.1.0

func (s *Share) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes json bytes to the share.

func (*Share) Version

func (s *Share) Version() uint8

Version returns the version of the share

type SparseShareSplitter

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

SparseShareSplitter lazily splits blobs into shares that will eventually be included in a data square. It also has methods to help progressively count how many shares the blobs written take up.

func NewSparseShareSplitter

func NewSparseShareSplitter() *SparseShareSplitter

func (*SparseShareSplitter) Count

func (sss *SparseShareSplitter) Count() int

Count returns the current number of shares that will be made if exporting.

func (*SparseShareSplitter) Export

func (sss *SparseShareSplitter) Export() []Share

Export finalizes and returns the underlying shares.

func (*SparseShareSplitter) Write

func (sss *SparseShareSplitter) Write(blob *Blob) error

Write writes the provided blob to this sparse share splitter. It returns an error or nil if no error is encountered.

func (*SparseShareSplitter) WriteNamespacePaddingShares

func (sss *SparseShareSplitter) WriteNamespacePaddingShares(count int) error

WriteNamespacePaddingShares adds padding shares with the namespace of the last written share. This is useful to follow the non-interactive default rules. This function assumes that at least one share has already been written.

Jump to

Keyboard shortcuts

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