No from* (#309)
* Remove fromJSON/fromU8a * Remove fromJSONx * Remove unused import
This commit is contained in:
committed by
Jaco Greeff
parent
ed66239d33
commit
221bfac019
@@ -41,18 +41,6 @@ export default class AccountId extends U8aFixed {
|
||||
return decodeAddress(value);
|
||||
}
|
||||
|
||||
fromJSON (input: any): AccountId {
|
||||
super.fromJSON(AccountId.decodeAccountId(input));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): AccountId {
|
||||
super.fromU8a(input);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
@@ -90,20 +90,6 @@ export default class AccountIndex extends U8a {
|
||||
}
|
||||
}
|
||||
|
||||
fromJSON (input: any): AccountIndex {
|
||||
super.fromJSON(AccountIndex.decodeAccountIndex(input));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): AccountIndex {
|
||||
const [offset, length] = AccountIndex.readLength(input);
|
||||
|
||||
super.fromU8a(input.subarray(offset, offset + length));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
@@ -69,20 +69,6 @@ export default class Address extends Base<AccountId | AccountIndex> {
|
||||
);
|
||||
}
|
||||
|
||||
fromJSON (input: any): Address {
|
||||
this.raw = Address.decodeAddress(input);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Address {
|
||||
this.raw = input[0] === 0xff
|
||||
? new AccountId().fromU8a(input.subarray(1))
|
||||
: new AccountIndex().fromU8a(input);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toHex (): string {
|
||||
return u8aToHex(this.toU8a());
|
||||
}
|
||||
|
||||
@@ -26,18 +26,6 @@ export default class Bool extends Base<boolean> {
|
||||
return 1;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Bool {
|
||||
this.raw = input === true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Bool {
|
||||
this.raw = input[0] === 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
return this.raw;
|
||||
}
|
||||
|
||||
@@ -50,14 +50,6 @@ export default class Bytes extends U8a {
|
||||
return this.length + Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Bytes {
|
||||
const [offset, length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
|
||||
|
||||
super.fromU8a(input.subarray(offset, offset + length.toNumber()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toU8a (isBare?: boolean): Uint8Array {
|
||||
return isBare
|
||||
? this.raw
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('Extrinsic', () => {
|
||||
Method.injectExtrinsics(extrinsics);
|
||||
});
|
||||
|
||||
it('decodes a non-signed properly via fromJSON', () => {
|
||||
it('decodes a non-signed properly via JSON', () => {
|
||||
const extrinsic = new Extrinsic('0x010200ea51b75b00000000');
|
||||
|
||||
expect(extrinsic.isSigned).toEqual(false);
|
||||
|
||||
@@ -111,20 +111,6 @@ export default class Extrinsic extends Struct {
|
||||
return length + Compact.encodeU8a(length, DEFAULT_LENGTH_BITS).length;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Extrinsic {
|
||||
super.fromU8a(hexToU8a(input));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Extrinsic {
|
||||
const [offset, length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
|
||||
|
||||
super.fromU8a(input.subarray(offset, offset + length.toNumber()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash: AnyU8a): Extrinsic {
|
||||
this.signature.addSignature(this.method, signerPair, nonce, blockHash);
|
||||
|
||||
|
||||
@@ -28,10 +28,4 @@ export default class ExtrinsicEra extends U8a {
|
||||
|
||||
return new Uint8Array([0]);
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): ExtrinsicEra {
|
||||
super.fromU8a(ExtrinsicEra.decodeExtrinsicEra(input));
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { AnyNumber, AnyU8a } from './types';
|
||||
|
||||
import isU8a from '@polkadot/util/is/u8a';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import u8aToU8a from '@polkadot/util/u8a/toU8a';
|
||||
|
||||
import Struct from './codec/Struct';
|
||||
import Address from './Address';
|
||||
@@ -122,23 +121,6 @@ export default class ExtrinsicSignature extends Struct {
|
||||
return this;
|
||||
}
|
||||
|
||||
fromJSON (input: any) {
|
||||
return this.fromU8a(u8aToU8a(input));
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): ExtrinsicSignature {
|
||||
const version = input[0];
|
||||
|
||||
if ((version & BIT_SIGNED) === BIT_SIGNED) {
|
||||
super.fromU8a(input.subarray(1));
|
||||
} else {
|
||||
// Just clear signature, leave others as-is
|
||||
this.raw.signature = new Signature();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toU8a (isBare?: boolean): Uint8Array {
|
||||
return u8aConcat(
|
||||
new Uint8Array([this.version]),
|
||||
|
||||
@@ -7,7 +7,6 @@ import { AnyNumber } from './types';
|
||||
import hexToU8a from '@polkadot/util/hex/toU8a';
|
||||
import isHex from '@polkadot/util/is/hex';
|
||||
import isU8a from '@polkadot/util/is/u8a';
|
||||
import toU8a from '@polkadot/util/u8a/toU8a';
|
||||
|
||||
import Base from './codec/Base';
|
||||
import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
||||
@@ -332,6 +331,8 @@ export default class RuntimeMetadata extends Struct {
|
||||
|
||||
static decodeMetadata (value: any): object | Uint8Array {
|
||||
if (isHex(value)) {
|
||||
// We receive this as an Array<number> in the JSON output from the Node.
|
||||
// Convert to u8a and use the U8a version to do the actual parsing.
|
||||
return RuntimeMetadata.decodeMetadata(hexToU8a(value));
|
||||
} else if (isU8a(value)) {
|
||||
// HACK 13 Oct 2018 - For current running BBQ nodes, Metadata is not properly
|
||||
@@ -350,34 +351,10 @@ export default class RuntimeMetadata extends Struct {
|
||||
return value;
|
||||
}
|
||||
|
||||
// We receive this as an Array<number> in the JSON output from the Node. Convert
|
||||
// to u8a and use the fromU8a to do the actual parsing
|
||||
fromJSON (input: Uint8Array | string | Array<number>): RuntimeMetadata {
|
||||
return this.fromU8a(
|
||||
toU8a(input)
|
||||
);
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): RuntimeMetadata {
|
||||
// HACK 13 Oct 2018 - For current running BBQ nodes, Metadata is not properly
|
||||
// encoded, it does not have a length prefix. For latest substrate master, it
|
||||
// is properly encoded. Here we pull the prefix, check it agianst the length -
|
||||
// if matches, then we have the length, otherwise we assume it is an older node
|
||||
// and use the whole buffer
|
||||
const [offset, length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
|
||||
|
||||
super.fromU8a(
|
||||
input.length === (offset + length.toNumber())
|
||||
? input.subarray(offset)
|
||||
: input
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// FIXME Currently toJSON creates a struct, so it is not a one-to-one mapping
|
||||
// with what is actually found on the RPC layer. This needs to be adjusted to
|
||||
// match fromJSON. (However for now, it is useful in debugging)
|
||||
// match the constructor with JSON. (However for now, it is useful in
|
||||
// debugging).
|
||||
toJSON (): any {
|
||||
return super.toJSON();
|
||||
}
|
||||
|
||||
@@ -48,20 +48,6 @@ export default class Moment extends Base<Date> {
|
||||
return BITLENGTH / 8;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Moment {
|
||||
this.raw = Moment.decodeMoment(input);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Moment {
|
||||
this.raw = Moment.decodeMoment(
|
||||
u8aToBn(input.subarray(0, this.byteLength()), true)
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
getTime (): number {
|
||||
return this.raw.getTime();
|
||||
}
|
||||
|
||||
@@ -14,14 +14,6 @@ export default class Null extends Base<null> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Null {
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Null {
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -53,20 +53,6 @@ export default class Text extends Base<string> {
|
||||
return this.length + Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Text {
|
||||
const [offset, length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
|
||||
|
||||
this.raw = u8aToUtf8(input.subarray(offset, offset + length.toNumber()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Text {
|
||||
this.raw = `${input || ''}`;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
@@ -27,18 +27,6 @@ export default class Type extends Text {
|
||||
return this._originalLength;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Type {
|
||||
super.fromJSON(input);
|
||||
|
||||
return this._cleanupTypes();
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Type {
|
||||
super.fromU8a(input);
|
||||
|
||||
return this._cleanupTypes();
|
||||
}
|
||||
|
||||
// Note Since we are mangling what we get in beyond recognition, we really should
|
||||
// not allow the re-encoding. Additionally, this is probably more of a decoder-only
|
||||
// helper, so treat it as such.
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('Base', () => {
|
||||
expect(new Base('foo-bar').raw).toEqual('foo-bar');
|
||||
});
|
||||
|
||||
['byteLength', 'fromJSON', 'fromU8a', 'toJSON', 'toString', 'toU8a'].forEach((method) => {
|
||||
['byteLength', 'toJSON', 'toString', 'toU8a'].forEach((method) => {
|
||||
it(`has abstract (?) impl for ${method}`, () => {
|
||||
expect(
|
||||
() => new Base()[method]()
|
||||
|
||||
@@ -15,7 +15,7 @@ export const l = logger('codec');
|
||||
// - Not convinced about abstratc - there are a number of extensions to this class that
|
||||
// don't provide some implementations (e.g. Length, good example and should not be
|
||||
// used-as-is). In those case it just relies in the throw in here.
|
||||
export default class Base <T = any> {
|
||||
export default class Base<T = any> {
|
||||
raw: T;
|
||||
|
||||
constructor (value?: any) {
|
||||
@@ -26,14 +26,6 @@ export default class Base <T = any> {
|
||||
throw new Error('Base::byteLength: unimplemented');
|
||||
}
|
||||
|
||||
fromJSON (input: any): Base <T> {
|
||||
throw new Error('Base::fromJSON: unimplemented');
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Base <T> {
|
||||
throw new Error('Base::fromU8a: unimplemented');
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
throw new Error('Base::toJSON: unimplemented');
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('Compact', () => {
|
||||
).toEqual(5);
|
||||
});
|
||||
|
||||
it('constructs properly via fromU8a', () => {
|
||||
it('constructs properly via U8a', () => {
|
||||
expect(
|
||||
new Compact(new Uint8Array([254, 255, 3, 0])).raw
|
||||
).toEqual(new BN(0xffff));
|
||||
|
||||
@@ -102,14 +102,6 @@ export default class Compact extends UInt {
|
||||
return this.toU8a().length;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): UInt {
|
||||
const [, value] = Compact.decodeU8a(input, this._bitLength);
|
||||
|
||||
this.raw = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toU8a (isBare?: boolean): Uint8Array {
|
||||
return Compact.encodeU8a(this.raw, this._bitLength);
|
||||
}
|
||||
|
||||
@@ -42,20 +42,6 @@ export default class Enum extends Base<number> {
|
||||
return 1;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Enum {
|
||||
// FIXME We potentially want to assert that the value is actually inside this._enum
|
||||
this.raw = input;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Enum {
|
||||
// FIXME We potentially want to assert that the value is actually inside this._enum
|
||||
this.raw = input[0];
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
return this.raw;
|
||||
}
|
||||
|
||||
@@ -90,32 +90,6 @@ export default class EnumType<T> extends Base<Base<T>> {
|
||||
return 1 + this.raw.byteLength();
|
||||
}
|
||||
|
||||
fromJSON (input: any = {}): EnumType<T> {
|
||||
// JSON comes in the form of { "<type (lowercased)>": "<value for type>" }, here we
|
||||
// additionally force to lower to ensure forward compat
|
||||
const key = Object.keys(input)[0];
|
||||
const lowerKey = key.toLowerCase();
|
||||
const index = this._indexes.find((value, index) =>
|
||||
this._Types[index].name.toLowerCase() === lowerKey
|
||||
);
|
||||
|
||||
if (isUndefined(index)) {
|
||||
throw new Error('Unable to reliably map input on JSON');
|
||||
}
|
||||
|
||||
this.setValue(index);
|
||||
this.raw.fromJSON(input[key]);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): EnumType<T> {
|
||||
this.setValue(input[0]);
|
||||
this.raw.fromU8a(input.subarray(1));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
setValue (index?: | EnumType<T> | number, value?: any): void {
|
||||
if (index instanceof EnumType) {
|
||||
this._index = index._index;
|
||||
|
||||
@@ -67,26 +67,6 @@ export default class Option<T> extends Base<Base<T>> {
|
||||
return 1 + childLength;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Option<T> {
|
||||
this._isEmpty = isNull(input) || isUndefined(input);
|
||||
|
||||
if (!this._isEmpty) {
|
||||
this.raw.fromJSON(input);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Option<T> {
|
||||
this._isEmpty = input[0] === 0;
|
||||
|
||||
if (!this._isEmpty) {
|
||||
this.raw.fromU8a(input.subarray(1));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
return this._isEmpty
|
||||
? undefined
|
||||
|
||||
@@ -7,7 +7,6 @@ import isHex from '@polkadot/util/is/hex';
|
||||
import isObject from '@polkadot/util/is/object';
|
||||
import isU8a from '@polkadot/util/is/u8a';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import toU8a from '@polkadot/util/u8a/toU8a';
|
||||
|
||||
import Base from './Base';
|
||||
import { Constructor } from '../types';
|
||||
@@ -125,36 +124,6 @@ export default class Struct<
|
||||
}, 0);
|
||||
}
|
||||
|
||||
fromJSON (input: any): Struct<S, T, V, E> {
|
||||
if (isHex(input) || isU8a(input)) {
|
||||
return this.fromU8a(toU8a(input));
|
||||
}
|
||||
|
||||
// null & undefined yields empty
|
||||
if (!input) {
|
||||
input = {};
|
||||
}
|
||||
|
||||
Object.keys(this.raw).forEach((key) => {
|
||||
const jsonKey = this._jsonMap.get(key as any) || key;
|
||||
|
||||
// @ts-ignore as above...
|
||||
this.raw[key].fromJSON(input[jsonKey]);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Struct<S, T, V, E> {
|
||||
Object.values(this.raw).reduce((offset, entry) => {
|
||||
entry.fromU8a(input.subarray(offset));
|
||||
|
||||
return offset + entry.byteLength();
|
||||
}, 0);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
get (index: number): Base {
|
||||
return this.values()[index];
|
||||
}
|
||||
|
||||
@@ -51,25 +51,6 @@ describe('Tuple', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
// FIXME Remove fromJSON
|
||||
it('fromJSON still works', () => {
|
||||
const test = new (Tuple.with({
|
||||
a: Text,
|
||||
b: U32,
|
||||
c: Text
|
||||
}))();
|
||||
|
||||
expect(
|
||||
test.fromJSON([
|
||||
'bazzing', 32
|
||||
]).toJSON()
|
||||
).toEqual([
|
||||
'bazzing',
|
||||
32,
|
||||
''
|
||||
]);
|
||||
});
|
||||
|
||||
it('creates properly via actual hex string', () => {
|
||||
const test = new (Tuple.with({
|
||||
blockNumber: BlockNumber,
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import hexToU8a from '@polkadot/util/hex/toU8a';
|
||||
import isHex from '@polkadot/util/is/hex';
|
||||
|
||||
import { AnyU8a, Constructor } from '../types';
|
||||
import Base from './Base';
|
||||
import Struct from './Struct';
|
||||
|
||||
// A Tuple defines an anonymous Object with key/values - where the values are Base<T> values.It
|
||||
// is a specialization of the Struct type where the toJSON/fromJSON operates on Array structures,
|
||||
// A Tuple defines an anonymous Object with key/values - where the values are Base<T> values. It
|
||||
// is a specialization of the Struct type where the toJSON operates on Array structures,
|
||||
// while the U8a encoding is handled in the same way as a Struct
|
||||
export default class Tuple<
|
||||
// S & T definitions maps to what we have in Struct (naming documented there)
|
||||
@@ -48,18 +45,6 @@ export default class Tuple<
|
||||
};
|
||||
}
|
||||
|
||||
fromJSON (input: any): Tuple<S, T, V> {
|
||||
if (isHex(input)) {
|
||||
return this.fromU8a(hexToU8a(input));
|
||||
}
|
||||
|
||||
Object.values(this.raw).forEach((entry, index) => {
|
||||
entry.fromJSON(input[index]);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
return Object.values(this.raw).map((entry) =>
|
||||
entry.toJSON()
|
||||
|
||||
@@ -11,7 +11,7 @@ import Base from './Base';
|
||||
|
||||
// A U8a. A basic wrapper around Uint8Array, with no frills and no fuss. It
|
||||
// wraps a Uint8Array. It does differ from other implementations wher it will
|
||||
// consume the full u8a as passed to it in fromU8a. As such it is meant to be
|
||||
// consume the full u8a as passed to it in U8a. As such it is meant to be
|
||||
// subclassed where the wrapper takes care of the actual lengths.
|
||||
export default class U8a extends Base<Uint8Array> {
|
||||
constructor (value: AnyU8a = new Uint8Array()) {
|
||||
@@ -38,18 +38,6 @@ export default class U8a extends Base<Uint8Array> {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
fromJSON (input: any): U8a {
|
||||
this.raw = toU8a(input);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): U8a {
|
||||
this.raw = input;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toHex (): string {
|
||||
return u8aToHex(this.raw);
|
||||
}
|
||||
|
||||
@@ -32,18 +32,4 @@ export default class U8aFixed extends U8a {
|
||||
byteLength (): number {
|
||||
return this._trimLength();
|
||||
}
|
||||
|
||||
fromJSON (input: any): U8aFixed {
|
||||
super.fromJSON(input);
|
||||
|
||||
this._trim();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): U8aFixed {
|
||||
super.fromU8a(input.subarray(0, this._trimLength()));
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,16 +58,6 @@ export default class UInt extends Base<BN> {
|
||||
return this._bitLength / 8;
|
||||
}
|
||||
|
||||
fromJSON (input: any): UInt {
|
||||
this.raw = UInt.decodeUInt(input, this._bitLength);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): UInt {
|
||||
return this.fromJSON(input);
|
||||
}
|
||||
|
||||
toHex (): string {
|
||||
return bnToHex(this.raw, this._bitLength);
|
||||
}
|
||||
|
||||
@@ -87,21 +87,6 @@ export default class Vector<
|
||||
return this.raw.forEach(fn);
|
||||
}
|
||||
|
||||
fromJSON (input: any): Vector<T> {
|
||||
// input could be null/undefined to indicate empty
|
||||
this.raw = (input || []).map((input: any) =>
|
||||
new this._Type().fromJSON(input)
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Vector<T> {
|
||||
this.raw = Vector.decode(this._Type, input);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
get (index: number): T {
|
||||
return this.raw[index];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user