mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
parent
377d9aaaeb
commit
27fdbd9c88
2
.github/workflows/deploy.yaml
vendored
2
.github/workflows/deploy.yaml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
|
||||
- name: Build on Linux
|
||||
if: runner.os == 'Linux'
|
||||
|
2
.github/workflows/race.yaml
vendored
2
.github/workflows/race.yaml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
|
||||
- name: Set scheduled branch name
|
||||
shell: bash
|
||||
|
6
.github/workflows/tests.yaml
vendored
6
.github/workflows/tests.yaml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
|
||||
|
||||
# Source: https://github.com/actions/cache/blob/main/examples.md#go---modules
|
||||
@ -58,7 +58,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@ -86,7 +86,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.19
|
||||
|
||||
- name: Delete the stability tests from coverage
|
||||
run: rm -r stability-tests
|
||||
|
@ -6,7 +6,7 @@ supported kaspa messages to and from the appmessage. This package does not deal
|
||||
with the specifics of message handling such as what to do when a message is
|
||||
received. This provides the caller with a high level of flexibility.
|
||||
|
||||
Kaspa Message Overview
|
||||
# Kaspa Message Overview
|
||||
|
||||
The kaspa protocol consists of exchanging messages between peers. Each
|
||||
message is preceded by a header which identifies information about it such as
|
||||
@ -22,7 +22,7 @@ messages, all of the details of marshalling and unmarshalling to and from the
|
||||
appmessage using kaspa encoding are handled so the caller doesn't have to concern
|
||||
themselves with the specifics.
|
||||
|
||||
Message Interaction
|
||||
# Message Interaction
|
||||
|
||||
The following provides a quick summary of how the kaspa messages are intended
|
||||
to interact with one another. As stated above, these interactions are not
|
||||
@ -45,13 +45,13 @@ interactions in no particular order.
|
||||
notfound message (MsgNotFound)
|
||||
ping message (MsgPing) pong message (MsgPong)
|
||||
|
||||
Common Parameters
|
||||
# Common Parameters
|
||||
|
||||
There are several common parameters that arise when using this package to read
|
||||
and write kaspa messages. The following sections provide a quick overview of
|
||||
these parameters so the next sections can build on them.
|
||||
|
||||
Protocol Version
|
||||
# Protocol Version
|
||||
|
||||
The protocol version should be negotiated with the remote peer at a higher
|
||||
level than this package via the version (MsgVersion) message exchange, however,
|
||||
@ -60,7 +60,7 @@ latest protocol version this package supports and is typically the value to use
|
||||
for all outbound connections before a potentially lower protocol version is
|
||||
negotiated.
|
||||
|
||||
Kaspa Network
|
||||
# Kaspa Network
|
||||
|
||||
The kaspa network is a magic number which is used to identify the start of a
|
||||
message and which kaspa network the message applies to. This package provides
|
||||
@ -71,7 +71,7 @@ the following constants:
|
||||
appmessage.Simnet (Simulation test network)
|
||||
appmessage.Devnet (Development network)
|
||||
|
||||
Determining Message Type
|
||||
# Determining Message Type
|
||||
|
||||
As discussed in the kaspa message overview section, this package reads
|
||||
and writes kaspa messages using a generic interface named Message. In
|
||||
@ -89,7 +89,7 @@ switch or type assertion. An example of a type switch follows:
|
||||
fmt.Printf("Number of tx in block: %d", msg.Header.TxnCount)
|
||||
}
|
||||
|
||||
Reading Messages
|
||||
# Reading Messages
|
||||
|
||||
In order to unmarshall kaspa messages from the appmessage, use the ReadMessage
|
||||
function. It accepts any io.Reader, but typically this will be a net.Conn to
|
||||
@ -104,7 +104,7 @@ a remote node running a kaspa peer. Example syntax is:
|
||||
// Log and handle the error
|
||||
}
|
||||
|
||||
Writing Messages
|
||||
# Writing Messages
|
||||
|
||||
In order to marshall kaspa messages to the appmessage, use the WriteMessage
|
||||
function. It accepts any io.Writer, but typically this will be a net.Conn to
|
||||
@ -122,7 +122,7 @@ from a remote peer is:
|
||||
// Log and handle the error
|
||||
}
|
||||
|
||||
Errors
|
||||
# Errors
|
||||
|
||||
Errors returned by this package are either the raw errors provided by underlying
|
||||
calls to read/write from streams such as io.EOF, io.ErrUnexpectedEOF, and
|
||||
|
@ -132,7 +132,7 @@ func TestConvertToPartial(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//blockOne is the first block in the mainnet block DAG.
|
||||
// blockOne is the first block in the mainnet block DAG.
|
||||
var blockOne = MsgBlock{
|
||||
Header: MsgBlockHeader{
|
||||
Version: 0,
|
||||
|
@ -4,7 +4,7 @@ kaspactl is an RPC client for kaspad
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.18 or later.
|
||||
Go 1.19 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.18-alpine AS build
|
||||
FROM golang:1.19-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
|
@ -4,7 +4,7 @@ Kaspaminer is a CPU-based miner for kaspad
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.18 or later.
|
||||
Go 1.19 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.18-alpine AS build
|
||||
FROM golang:1.19-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
Package base58 provides an API for working with modified base58 and Base58Check
|
||||
encodings.
|
||||
|
||||
Modified Base58 Encoding
|
||||
# Modified Base58 Encoding
|
||||
|
||||
Standard base58 encoding is similar to standard base64 encoding except, as the
|
||||
name implies, it uses a 58 character alphabet which results in an alphanumeric
|
||||
@ -17,7 +17,7 @@ The modified base58 alphabet used by Bitcoin, and hence this package, omits the
|
||||
0, O, I, and l characters that look the same in many fonts and are therefore
|
||||
hard to humans to distinguish.
|
||||
|
||||
Base58Check Encoding Scheme
|
||||
# Base58Check Encoding Scheme
|
||||
|
||||
The Base58Check encoding scheme is primarily used for Bitcoin addresses at the
|
||||
time of this writing, however it can be used to generically encode arbitrary
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
)
|
||||
|
||||
//KaspawalletdUTXOsTolibkaspawalletUTXOs converts a []*pb.UtxosByAddressesEntry to a []*libkaspawallet.UTXO
|
||||
// KaspawalletdUTXOsTolibkaspawalletUTXOs converts a []*pb.UtxosByAddressesEntry to a []*libkaspawallet.UTXO
|
||||
func KaspawalletdUTXOsTolibkaspawalletUTXOs(kaspawalletdUtxoEntires []*pb.UtxosByAddressesEntry) ([]*UTXO, error) {
|
||||
UTXOs := make([]*UTXO, len(kaspawalletdUtxoEntires))
|
||||
for i, entry := range kaspawalletdUtxoEntires {
|
||||
|
@ -88,7 +88,7 @@ func SerializePartiallySignedTransaction(partiallySignedTransaction *PartiallySi
|
||||
return proto.Marshal(partiallySignedTransactionToProto(partiallySignedTransaction))
|
||||
}
|
||||
|
||||
//DeserializeDomainTransaction Deserialize a Transaction to an *externalapi.DomainTransaction
|
||||
// DeserializeDomainTransaction Deserialize a Transaction to an *externalapi.DomainTransaction
|
||||
func DeserializeDomainTransaction(serializedTransactionMessage []byte) (*externalapi.DomainTransaction, error) {
|
||||
protoTransactionMessage := &protoserialization.TransactionMessage{}
|
||||
err := proto.Unmarshal(serializedTransactionMessage, protoTransactionMessage)
|
||||
|
2
doc.go
2
doc.go
@ -13,9 +13,11 @@ the box' for most users. However, there are also a wide variety of flags that
|
||||
can be used to control it.
|
||||
|
||||
Usage:
|
||||
|
||||
kaspad [OPTIONS]
|
||||
|
||||
For an up-to-date help message:
|
||||
|
||||
kaspad --help
|
||||
|
||||
The long form of all option flags (except -C) can be specified in a configuration
|
||||
|
@ -1,5 +1,5 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.18-alpine AS build
|
||||
FROM golang:1.19-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
|
@ -10,9 +10,11 @@ package externalapi
|
||||
//
|
||||
// For example, assume a selected parent chain with IDs as depicted below, and the
|
||||
// stop block is genesis:
|
||||
//
|
||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||
//
|
||||
// The block locator for block 17 would be the hashes of blocks:
|
||||
//
|
||||
// [17 16 14 11 7 2 genesis]
|
||||
type BlockLocator []*DomainHash
|
||||
|
||||
|
@ -204,8 +204,8 @@ func (c *coinbaseManager) calcDeflationaryPeriodBlockSubsidy(blockDaaScore uint6
|
||||
}
|
||||
|
||||
/*
|
||||
This table was pre-calculated by calling `calcDeflationaryPeriodBlockSubsidyFloatCalc` for all months until reaching 0 subsidy.
|
||||
To regenerate this table, run `TestBuildSubsidyTable` in coinbasemanager_test.go (note the `deflationaryPhaseBaseSubsidy` therein)
|
||||
This table was pre-calculated by calling `calcDeflationaryPeriodBlockSubsidyFloatCalc` for all months until reaching 0 subsidy.
|
||||
To regenerate this table, run `TestBuildSubsidyTable` in coinbasemanager_test.go (note the `deflationaryPhaseBaseSubsidy` therein)
|
||||
*/
|
||||
var subsidyByDeflationaryMonthTable = []uint64{
|
||||
44000000000, 41530469757, 39199543598, 36999442271, 34922823143, 32962755691, 31112698372, 29366476791, 27718263097, 26162556530, 24694165062, 23308188075, 22000000000, 20765234878, 19599771799, 18499721135, 17461411571, 16481377845, 15556349186, 14683238395, 13859131548, 13081278265, 12347082531, 11654094037, 11000000000,
|
||||
|
@ -159,6 +159,7 @@ func TestDoubleSpends(t *testing.T) {
|
||||
// TestTransactionAcceptance checks that block transactions are accepted correctly when the merge set is sorted topologically.
|
||||
// DAG diagram:
|
||||
// genesis <- blockA <- blockB <- blockC <- ..(chain of k-blocks).. lastBlockInChain <- blockD <- blockE <- blockF <- blockG
|
||||
//
|
||||
// ^ ^ |
|
||||
// | redBlock <------------------------ blueChildOfRedBlock <-------------------------------
|
||||
func TestTransactionAcceptance(t *testing.T) {
|
||||
|
@ -30,7 +30,7 @@ func (bg *blockGHOSTDAGData) toModel() *externalapi.BlockGHOSTDAGData {
|
||||
//
|
||||
// 1) |anticone-of-candidate-block ∩ blue-set-of-newBlock| ≤ K
|
||||
//
|
||||
// 2) For every blue block in blue-set-of-newBlock:
|
||||
// 2. For every blue block in blue-set-of-newBlock:
|
||||
// |(anticone-of-blue-block ∩ blue-set-newBlock) ∪ {candidate-block}| ≤ K.
|
||||
// We validate this condition by maintaining a map BluesAnticoneSizes for
|
||||
// each block which holds all the blue anticone sizes that were affected by
|
||||
|
@ -12,11 +12,11 @@ import (
|
||||
// interval contains B's interval, it replaces it.
|
||||
//
|
||||
// Notes:
|
||||
// * Intervals never intersect unless one contains the other
|
||||
// - Intervals never intersect unless one contains the other
|
||||
// (this follows from the tree structure and the indexing rule).
|
||||
// * Since node.FutureCoveringSet is kept ordered, a binary search can be
|
||||
// - Since node.FutureCoveringSet is kept ordered, a binary search can be
|
||||
// used for insertion/queries.
|
||||
// * Although reindexing may change a block's interval, the
|
||||
// - Although reindexing may change a block's interval, the
|
||||
// is-superset relation will by definition
|
||||
// be always preserved.
|
||||
func (rt *reachabilityManager) insertToFutureCoveringSet(stagingArea *model.StagingArea, node, futureNode *externalapi.DomainHash) error {
|
||||
|
@ -161,7 +161,9 @@ func intervalSplitWithExponentialBias(ri *model.ReachabilityInterval, sizes []ui
|
||||
|
||||
// exponentialFractions returns a fraction of each size in sizes
|
||||
// as follows:
|
||||
//
|
||||
// fraction[i] = 2^size[i] / sum_j(2^size[j])
|
||||
//
|
||||
// In the code below the above equation is divided by 2^max(size)
|
||||
// to avoid exploding numbers. Note that in 1 / 2^(max(size)-size[i])
|
||||
// we divide 1 by potentially a very large number, which will
|
||||
|
@ -4,7 +4,7 @@ Package txscript implements the kaspa transaction script language.
|
||||
This package provides data structures and functions to parse and execute
|
||||
kaspa transaction scripts.
|
||||
|
||||
Script Overview
|
||||
# Script Overview
|
||||
|
||||
Kaspa transaction scripts are written in a stack-base, FORTH-like language.
|
||||
|
||||
@ -22,7 +22,7 @@ is used to prove the the spender is authorized to perform the transaction.
|
||||
One benefit of using a scripting language is added flexibility in specifying
|
||||
what conditions must be met in order to spend kaspa.
|
||||
|
||||
Errors
|
||||
# Errors
|
||||
|
||||
Errors returned by this package are of type txscript.Error. This allows the
|
||||
caller to programmatically determine the specific error by examining the
|
||||
|
@ -272,10 +272,10 @@ func (e ErrorCode) String() string {
|
||||
|
||||
// Error identifies a script-related error. It is used to indicate three
|
||||
// classes of errors:
|
||||
// 1) Script execution failures due to violating one of the many requirements
|
||||
// 1. Script execution failures due to violating one of the many requirements
|
||||
// imposed by the script engine or evaluating to false
|
||||
// 2) Improper API usage by callers
|
||||
// 3) Internal consistency check failures
|
||||
// 2. Improper API usage by callers
|
||||
// 3. Internal consistency check failures
|
||||
//
|
||||
// The caller can use type assertions on the returned errors to access the
|
||||
// ErrorCode field to ascertain the specific reason for the error. As an
|
||||
|
@ -37,6 +37,7 @@ func (e ErrScriptNotCanonical) Error() string {
|
||||
// For example, the following would build a 2-of-3 multisig script for usage in
|
||||
// a pay-to-script-hash (although in this situation MultiSigScript() would be a
|
||||
// better choice to generate the script):
|
||||
//
|
||||
// builder := txscript.NewScriptBuilder()
|
||||
// builder.AddOp(txscript.OP_2).AddData(pubKey1).AddData(pubKey2)
|
||||
// builder.AddData(pubKey3).AddOp(txscript.OP_3)
|
||||
|
@ -82,6 +82,7 @@ func checkMinimalDataEncoding(v []byte) error {
|
||||
// Bytes returns the number serialized as a little endian with a sign bit.
|
||||
//
|
||||
// Example encodings:
|
||||
//
|
||||
// 127 -> [0x7f]
|
||||
// -127 -> [0xff]
|
||||
// 128 -> [0x80 0x00]
|
||||
|
@ -76,13 +76,19 @@ func subtractionWithRemainderHavingDAAScoreInPlace(collection1, collection2, res
|
||||
// diffFrom follows a set of rules represented by the following 3 by 3 table:
|
||||
//
|
||||
// | | this | |
|
||||
//
|
||||
// ---------+-----------+-----------+-----------+-----------
|
||||
//
|
||||
// | | toAdd | toRemove | None
|
||||
//
|
||||
// ---------+-----------+-----------+-----------+-----------
|
||||
// other | toAdd | - | X | toAdd
|
||||
// ---------+-----------+-----------+-----------+-----------
|
||||
//
|
||||
// | toRemove | X | - | toRemove
|
||||
//
|
||||
// ---------+-----------+-----------+-----------+-----------
|
||||
//
|
||||
// | None | toRemove | toAdd | -
|
||||
//
|
||||
// Key:
|
||||
|
@ -3,9 +3,10 @@ Package dagconfig defines DAG configuration parameters.
|
||||
|
||||
In addition to the main Kaspa network, which is intended for the transfer
|
||||
of monetary value, there also exists the following standard networks:
|
||||
* testnet
|
||||
* simnet
|
||||
* devnet
|
||||
- testnet
|
||||
- simnet
|
||||
- devnet
|
||||
|
||||
These networks are incompatible with each other (each sharing a different
|
||||
genesis block) and software should handle errors where input intended for
|
||||
one network is used on an application instance running on a different
|
||||
|
@ -207,10 +207,10 @@ func DefaultConfig() *Config {
|
||||
// line options.
|
||||
//
|
||||
// The configuration proceeds as follows:
|
||||
// 1) Start with a default config with sane settings
|
||||
// 2) Pre-parse the command line to check for an alternative config file
|
||||
// 3) Load configuration file overwriting defaults with any specified options
|
||||
// 4) Parse CLI options and overwrite/add any specified options
|
||||
// 1. Start with a default config with sane settings
|
||||
// 2. Pre-parse the command line to check for an alternative config file
|
||||
// 3. Load configuration file overwriting defaults with any specified options
|
||||
// 4. Parse CLI options and overwrite/add any specified options
|
||||
//
|
||||
// The above results in kaspad functioning properly without any config settings
|
||||
// while still allowing the user to override settings with config files and
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
Package database provides a database for kaspad.
|
||||
|
||||
Overview
|
||||
# Overview
|
||||
|
||||
This package provides a database layer to store and retrieve data in a simple
|
||||
and efficient manner.
|
||||
@ -11,23 +11,23 @@ checksums in key areas to ensure data integrity.
|
||||
|
||||
Implementors of additional backends are required to implement the following interfaces:
|
||||
|
||||
DataAccessor
|
||||
# DataAccessor
|
||||
|
||||
This defines the common interface by which data gets accessed in a generic kaspad
|
||||
database. Both the Database and the Transaction interfaces (see below) implement it.
|
||||
|
||||
Database
|
||||
# Database
|
||||
|
||||
This defines the interface of a database that can begin transactions and close itself.
|
||||
|
||||
Transaction
|
||||
# Transaction
|
||||
|
||||
This defines the interface of a generic kaspad database transaction.
|
||||
Note: transactions provide data consistency over the state of the database as it was
|
||||
when the transaction started. There is NO guarantee that if one puts data into the
|
||||
transaction then it will be available to get within the same transaction.
|
||||
|
||||
Cursor
|
||||
# Cursor
|
||||
|
||||
This iterates over database entries given some bucket.
|
||||
*/
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
Package addressmanager implements concurrency safe Kaspa address manager.
|
||||
|
||||
Address Manager Overview
|
||||
# Address Manager Overview
|
||||
|
||||
In order maintain the peer-to-peer Kaspa network, there needs to be a source
|
||||
of addresses to connect to as nodes come and go. The Kaspa protocol provides
|
||||
|
@ -4,7 +4,7 @@ ARG KASPAMINER_IMAGE
|
||||
FROM ${KASPAD_IMAGE} as kaspad
|
||||
FROM ${KASPAMINER_IMAGE} as kaspaminer
|
||||
|
||||
FROM golang:1.18-alpine
|
||||
FROM golang:1.19-alpine
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
|
@ -95,6 +95,7 @@ func appDir(goos, appName string, roaming bool) string {
|
||||
// (%LOCALAPPDATA%) that is used by default.
|
||||
//
|
||||
// Example results:
|
||||
//
|
||||
// dir := AppDir("myapp", false)
|
||||
// POSIX (Linux/BSD): ~/.myapp
|
||||
// Mac OS: $HOME/Library/Application Support/Myapp
|
||||
|
@ -22,9 +22,11 @@ var (
|
||||
// Like IEEE754 floating point, there are three basic components: the sign,
|
||||
// the exponent, and the mantissa. They are broken out as follows:
|
||||
//
|
||||
// * the most significant 8 bits represent the unsigned base 256 exponent
|
||||
// * bit 23 (the 24th bit) represents the sign bit
|
||||
// * the least significant 23 bits represent the mantissa
|
||||
// - the most significant 8 bits represent the unsigned base 256 exponent
|
||||
//
|
||||
// - bit 23 (the 24th bit) represents the sign bit
|
||||
//
|
||||
// - the least significant 23 bits represent the mantissa
|
||||
//
|
||||
// -------------------------------------------------
|
||||
// | Exponent | Sign | Mantissa |
|
||||
@ -33,6 +35,7 @@ var (
|
||||
// -------------------------------------------------
|
||||
//
|
||||
// The formula to calculate N is:
|
||||
//
|
||||
// N = (-1^sign) * mantissa * 256^(exponent-3)
|
||||
func CompactToBig(compact uint32) *big.Int {
|
||||
destination := big.NewInt(0)
|
||||
|
@ -1,21 +1,21 @@
|
||||
/*
|
||||
Package util provides kaspa-specific convenience functions and types.
|
||||
|
||||
Block Overview
|
||||
# Block Overview
|
||||
|
||||
A Block defines a kaspa block that provides easier and more efficient
|
||||
manipulation of raw blocks. It also memoizes hashes for the
|
||||
block and its transactions on their first access so subsequent accesses don't
|
||||
have to repeat the relatively expensive hashing operations.
|
||||
|
||||
Tx Overview
|
||||
# Tx Overview
|
||||
|
||||
A Tx defines a kaspa transaction that provides more efficient manipulation of
|
||||
raw transactions. It memoizes the hash for the transaction on its
|
||||
first access so subsequent accesses don't have to repeat the relatively
|
||||
expensive hashing operations.
|
||||
|
||||
Address Overview
|
||||
# Address Overview
|
||||
|
||||
The Address interface provides an abstraction for a kaspa address. While the
|
||||
most common type is a pay-to-pubkey, kaspa already supports others and
|
||||
|
@ -28,6 +28,7 @@ func (t Time) UnixSeconds() int64 {
|
||||
}
|
||||
|
||||
// String returns the time formatted using the format string
|
||||
//
|
||||
// "2006-01-02 15:04:05.999999999 -0700 MST"
|
||||
func (t Time) String() string {
|
||||
return t.time.String()
|
||||
|
Loading…
x
Reference in New Issue
Block a user