mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-28 16:22:15 +00:00
linter: add metalinter to Makefile & apply some fixes
This commit is contained in:
parent
0418d32276
commit
fe66a683bc
38
Makefile
38
Makefile
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
GOTOOLS = \
|
GOTOOLS = \
|
||||||
github.com/Masterminds/glide \
|
github.com/Masterminds/glide \
|
||||||
github.com/jteeuwen/go-bindata/go-bindata
|
github.com/jteeuwen/go-bindata/go-bindata \
|
||||||
|
github.com/alecthomas/gometalinter
|
||||||
|
|
||||||
REPO:=github.com/tendermint/go-crypto
|
REPO:=github.com/tendermint/go-crypto
|
||||||
|
|
||||||
all: get_vendor_deps test
|
all: get_vendor_deps test
|
||||||
@ -31,3 +33,37 @@ codegen:
|
|||||||
@echo "--> regenerating all interface wrappers"
|
@echo "--> regenerating all interface wrappers"
|
||||||
@gen
|
@gen
|
||||||
@echo "Done!"
|
@echo "Done!"
|
||||||
|
|
||||||
|
metalinter: ensure_tools
|
||||||
|
@gometalinter --install
|
||||||
|
gometalinter --vendor --deadline=600s --enable-all --disable=lll ./...
|
||||||
|
|
||||||
|
metalinter_test: ensure_tools
|
||||||
|
@gometalinter --install
|
||||||
|
gometalinter --vendor --deadline=600s --disable-all \
|
||||||
|
--enable=aligncheck \
|
||||||
|
--enable=deadcode \
|
||||||
|
--enable=gas \
|
||||||
|
--enable=goconst \
|
||||||
|
--enable=gocyclo \
|
||||||
|
--enable=goimports \
|
||||||
|
--enable=gosimple \
|
||||||
|
--enable=gotype \
|
||||||
|
--enable=ineffassign \
|
||||||
|
--enable=interfacer \
|
||||||
|
--enable=megacheck \
|
||||||
|
--enable=misspell \
|
||||||
|
--enable=safesql \
|
||||||
|
--enable=staticcheck \
|
||||||
|
--enable=structcheck \
|
||||||
|
--enable=unconvert \
|
||||||
|
--enable=unused \
|
||||||
|
--enable=vetshadow \
|
||||||
|
--enable=vet \
|
||||||
|
--enable=unparam \
|
||||||
|
--enable=varcheck \
|
||||||
|
./...
|
||||||
|
|
||||||
|
#--enable=dupl \
|
||||||
|
#--enable=errcheck \
|
||||||
|
#--enable=golint \ <== comments on anything exported
|
||||||
|
6
_gen.go
6
_gen.go
@ -1,6 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "github.com/tendermint/go-wire/gen"
|
|
||||||
_ "github.com/clipperhouse/stringer"
|
|
||||||
)
|
|
2
armor.go
2
armor.go
@ -22,7 +22,7 @@ func EncodeArmor(blockType string, headers map[string]string, data []byte) strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
PanicSanity("Error encoding ascii armor: " + err.Error())
|
PanicSanity("Error encoding ascii armor: " + err.Error())
|
||||||
}
|
}
|
||||||
return string(buf.Bytes())
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecodeArmor(armorStr string) (blockType string, headers map[string]string, data []byte, err error) {
|
func DecodeArmor(armorStr string) (blockType string, headers map[string]string, data []byte, err error) {
|
||||||
|
@ -50,7 +50,7 @@ func (ih InvalidHashPrefixError) Error() string {
|
|||||||
type InvalidCostError int
|
type InvalidCostError int
|
||||||
|
|
||||||
func (ic InvalidCostError) Error() string {
|
func (ic InvalidCostError) Error() string {
|
||||||
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
|
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost)) // nolint: unconvert
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
1
hash.go
1
hash.go
@ -1,3 +1,4 @@
|
|||||||
|
// nolint: goimports
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"log"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -88,18 +87,6 @@ func ComputeTxId(rawTxHex string) string {
|
|||||||
|
|
||||||
// Private methods...
|
// Private methods...
|
||||||
|
|
||||||
func printKeyInfo(privKeyBytes []byte, pubKeyBytes []byte, chain []byte) {
|
|
||||||
if pubKeyBytes == nil {
|
|
||||||
pubKeyBytes = PubKeyBytesFromPrivKeyBytes(privKeyBytes, true)
|
|
||||||
}
|
|
||||||
addr := AddrFromPubKeyBytes(pubKeyBytes)
|
|
||||||
log.Println("\nprikey:\t%v\npubKeyBytes:\t%v\naddr:\t%v\nchain:\t%v",
|
|
||||||
HexEncode(privKeyBytes),
|
|
||||||
HexEncode(pubKeyBytes),
|
|
||||||
addr,
|
|
||||||
HexEncode(chain))
|
|
||||||
}
|
|
||||||
|
|
||||||
func DerivePrivateKeyForPath(privKeyBytes []byte, chain []byte, path string) []byte {
|
func DerivePrivateKeyForPath(privKeyBytes []byte, chain []byte, path string) []byte {
|
||||||
data := privKeyBytes
|
data := privKeyBytes
|
||||||
parts := strings.Split(path, "/")
|
parts := strings.Split(path, "/")
|
||||||
@ -144,7 +131,7 @@ func DerivePublicKeyForPath(pubKeyBytes []byte, chain []byte, path string) []byt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DerivePrivateKey(privKeyBytes []byte, chain []byte, i uint32, prime bool) ([]byte, []byte) {
|
func DerivePrivateKey(privKeyBytes []byte, chain []byte, i uint32, prime bool) ([]byte, []byte) {
|
||||||
data := []byte{}
|
data := []byte{} // nolint [ megacheck, deadcode ]
|
||||||
if prime {
|
if prime {
|
||||||
i = i | 0x80000000
|
i = i | 0x80000000
|
||||||
data = append([]byte{byte(0)}, privKeyBytes...)
|
data = append([]byte{byte(0)}, privKeyBytes...)
|
||||||
@ -177,7 +164,7 @@ func addPoints(a []byte, b []byte) []byte {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
sumX, sumY := btcec.S256().Add(ap.X, ap.Y, bp.X, bp.Y)
|
sumX, sumY := btcec.S256().Add(ap.X, ap.Y, bp.X, bp.Y)
|
||||||
sum := (*btcec.PublicKey)(&btcec.PublicKey{
|
sum := (*btcec.PublicKey)(&btcec.PublicKey{ // nolint: unconvert
|
||||||
Curve: btcec.S256(),
|
Curve: btcec.S256(),
|
||||||
X: sumX,
|
X: sumX,
|
||||||
Y: sumY,
|
Y: sumY,
|
||||||
@ -248,7 +235,7 @@ func WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string {
|
|||||||
|
|
||||||
func PubKeyBytesFromPrivKeyBytes(privKeyBytes []byte, compress bool) (pubKeyBytes []byte) {
|
func PubKeyBytesFromPrivKeyBytes(privKeyBytes []byte, compress bool) (pubKeyBytes []byte) {
|
||||||
x, y := btcec.S256().ScalarBaseMult(privKeyBytes)
|
x, y := btcec.S256().ScalarBaseMult(privKeyBytes)
|
||||||
pub := (*btcec.PublicKey)(&btcec.PublicKey{
|
pub := (*btcec.PublicKey)(&btcec.PublicKey{ // nolint: unconvert
|
||||||
Curve: btcec.S256(),
|
Curve: btcec.S256(),
|
||||||
X: x,
|
X: x,
|
||||||
Y: y,
|
Y: y,
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
|
// nolint: goimports
|
||||||
package hd
|
package hd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -15,11 +13,6 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tyler-smith/go-bip39"
|
"github.com/tyler-smith/go-bip39"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
|
||||||
"github.com/btcsuite/btcutil/hdkeychain"
|
|
||||||
"github.com/mndrix/btcutil"
|
|
||||||
"github.com/tyler-smith/go-bip32"
|
|
||||||
|
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,7 +26,7 @@ type addrData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: atom fundraiser address
|
// NOTE: atom fundraiser address
|
||||||
var hdPath string = "m/44'/118'/0'/0/0"
|
// var hdPath string = "m/44'/118'/0'/0/0"
|
||||||
var hdToAddrTable []addrData
|
var hdToAddrTable []addrData
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -109,13 +102,6 @@ func TestReverseBytes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ifExit(err error, n int) {
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(n, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func gocrypto(seed []byte) ([]byte, []byte, []byte) {
|
func gocrypto(seed []byte) ([]byte, []byte, []byte) {
|
||||||
|
|
||||||
_, priv, ch, _ := ComputeMastersFromSeed(string(seed))
|
_, priv, ch, _ := ComputeMastersFromSeed(string(seed))
|
||||||
@ -131,83 +117,6 @@ func gocrypto(seed []byte) ([]byte, []byte, []byte) {
|
|||||||
return HexDecode(priv), privBytes, pubBytes
|
return HexDecode(priv), privBytes, pubBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func btcsuite(seed []byte) ([]byte, []byte, []byte) {
|
|
||||||
fmt.Println("HD")
|
|
||||||
masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
|
|
||||||
if err != nil {
|
|
||||||
hmac := hmac.New(sha512.New, []byte("Bitcoin seed"))
|
|
||||||
hmac.Write([]byte(seed))
|
|
||||||
intermediary := hmac.Sum(nil)
|
|
||||||
|
|
||||||
curve := btcutil.Secp256k1()
|
|
||||||
curveParams := curve.Params()
|
|
||||||
|
|
||||||
// Split it into our key and chain code
|
|
||||||
keyBytes := intermediary[:32]
|
|
||||||
fmt.Printf("\t%X\n", keyBytes)
|
|
||||||
fmt.Printf("\t%X\n", curveParams.N.Bytes())
|
|
||||||
keyInt, _ := binary.ReadVarint(bytes.NewBuffer(keyBytes))
|
|
||||||
fmt.Printf("\t%d\n", keyInt)
|
|
||||||
}
|
|
||||||
fh := hdkeychain.HardenedKeyStart
|
|
||||||
k, err := masterKey.Child(uint32(fh + 44))
|
|
||||||
ifExit(err, 44)
|
|
||||||
k, err = k.Child(uint32(fh + 118))
|
|
||||||
ifExit(err, 118)
|
|
||||||
k, err = k.Child(uint32(fh + 0))
|
|
||||||
ifExit(err, 1)
|
|
||||||
k, err = k.Child(uint32(0))
|
|
||||||
ifExit(err, 2)
|
|
||||||
k, err = k.Child(uint32(0))
|
|
||||||
ifExit(err, 3)
|
|
||||||
ecpriv, err := k.ECPrivKey()
|
|
||||||
ifExit(err, 10)
|
|
||||||
ecpub, err := k.ECPubKey()
|
|
||||||
ifExit(err, 11)
|
|
||||||
|
|
||||||
priv := ecpriv.Serialize()
|
|
||||||
pub := ecpub.SerializeCompressed()
|
|
||||||
mkey, _ := masterKey.ECPrivKey()
|
|
||||||
return mkey.Serialize(), priv, pub
|
|
||||||
}
|
|
||||||
|
|
||||||
// return priv and pub
|
|
||||||
func tylerSmith(seed []byte) ([]byte, []byte, []byte) {
|
|
||||||
masterKey, err := bip32.NewMasterKey(seed)
|
|
||||||
if err != nil {
|
|
||||||
hmac := hmac.New(sha512.New, []byte("Bitcoin seed"))
|
|
||||||
hmac.Write([]byte(seed))
|
|
||||||
intermediary := hmac.Sum(nil)
|
|
||||||
|
|
||||||
curve := btcutil.Secp256k1()
|
|
||||||
curveParams := curve.Params()
|
|
||||||
|
|
||||||
// Split it into our key and chain code
|
|
||||||
keyBytes := intermediary[:32]
|
|
||||||
fmt.Printf("\t%X\n", keyBytes)
|
|
||||||
fmt.Printf("\t%X\n", curveParams.N.Bytes())
|
|
||||||
keyInt, _ := binary.ReadVarint(bytes.NewBuffer(keyBytes))
|
|
||||||
fmt.Printf("\t%d\n", keyInt)
|
|
||||||
|
|
||||||
}
|
|
||||||
ifExit(err, 0)
|
|
||||||
fh := bip32.FirstHardenedChild
|
|
||||||
k, err := masterKey.NewChildKey(fh + 44)
|
|
||||||
ifExit(err, 44)
|
|
||||||
k, err = k.NewChildKey(fh + 118)
|
|
||||||
ifExit(err, 118)
|
|
||||||
k, err = k.NewChildKey(fh + 0)
|
|
||||||
ifExit(err, 1)
|
|
||||||
k, err = k.NewChildKey(0)
|
|
||||||
ifExit(err, 2)
|
|
||||||
k, err = k.NewChildKey(0)
|
|
||||||
ifExit(err, 3)
|
|
||||||
|
|
||||||
priv := k.Key
|
|
||||||
pub := k.PublicKey().Key
|
|
||||||
return masterKey.Key, priv, pub
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmarks
|
// Benchmarks
|
||||||
|
|
||||||
var revBytesCases = [][]byte{
|
var revBytesCases = [][]byte{
|
||||||
@ -237,6 +146,6 @@ func BenchmarkReverseBytes(b *testing.B) {
|
|||||||
|
|
||||||
// sink is necessary to ensure if the compiler tries
|
// sink is necessary to ensure if the compiler tries
|
||||||
// to smart, that it won't optimize away the benchmarks.
|
// to smart, that it won't optimize away the benchmarks.
|
||||||
if sink != nil {
|
if sink != nil { // nolint: megacheck
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,13 @@ func New(coder Encoder, store keys.Storage, codec keys.Codec) Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// exists just to make sure we fulfill the Signer interface
|
// exists just to make sure we fulfill the Signer interface
|
||||||
|
// nolint [ megacheck, deadcode ]
|
||||||
func (s Manager) assertSigner() keys.Signer {
|
func (s Manager) assertSigner() keys.Signer {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// exists just to make sure we fulfill the Manager interface
|
// exists just to make sure we fulfill the Manager interface
|
||||||
|
// nolint [ megacheck, deadcode ]
|
||||||
func (s Manager) assertKeyManager() keys.Manager {
|
func (s Manager) assertKeyManager() keys.Manager {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ for key management, transaction signing, and query validation.
|
|||||||
Please read the README and godoc to see how to
|
Please read the README and godoc to see how to
|
||||||
configure the server for your application.
|
configure the server for your application.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// nolint: goimports
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -12,8 +14,8 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
data "github.com/tendermint/go-wire/data"
|
|
||||||
"github.com/tendermint/go-crypto/keys/server/types"
|
"github.com/tendermint/go-crypto/keys/server/types"
|
||||||
|
data "github.com/tendermint/go-wire/data"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -22,8 +22,8 @@ const (
|
|||||||
PrivExt = "tlc"
|
PrivExt = "tlc"
|
||||||
PubExt = "pub"
|
PubExt = "pub"
|
||||||
keyPerm = os.FileMode(0600)
|
keyPerm = os.FileMode(0600)
|
||||||
pubPerm = os.FileMode(0644)
|
// pubPerm = os.FileMode(0644)
|
||||||
dirPerm = os.FileMode(0700)
|
dirPerm = os.FileMode(0700)
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileStore struct {
|
type FileStore struct {
|
||||||
@ -43,6 +43,7 @@ func New(dir string) FileStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assertStorage just makes sure we implement the proper Storage interface
|
// assertStorage just makes sure we implement the proper Storage interface
|
||||||
|
// nolint [ megacheck, deadcode ]
|
||||||
func (s FileStore) assertStorage() keys.Storage {
|
func (s FileStore) assertStorage() keys.Storage {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ func New() MemStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assertStorage just makes sure we implement the Storage interface
|
// assertStorage just makes sure we implement the Storage interface
|
||||||
|
// nolint [ megacheck, deadcode ]
|
||||||
func (s MemStore) assertStorage() keys.Storage {
|
func (s MemStore) assertStorage() keys.Storage {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,8 @@ func TestCheckInvalidLists(t *testing.T) {
|
|||||||
w, err := codec.BytesToWords(data)
|
w, err := codec.BytesToWords(data)
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
assert.Nil(err, "%d: %+v", i, err)
|
assert.Nil(err, "%d: %+v", i, err)
|
||||||
b, err := codec.WordsToBytes(w)
|
b, err1 := codec.WordsToBytes(w)
|
||||||
assert.Nil(err, "%d: %+v", i, err)
|
assert.Nil(err1, "%d: %+v", i, err1)
|
||||||
assert.Equal(data, b)
|
assert.Equal(data, b)
|
||||||
} else {
|
} else {
|
||||||
assert.NotNil(err, "%d", i)
|
assert.NotNil(err, "%d", i)
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
// keys/wordlist/spanish.txt
|
// keys/wordlist/spanish.txt
|
||||||
// DO NOT EDIT!
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
// nolint: goimports
|
||||||
package wordlist
|
package wordlist
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -204,9 +205,9 @@ func AssetNames() []string {
|
|||||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
var _bindata = map[string]func() (*asset, error){
|
var _bindata = map[string]func() (*asset, error){
|
||||||
"keys/wordlist/chinese_simplified.txt": keysWordlistChinese_simplifiedTxt,
|
"keys/wordlist/chinese_simplified.txt": keysWordlistChinese_simplifiedTxt,
|
||||||
"keys/wordlist/english.txt": keysWordlistEnglishTxt,
|
"keys/wordlist/english.txt": keysWordlistEnglishTxt,
|
||||||
"keys/wordlist/japanese.txt": keysWordlistJapaneseTxt,
|
"keys/wordlist/japanese.txt": keysWordlistJapaneseTxt,
|
||||||
"keys/wordlist/spanish.txt": keysWordlistSpanishTxt,
|
"keys/wordlist/spanish.txt": keysWordlistSpanishTxt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetDir returns the file names below a certain
|
// AssetDir returns the file names below a certain
|
||||||
@ -248,13 +249,14 @@ type bintree struct {
|
|||||||
Func func() (*asset, error)
|
Func func() (*asset, error)
|
||||||
Children map[string]*bintree
|
Children map[string]*bintree
|
||||||
}
|
}
|
||||||
|
|
||||||
var _bintree = &bintree{nil, map[string]*bintree{
|
var _bintree = &bintree{nil, map[string]*bintree{
|
||||||
"keys": &bintree{nil, map[string]*bintree{
|
"keys": &bintree{nil, map[string]*bintree{
|
||||||
"wordlist": &bintree{nil, map[string]*bintree{
|
"wordlist": &bintree{nil, map[string]*bintree{
|
||||||
"chinese_simplified.txt": &bintree{keysWordlistChinese_simplifiedTxt, map[string]*bintree{}},
|
"chinese_simplified.txt": &bintree{keysWordlistChinese_simplifiedTxt, map[string]*bintree{}},
|
||||||
"english.txt": &bintree{keysWordlistEnglishTxt, map[string]*bintree{}},
|
"english.txt": &bintree{keysWordlistEnglishTxt, map[string]*bintree{}},
|
||||||
"japanese.txt": &bintree{keysWordlistJapaneseTxt, map[string]*bintree{}},
|
"japanese.txt": &bintree{keysWordlistJapaneseTxt, map[string]*bintree{}},
|
||||||
"spanish.txt": &bintree{keysWordlistSpanishTxt, map[string]*bintree{}},
|
"spanish.txt": &bintree{keysWordlistSpanishTxt, map[string]*bintree{}},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
@ -305,4 +307,3 @@ func _filePath(dir, name string) string {
|
|||||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user