std

package
v0.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 3, 2024 License: Apache-2.0, UNKNOWN not legal advice Imports: 0 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrTxsLoadingAborted = errors.New("transaction loading aborted")
)
View Source
var Package = amino.RegisterPackage(amino.NewPackage(
	"github.com/gnolang/gno/tm2/pkg/std",
	"std",
	amino.GetCallersDirname(),
).WithDependencies().WithTypes(

	&BaseAccount{}, "BaseAccount",

	InternalError{}, "InternalError",
	TxDecodeError{}, "TxDecodeError",
	InvalidSequenceError{}, "InvalidSequenceError",
	UnauthorizedError{}, "UnauthorizedError",
	InsufficientFundsError{}, "InsufficientFundsError",
	UnknownRequestError{}, "UnknownRequestError",
	InvalidAddressError{}, "InvalidAddressError",
	UnknownAddressError{}, "UnknownAddressError",
	InvalidPubKeyError{}, "InvalidPubKeyError",
	InsufficientCoinsError{}, "InsufficientCoinsError",
	InvalidCoinsError{}, "InvalidCoinsError",
	InvalidGasWantedError{}, "InvalidGasWantedError",
	OutOfGasError{}, "OutOfGasError",
	MemoTooLargeError{}, "MemoTooLargeError",
	InsufficientFeeError{}, "InsufficientFeeError",
	TooManySignaturesError{}, "TooManySignaturesError",
	NoSignaturesError{}, "NoSignaturesError",
	GasOverflowError{}, "GasOverflowError",
))

Functions

func CountSubKeys

func CountSubKeys(pub crypto.PubKey) int

CountSubKeys counts the total number of keys for a multi-sig public key.

func ErrGasOverflow

func ErrGasOverflow(msg string) error

func ErrInsufficientCoins

func ErrInsufficientCoins(msg string) error

func ErrInsufficientFee

func ErrInsufficientFee(msg string) error

func ErrInsufficientFunds

func ErrInsufficientFunds(msg string) error

func ErrInternal

func ErrInternal(msg string) error

func ErrInvalidAddress

func ErrInvalidAddress(msg string) error

func ErrInvalidCoins

func ErrInvalidCoins(msg string) error

func ErrInvalidGasWanted

func ErrInvalidGasWanted(msg string) error

func ErrInvalidPubKey

func ErrInvalidPubKey(msg string) error

func ErrInvalidSequence

func ErrInvalidSequence(msg string) error

func ErrMemoTooLarge

func ErrMemoTooLarge(msg string) error

func ErrNoSignatures

func ErrNoSignatures(msg string) error

func ErrOutOfGas

func ErrOutOfGas(msg string) error

func ErrTooManySignatures

func ErrTooManySignatures(msg string) error

func ErrTxDecode

func ErrTxDecode(msg string) error

func ErrUnauthorized

func ErrUnauthorized(msg string) error

func ErrUnknownAddress

func ErrUnknownAddress(msg string) error

func ErrUnknownRequest

func ErrUnknownRequest(msg string) error

func GetSignaturePayload

func GetSignaturePayload(s SignDoc) ([]byte, error)

GetSignaturePayload returns the sign payload for the SignDoc. The signature payload is generated by marshalling the SignDoc into Amino JSON, and then sorting the Amino JSON. Ultimately, the formula for signing is sign(sortJSON(aminoJSON(SignDoc)))

func MustSortJSON

func MustSortJSON(toSortJSON []byte) []byte

MustSortJSON is like sortJSON but panic if an error occurs, e.g., if the passed JSON isn't valid.

Types

type Account

type Account interface {
	GetAddress() crypto.Address
	SetAddress(crypto.Address) error // errors if already set.

	GetPubKey() crypto.PubKey // can return nil.
	SetPubKey(crypto.PubKey) error

	GetAccountNumber() uint64
	SetAccountNumber(uint64) error

	GetSequence() uint64
	SetSequence(uint64) error

	GetCoins() Coins
	SetCoins(Coins) error

	// Ensure that account implements stringer
	String() string
}

Account is an interface used to store coins at a given address within state. It presumes a notion of sequence numbers for replay protection, a notion of account numbers for replay protection for previously pruned accounts, and a pubkey for authentication purposes.

Many complex conditions can be used in the concrete struct which implements Account.

func ProtoBaseAccount

func ProtoBaseAccount() Account

ProtoBaseAccount - a prototype function for BaseAccount

type BaseAccount

type BaseAccount struct {
	Address       crypto.Address `json:"address" yaml:"address"`
	Coins         Coins          `json:"coins" yaml:"coins"`
	PubKey        crypto.PubKey  `json:"public_key" yaml:"public_key"`
	AccountNumber uint64         `json:"account_number" yaml:"account_number"`
	Sequence      uint64         `json:"sequence" yaml:"sequence"`
}

BaseAccount - a base account structure. This can be extended by embedding within in your *Account structure.

func NewBaseAccount

func NewBaseAccount(address crypto.Address, coins Coins,
	pubKey crypto.PubKey, accountNumber uint64, sequence uint64,
) *BaseAccount

NewBaseAccount creates a new BaseAccount object

func NewBaseAccountWithAddress

func NewBaseAccountWithAddress(addr crypto.Address) BaseAccount

NewBaseAccountWithAddress - returns a new base account with a given address

func (*BaseAccount) GetAccountNumber

func (acc *BaseAccount) GetAccountNumber() uint64

GetAccountNumber - Implements Account

func (BaseAccount) GetAddress

func (acc BaseAccount) GetAddress() crypto.Address

GetAddress - Implements Account.

func (*BaseAccount) GetCoins

func (acc *BaseAccount) GetCoins() Coins

GetCoins - Implements Account.

func (BaseAccount) GetPubKey

func (acc BaseAccount) GetPubKey() crypto.PubKey

GetPubKey - Implements Account.

func (*BaseAccount) GetSequence

func (acc *BaseAccount) GetSequence() uint64

GetSequence - Implements Account.

func (*BaseAccount) SetAccountNumber

func (acc *BaseAccount) SetAccountNumber(accNumber uint64) error

SetAccountNumber - Implements Account

func (*BaseAccount) SetAddress

func (acc *BaseAccount) SetAddress(addr crypto.Address) error

SetAddress - Implements Account.

func (*BaseAccount) SetCoins

func (acc *BaseAccount) SetCoins(coins Coins) error

SetCoins - Implements Account.

func (*BaseAccount) SetPubKey

func (acc *BaseAccount) SetPubKey(pubKey crypto.PubKey) error

SetPubKey - Implements Account.

func (*BaseAccount) SetSequence

func (acc *BaseAccount) SetSequence(seq uint64) error

SetSequence - Implements Account.

func (BaseAccount) String

func (acc BaseAccount) String() string

String implements fmt.Stringer

type Coin

type Coin struct {
	Denom  string `json:"denom"`
	Amount int64  `json:"amount"`
}

Coin hold some amount of one currency. A negative amount is invalid.

func MustParseCoin

func MustParseCoin(coinStr string) Coin

func NewCoin

func NewCoin(denom string, amount int64) Coin

NewCoin returns a new coin with a denomination and amount. It will panic if the amount is negative. To construct a negative (invalid) amount, use an operation.

func ParseCoin

func ParseCoin(coinStr string) (coin Coin, err error)

ParseCoin parses a cli input for one coin type, returning errors if invalid. This returns an error on an empty string as well.

func (Coin) Add

func (coin Coin) Add(coinB Coin) Coin

Adds amounts of two coins with same denom. If the coins differ in denom then it panics. An overflow or underflow panics. An invalid result panics.

func (Coin) AddUnsafe

func (coin Coin) AddUnsafe(coinB Coin) Coin

func (Coin) IsEqual

func (coin Coin) IsEqual(other Coin) bool

IsEqual returns true if the two sets of Coins have the same value

func (Coin) IsGTE

func (coin Coin) IsGTE(other Coin) bool

IsGTE returns true if they are the same type and the receiver is an equal or greater value

func (Coin) IsLT

func (coin Coin) IsLT(other Coin) bool

IsLT returns true if they are the same type and the receiver is a smaller value

func (Coin) IsNegative

func (coin Coin) IsNegative() bool

IsNegative returns true if the coin amount is negative and false otherwise.

func (Coin) IsPositive

func (coin Coin) IsPositive() bool

IsPositive returns true if coin amount is positive.

func (Coin) IsValid

func (coin Coin) IsValid() bool

IsValid returns true if the Coin has a non-negative amount and the denom is valid.

func (Coin) IsZero

func (coin Coin) IsZero() bool

IsZero returns if this represents no money

func (Coin) MarshalAmino

func (coin Coin) MarshalAmino() (string, error)

func (Coin) String

func (coin Coin) String() string

String provides a human-readable representation of a coin

func (Coin) Sub

func (coin Coin) Sub(coinB Coin) Coin

Subtracts amounts of two coins with same denom. If the coins differ in denom then it panics. An overflow or underflow panics. An invalid result panics.

func (Coin) SubUnsafe

func (coin Coin) SubUnsafe(coinB Coin) Coin

func (*Coin) UnmarshalAmino

func (coin *Coin) UnmarshalAmino(coinstr string) (err error)

type Coins

type Coins []Coin

Coins is a set of Coin, one per currency

func MustParseCoins

func MustParseCoins(coinsStr string) Coins

func NewCoins

func NewCoins(coins ...Coin) Coins

NewCoins constructs a new coin set.

func ParseCoins

func ParseCoins(coinsStr string) (Coins, error)

ParseCoins will parse out a list of coins separated by commas. If nothing is provided, it returns nil Coins. Returned coins are sorted.

func (Coins) Add

func (coins Coins) Add(coinsB Coins) Coins

Add adds two sets of coins.

e.g. {2A} + {A, 2B} = {3A, 2B} {2A} + {0B} = {2A}

NOTE: Add operates under the invariant that coins are sorted by denominations. Panics on invalid result.

func (Coins) AddUnsafe

func (coins Coins) AddUnsafe(coinsB Coins) Coins

AddUnsafe will perform addition of two coins sets. If both coin sets are empty, then an empty set is returned. If only a single set is empty, the other set is returned. Otherwise, the coins are compared in order of their denomination and addition only occurs when the denominations match, otherwise the coin is simply added to the sum assuming it's not zero.

func (Coins) AmountOf

func (coins Coins) AmountOf(denom string) int64

Returns the amount of a denom from coins, which may be negative.

func (Coins) DenomsSubsetOf

func (coins Coins) DenomsSubsetOf(coinsB Coins) bool

DenomsSubsetOf returns true if receiver's denom set is subset of coinsB's denoms.

func (Coins) Empty

func (coins Coins) Empty() bool

Empty returns true if there are no coins and false otherwise.

func (Coins) IsAllGT

func (coins Coins) IsAllGT(coinsB Coins) bool

IsAllGT returns true if for every denom in coinsB, the denom is present at a greater amount in coins.

func (Coins) IsAllGTE

func (coins Coins) IsAllGTE(coinsB Coins) bool

IsAllGTE returns false if for any denom in coinsB, the denom is present at a smaller amount in coins; else returns true.

func (Coins) IsAllLT

func (coins Coins) IsAllLT(coinsB Coins) bool

IsAllLT returns True iff for every denom in coins, the denom is present at a smaller amount in coinsB.

func (Coins) IsAllLTE

func (coins Coins) IsAllLTE(coinsB Coins) bool

IsAllLTE returns true iff for every denom in coins, the denom is present at a smaller or equal amount in coinsB.

func (Coins) IsAllPositive

func (coins Coins) IsAllPositive() bool

IsAllPositive returns true if there is at least one coin and all currencies have a positive value. NOTE: besides this function, which is zero sensitive, all other functions don't need to be called "IsAll*" -- TODO: rename back coins.IsAll* to coins.Is*?

func (Coins) IsAnyGT

func (coins Coins) IsAnyGT(coinsB Coins) bool

IsAnyGT returns true iff for any denom in coins, the denom is present at a greater amount in coinsB.

e.g. {2A, 3B}.IsAnyGT{A} = true {2A, 3B}.IsAnyGT{5C} = false {}.IsAnyGT{5C} = false {2A, 3B}.IsAnyGT{} = false

func (Coins) IsAnyGTE

func (coins Coins) IsAnyGTE(coinsB Coins) bool

IsAnyGTE returns true iff coins contains at least one denom that is present at a greater or equal amount in coinsB; it returns false otherwise.

NOTE: IsAnyGTE operates under the invariant that both coin sets are sorted by denominations and there exists no zero coins.

func (Coins) IsAnyNegative

func (coins Coins) IsAnyNegative() bool

IsAnyNegative returns true if there is at least one coin whose amount is negative; returns false otherwise. It returns false if the coin set is empty too.

TODO: Remove once unsigned integers are used.

func (Coins) IsEqual

func (coins Coins) IsEqual(coinsB Coins) bool

IsEqual returns true if the two sets of Coins have the same value

func (Coins) IsValid

func (coins Coins) IsValid() bool

IsValid asserts the Coins are sorted, have positive amount, and Denom does not contain upper case characters.

func (Coins) IsZero

func (coins Coins) IsZero() bool

IsZero returns true if there are no coins or all coins are zero.

func (Coins) Len

func (coins Coins) Len() int

func (Coins) Less

func (coins Coins) Less(i, j int) bool

func (Coins) MarshalAmino

func (coins Coins) MarshalAmino() (string, error)

func (Coins) Sort

func (coins Coins) Sort() Coins

Sort is a helper function to sort the set of coins inplace

func (Coins) String

func (coins Coins) String() string

func (Coins) Sub

func (coins Coins) Sub(coinsB Coins) Coins

Sub subtracts a set of coins from another.

e.g. {2A, 3B} - {A} = {A, 3B} {2A} - {0B} = {2A} {A, B} - {A} = {B}

Panics on invalid result.

func (Coins) SubUnsafe

func (coins Coins) SubUnsafe(coinsB Coins) Coins

SubUnsafe performs the same arithmetic as Sub but returns a boolean if any negative coin amount was returned.

func (Coins) Swap

func (coins Coins) Swap(i, j int)

func (*Coins) UnmarshalAmino

func (coins *Coins) UnmarshalAmino(coinsstr string) (err error)

type Fee

type Fee struct {
	GasWanted int64 `json:"gas_wanted" yaml:"gas_wanted"`
	GasFee    Coin  `json:"gas_fee" yaml:"gas_fee"`
}

Fee includes the amount of coins paid in fees and the maximum gas to be used by the transaction. The ratio yields an effective "gasprice", which must be above some miminum to be accepted into the mempool.

func NewFee

func NewFee(gasWanted int64, gasFee Coin) Fee

NewFee returns a new instance of Fee

func (Fee) Bytes

func (fee Fee) Bytes() []byte

Bytes for signing later

type GasOverflowError

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

func (GasOverflowError) AssertABCIError

func (GasOverflowError) AssertABCIError()

func (GasOverflowError) Error

func (e GasOverflowError) Error() string

type GasPrice

type GasPrice struct {
	Gas   int64 `json:"gas"`
	Price Coin  `json:"price"`
}

minimum gas price is Price/Gas per gas unit.

func ParseGasPrice

func ParseGasPrice(gasprice string) (GasPrice, error)

func ParseGasPrices

func ParseGasPrices(gasprices string) (res []GasPrice, err error)

type InsufficientCoinsError

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

func (InsufficientCoinsError) AssertABCIError

func (InsufficientCoinsError) AssertABCIError()

func (InsufficientCoinsError) Error

func (e InsufficientCoinsError) Error() string

type InsufficientFeeError

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

func (InsufficientFeeError) AssertABCIError

func (InsufficientFeeError) AssertABCIError()

func (InsufficientFeeError) Error

func (e InsufficientFeeError) Error() string

type InsufficientFundsError

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

func (InsufficientFundsError) AssertABCIError

func (InsufficientFundsError) AssertABCIError()

func (InsufficientFundsError) Error

func (e InsufficientFundsError) Error() string

type InternalError

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

declare all std errors. NOTE: these are meant to be used in conjunction with pkgs/errors.

func (InternalError) AssertABCIError

func (InternalError) AssertABCIError()

func (InternalError) Error

func (e InternalError) Error() string

type InvalidAddressError

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

func (InvalidAddressError) AssertABCIError

func (InvalidAddressError) AssertABCIError()

func (InvalidAddressError) Error

func (e InvalidAddressError) Error() string

type InvalidCoinsError

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

func (InvalidCoinsError) AssertABCIError

func (InvalidCoinsError) AssertABCIError()

func (InvalidCoinsError) Error

func (e InvalidCoinsError) Error() string

type InvalidGasWantedError

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

func (InvalidGasWantedError) AssertABCIError

func (InvalidGasWantedError) AssertABCIError()

func (InvalidGasWantedError) Error

func (e InvalidGasWantedError) Error() string

type InvalidPubKeyError

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

func (InvalidPubKeyError) AssertABCIError

func (InvalidPubKeyError) AssertABCIError()

func (InvalidPubKeyError) Error

func (e InvalidPubKeyError) Error() string

type InvalidSequenceError

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

func (InvalidSequenceError) AssertABCIError

func (InvalidSequenceError) AssertABCIError()

func (InvalidSequenceError) Error

func (e InvalidSequenceError) Error() string

type KI64Pair

type KI64Pair struct {
	Key   []byte
	Value int64
}

KI64Pair is a key-value struct for int64 value.

type KI64Pairs

type KI64Pairs []KI64Pair

KI64Pairs is a slice of KI64Pair.

func (KI64Pairs) Len

func (kvs KI64Pairs) Len() int

Len returns the length of kvs.

func (KI64Pairs) Less

func (kvs KI64Pairs) Less(i, j int) bool

Less reports whether kvs[i] should be ordered before kvs[j].

func (KI64Pairs) Sort

func (kvs KI64Pairs) Sort()

Sort sorts a kvs in ascending order.

func (KI64Pairs) Swap

func (kvs KI64Pairs) Swap(i, j int)

Swap swaps the elements with indexes, i and j.

type KVPair

type KVPair struct {
	Key   []byte
	Value []byte
}

KVPair is a key-value struct for []byte value.

type KVPairs

type KVPairs []KVPair

KVPairs is a slice of KVPair.

func (KVPairs) Len

func (kvs KVPairs) Len() int

Len returns the length of kvs.

func (KVPairs) Less

func (kvs KVPairs) Less(i, j int) bool

Less reports whether kvs[i] should be ordered before kvs[j].

func (KVPairs) Sort

func (kvs KVPairs) Sort()

Sort sorts a kvs in ascending order.

func (KVPairs) Swap

func (kvs KVPairs) Swap(i, j int)

Swap swaps the elements with indexes, i and j.

type MemoTooLargeError

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

func (MemoTooLargeError) AssertABCIError

func (MemoTooLargeError) AssertABCIError()

func (MemoTooLargeError) Error

func (e MemoTooLargeError) Error() string

type Msg

type Msg interface {
	// Return the message type.
	// Must be alphanumeric or empty.
	Route() string

	// Returns a human-readable string for the message, intended for utilization
	// within tags
	Type() string

	// ValidateBasic does a simple validation check that
	// doesn't require access to any other information.
	ValidateBasic() error

	// Get the canonical byte representation of the Msg.
	GetSignBytes() []byte

	// Signers returns the addrs of signers that must sign.
	// CONTRACT: All signatures must be present to be valid.
	// CONTRACT: Returns addrs in some deterministic order.
	GetSigners() []crypto.Address
}

Transactions messages must fulfill the Msg.

type NoSignaturesError

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

func (NoSignaturesError) AssertABCIError

func (NoSignaturesError) AssertABCIError()

func (NoSignaturesError) Error

func (e NoSignaturesError) Error() string

type OutOfGasError

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

func (OutOfGasError) AssertABCIError

func (OutOfGasError) AssertABCIError()

func (OutOfGasError) Error

func (e OutOfGasError) Error() string

type SignDoc

type SignDoc struct {
	ChainID       string `json:"chain_id" yaml:"chain_id"`
	AccountNumber uint64 `json:"account_number" yaml:"account_number"`
	Sequence      uint64 `json:"sequence" yaml:"sequence"`
	Fee           Fee    `json:"fee" yaml:"fee"`
	Msgs          []Msg  `json:"msgs" yaml:"msgs"`
	Memo          string `json:"memo" yaml:"memo"`
}

SignDoc is the standard object for transactions. AccountNumber is a replay-prevention field for the whole account (eg. nonce) to prevent the replay of txs after an account has been deleted (due to zero balance). Sequence is a replay-prevention field for each transaction given a nonce

type Signature

type Signature struct {
	PubKey    crypto.PubKey `json:"pub_key" yaml:"pub_key"` // optional
	Signature []byte        `json:"signature" yaml:"signature"`
}

Signature represents a wrapped signature of a transaction

type TooManySignaturesError

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

func (TooManySignaturesError) AssertABCIError

func (TooManySignaturesError) AssertABCIError()

func (TooManySignaturesError) Error

func (e TooManySignaturesError) Error() string

type Tx

type Tx struct {
	Msgs       []Msg       `json:"msg" yaml:"msg"`
	Fee        Fee         `json:"fee" yaml:"fee"`
	Signatures []Signature `json:"signatures" yaml:"signatures"`
	Memo       string      `json:"memo" yaml:"memo"`
}

Tx is a standard way to wrap a Msg with Fee and Signatures. NOTE: the first signature is the fee payer (Signatures must not be nil).

func NewTx

func NewTx(msgs []Msg, fee Fee, sigs []Signature, memo string) Tx

func ParseTxs

func ParseTxs(ctx context.Context, reader io.Reader) ([]Tx, error)

func (Tx) GetMemo

func (tx Tx) GetMemo() string

GetMemo returns the memo

func (Tx) GetMsgs

func (tx Tx) GetMsgs() []Msg

GetMsgs returns the all the transaction's messages.

func (Tx) GetSignBytes

func (tx Tx) GetSignBytes(chainID string, accountNumber uint64, sequence uint64) ([]byte, error)

func (Tx) GetSignatures

func (tx Tx) GetSignatures() []Signature

GetSignatures returns the signature of signers who signed the Msg. GetSignatures returns the signature of signers who signed the Msg. CONTRACT: Length returned is same as length of pubkeys returned from MsgKeySigners, and the order matches. CONTRACT: If the signature is missing (ie the Msg is invalid), then the corresponding signature is .Empty().

func (Tx) GetSigners

func (tx Tx) GetSigners() []crypto.Address

GetSigners returns the addresses that must sign the transaction. Addresses are returned in a deterministic order. They are accumulated from the GetSigners method for each Msg in the order they appear in tx.GetMsgs(). Duplicate addresses will be omitted.

func (Tx) ValidateBasic

func (tx Tx) ValidateBasic() error

ValidateBasic does a simple and lightweight validation check that doesn't require access to any other information.

type TxDecodeError

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

func (TxDecodeError) AssertABCIError

func (TxDecodeError) AssertABCIError()

func (TxDecodeError) Error

func (e TxDecodeError) Error() string

type UnauthorizedError

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

func (UnauthorizedError) AssertABCIError

func (UnauthorizedError) AssertABCIError()

func (UnauthorizedError) Error

func (e UnauthorizedError) Error() string

type UnknownAddressError

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

func (UnknownAddressError) AssertABCIError

func (UnknownAddressError) AssertABCIError()

func (UnknownAddressError) Error

func (e UnknownAddressError) Error() string

type UnknownRequestError

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

func (UnknownRequestError) AssertABCIError

func (UnknownRequestError) AssertABCIError()

func (UnknownRequestError) Error

func (e UnknownRequestError) Error() string

Jump to

Keyboard shortcuts

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