mirror of
https://github.com/planetmint/planetmint-go.git
synced 2025-03-30 15:08:28 +00:00
feat(log): force to log error object
Signed-off-by: Julian Strobl <jmastr@mailbox.org>
This commit is contained in:
parent
94706c8f99
commit
ff51c2c6d1
@ -31,19 +31,19 @@ func GetValidatorCometBFTIdentity(ctx sdk.Context, rootDir string) (validatorIde
|
||||
|
||||
jsonFile, err := os.Open(jsonFilePath)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, "error while opening config", err.Error())
|
||||
GetAppLogger().Error(ctx, err, "error while opening config: %v", jsonFilePath)
|
||||
return
|
||||
}
|
||||
jsonBytes, err := io.ReadAll(jsonFile)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, "error while reading file", err.Error())
|
||||
GetAppLogger().Error(ctx, err, "error while reading file: %v", jsonFile)
|
||||
return
|
||||
}
|
||||
|
||||
var keyFile KeyFile
|
||||
err = json.Unmarshal(jsonBytes, &keyFile)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, "error while unmarshaling key file", err.Error())
|
||||
GetAppLogger().Error(ctx, err, "error while unmarshaling key file")
|
||||
return
|
||||
}
|
||||
validatorIdentity = strings.ToLower(keyFile.Address)
|
||||
@ -53,7 +53,7 @@ func GetValidatorCometBFTIdentity(ctx sdk.Context, rootDir string) (validatorIde
|
||||
func IsValidatorBlockProposer(ctx sdk.Context, rootDir string) (result bool) {
|
||||
validatorIdentity, err := GetValidatorCometBFTIdentity(ctx, rootDir)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, errormsg.CouldNotGetValidatorIdentity+": "+err.Error())
|
||||
GetAppLogger().Error(ctx, err, errormsg.CouldNotGetValidatorIdentity)
|
||||
return
|
||||
}
|
||||
hexProposerAddress := hex.EncodeToString(ctx.BlockHeader().ProposerAddress)
|
||||
|
@ -22,18 +22,18 @@ func buildSignBroadcastTx(goCtx context.Context, loggingContext string, sendingV
|
||||
addr := sdk.MustAccAddressFromBech32(sendingValidatorAddress)
|
||||
txJSON, err := lib.BuildUnsignedTx(addr, msg)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, loggingContext+" build unsigned tx failed: "+err.Error())
|
||||
GetAppLogger().Error(ctx, err, loggingContext+" build unsigned tx failed for: %v, %v", addr, msg)
|
||||
return
|
||||
}
|
||||
GetAppLogger().Debug(ctx, loggingContext+" unsigned tx: "+txJSON)
|
||||
out, err := lib.BroadcastTxWithFileLock(addr, msg)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, loggingContext+" broadcast tx failed: "+err.Error())
|
||||
GetAppLogger().Error(ctx, err, loggingContext+" broadcast tx failed: %v, %v", addr, msg)
|
||||
return
|
||||
}
|
||||
txResponse, err := lib.GetTxResponseFromOut(out)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, loggingContext+" getting tx response from out failed: "+err.Error())
|
||||
GetAppLogger().Error(ctx, err, loggingContext+" getting tx response from out failed: %v", out)
|
||||
return
|
||||
}
|
||||
if txResponse.Code == 0 {
|
||||
@ -42,10 +42,10 @@ func buildSignBroadcastTx(goCtx context.Context, loggingContext string, sendingV
|
||||
}
|
||||
txResponseJSON, err := yaml.YAMLToJSON([]byte(txResponse.String()))
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, loggingContext+" converting tx response from yaml to json failed: "+err.Error())
|
||||
GetAppLogger().Error(ctx, err, loggingContext+" converting tx response from yaml to json failed: %v", txResponse)
|
||||
return
|
||||
}
|
||||
GetAppLogger().Error(ctx, loggingContext+" broadcast tx failed: "+string(txResponseJSON))
|
||||
GetAppLogger().Info(ctx, loggingContext+" broadcast tx failed: "+string(txResponseJSON))
|
||||
}()
|
||||
}
|
||||
|
||||
|
@ -69,8 +69,8 @@ func (logger *AppLogger) Debug(ctx sdk.Context, msg string, keyvals ...interface
|
||||
ctx.Logger().Debug(globalApplicationLoggerTag + msg)
|
||||
}
|
||||
|
||||
func (logger *AppLogger) Error(ctx sdk.Context, msg string, keyvals ...interface{}) {
|
||||
func (logger *AppLogger) Error(ctx sdk.Context, err error, msg string, keyvals ...interface{}) {
|
||||
msg = format(msg, keyvals...)
|
||||
logger.testingLog(globalApplicationLoggerTag + msg)
|
||||
ctx.Logger().Error(globalApplicationLoggerTag + msg)
|
||||
logger.testingLog(globalApplicationLoggerTag + msg + ": " + err.Error())
|
||||
ctx.Logger().Error(globalApplicationLoggerTag + msg + ": " + err.Error())
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ func IssueMachineNFT(goCtx context.Context, machine *types.Machine, scheme strin
|
||||
notarizedAsset.Registered = true
|
||||
assetID, contract, hex, err := clients.IssueNFTAsset(goCtx, machine.Name, machine.Address, domain)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, err.Error())
|
||||
GetAppLogger().Error(ctx, err, "")
|
||||
return err
|
||||
}
|
||||
assetRegistryEndpoint := fmt.Sprintf("%s://%s/%s", scheme, domain, path)
|
||||
@ -43,7 +43,7 @@ func IssueMachineNFT(goCtx context.Context, machine *types.Machine, scheme strin
|
||||
GetAppLogger().Info(ctx, "Liquid Token Issuance assetID: "+assetID+" contract: "+contract+" tx: "+hex)
|
||||
err = RegisterAsset(goCtx, assetID, contract, assetRegistryEndpoint)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, err.Error())
|
||||
GetAppLogger().Error(ctx, err, "")
|
||||
notarizedAsset.Registered = false
|
||||
}
|
||||
// issue message with:
|
||||
|
@ -71,7 +71,7 @@ func SendMqttPopInitMessagesToServer(ctx sdk.Context, challenge types.Challenge)
|
||||
}
|
||||
err := sendMqttPopInitMessages(challenge)
|
||||
if err != nil {
|
||||
GetAppLogger().Error(ctx, "MQTT error: "+err.Error())
|
||||
GetAppLogger().Error(ctx, err, "MQTT")
|
||||
return
|
||||
}
|
||||
GetAppLogger().Info(ctx, "MQTT message successfully sent: "+challenge.String())
|
||||
|
@ -26,7 +26,7 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
|
||||
// select PoP participants
|
||||
challenger, challengee, err := monitor.SelectPoPParticipantsOutOfActiveActors()
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "error during PoP Participant selection ", err)
|
||||
util.GetAppLogger().Error(ctx, err, "error during PoP Participant selection")
|
||||
}
|
||||
if err != nil || challenger == "" || challengee == "" {
|
||||
challenger = ""
|
||||
@ -44,14 +44,14 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
|
||||
util.SendInitReissuance(ctx, hexProposerAddress, reissuance.GetCommand(), currentBlockHeight,
|
||||
reissuance.GetFirstIncludedPop(), reissuance.GetLastIncludedPop())
|
||||
} else {
|
||||
util.GetAppLogger().Error(ctx, "error while computing the RDDL reissuance ", err)
|
||||
util.GetAppLogger().Error(ctx, err, "error while computing the RDDL reissuance")
|
||||
}
|
||||
}
|
||||
|
||||
if isDistributionHeight(ctx, k, currentBlockHeight) {
|
||||
distribution, err := k.GetDistributionForReissuedTokens(ctx, currentBlockHeight)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "error while computing the RDDL distribution ", err)
|
||||
util.GetAppLogger().Error(ctx, err, "error while computing the RDDL distribution")
|
||||
}
|
||||
distribution.Proposer = hexProposerAddress
|
||||
util.SendDistributionRequest(ctx, distribution)
|
||||
|
@ -46,7 +46,7 @@ func (k Keeper) getChallengeRangeFromStore(ctx sdk.Context, iterator db.Iterator
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var challenge types.Challenge
|
||||
if err := challenge.Unmarshal(iterator.Value()); err != nil {
|
||||
util.GetAppLogger().Error(ctx, "unable to unmarshal challenge "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, "unable to unmarshal challenge")
|
||||
return nil, err // or continue TODO make decision
|
||||
}
|
||||
val = append(val, challenge)
|
||||
@ -62,7 +62,7 @@ func (k Keeper) GetChallenges(ctx sdk.Context) (challenges []types.Challenge, er
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var event types.Challenge
|
||||
if err = event.Unmarshal(iterator.Value()); err != nil {
|
||||
util.GetAppLogger().Error(ctx, "unable to unmarshal challenge "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, "unable to unmarshal challenge")
|
||||
return nil, err // or continue TODO make decision
|
||||
}
|
||||
challenges = append(challenges, event)
|
||||
|
@ -51,7 +51,7 @@ func (k Keeper) ComputeDistribution(ctx sdk.Context, lastReissuance int64, block
|
||||
// PoP rewards subtracted from DaoAmount and added to PoPAmount for later distribution
|
||||
validatorPoPRewards, err := k.accumulateValidatorPoPRewardsForDistribution(ctx, lastReissuance, blockHeight)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "error calculating Validator PoP rewards from height %v to %v", lastReissuance, blockHeight)
|
||||
util.GetAppLogger().Error(ctx, err, "calculating Validator PoP rewards from height %v to %v", lastReissuance, blockHeight)
|
||||
}
|
||||
|
||||
distribution.DaoAmount = util.UintValueToRDDLTokenString(uint64(float64(amount)*types.PercentageDao) - validatorPoPRewards)
|
||||
|
@ -32,7 +32,7 @@ func (k msgServer) DistributionRequest(goCtx context.Context, msg *types.MsgDist
|
||||
|
||||
validatorIdentity, err := util.GetValidatorCometBFTIdentity(ctx, k.RootDir)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, distributionRequestTag+errormsg.CouldNotGetValidatorIdentity+": "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, distributionRequestTag+errormsg.CouldNotGetValidatorIdentity)
|
||||
return nil, err
|
||||
}
|
||||
if msg.Distribution.GetProposer() != validatorIdentity {
|
||||
@ -45,23 +45,23 @@ func (k msgServer) DistributionRequest(goCtx context.Context, msg *types.MsgDist
|
||||
// issue 5 distributions:
|
||||
earlyInvestorTx, err := clients.SendTokens(goCtx, msg.Distribution.EarlyInvAddr, msg.Distribution.EarlyInvAmount, reissuanceAsset)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, distributionRequestTag+"could not distribute asset to early investors: "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, distributionRequestTag+"could not distribute asset to early investors")
|
||||
}
|
||||
investorTx, err := clients.SendTokens(goCtx, msg.Distribution.InvestorAddr, msg.Distribution.InvestorAmount, reissuanceAsset)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, distributionRequestTag+"could not distribute asset to investors: "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, distributionRequestTag+"could not distribute asset to investors")
|
||||
}
|
||||
strategicTx, err := clients.SendTokens(goCtx, msg.Distribution.StrategicAddr, msg.Distribution.StrategicAmount, reissuanceAsset)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, distributionRequestTag+"could not distribute asset to strategic investments: "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, distributionRequestTag+"could not distribute asset to strategic investments")
|
||||
}
|
||||
popTx, err := clients.SendTokens(goCtx, msg.Distribution.PopAddr, msg.Distribution.PopAmount, reissuanceAsset)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, distributionRequestTag+"could not distribute asset to PoP: "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, distributionRequestTag+"could not distribute asset to PoP")
|
||||
}
|
||||
daoTx, err := clients.SendTokens(goCtx, msg.Distribution.DaoAddr, msg.Distribution.DaoAmount, reissuanceAsset)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, distributionRequestTag+"could not distribute asset to DAO: "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, distributionRequestTag+"could not distribute asset to DAO")
|
||||
}
|
||||
|
||||
util.SendDistributionResult(goCtx, msg.Distribution.LastPop, daoTx, investorTx, popTx, earlyInvestorTx, strategicTx)
|
||||
|
@ -2,6 +2,8 @@ package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
@ -22,9 +24,9 @@ func (k msgServer) DistributionResult(goCtx context.Context, msg *types.MsgDistr
|
||||
|
||||
distribution, found := k.LookupDistributionOrder(ctx, msg.GetLastPop())
|
||||
if !found {
|
||||
errorMessage := types.ErrDistributionNotFound.Error() + " for provided block height " + strconv.FormatInt(msg.GetLastPop(), 10)
|
||||
util.GetAppLogger().Error(ctx, errorMessage)
|
||||
return nil, errorsmod.Wrap(types.ErrDistributionNotFound, errorMessage)
|
||||
err := errors.New(types.ErrDistributionNotFound.Error() + " for provided block height " + strconv.FormatInt(msg.GetLastPop(), 10))
|
||||
util.GetAppLogger().Error(ctx, err, "")
|
||||
return nil, errorsmod.Wrap(types.ErrDistributionNotFound, err.Error())
|
||||
}
|
||||
|
||||
distribution.DaoTxID = msg.DaoTxID
|
||||
@ -34,12 +36,12 @@ func (k msgServer) DistributionResult(goCtx context.Context, msg *types.MsgDistr
|
||||
distribution.StrategicTxID = msg.StrategicTxID
|
||||
|
||||
if err := k.clearUnresolvedClaims(ctx, distribution.FirstPop); err != nil {
|
||||
util.GetAppLogger().Error(ctx, "error while clearing unresolved claims for heights %d-%d: %v", distribution.FirstPop, distribution.LastPop, err)
|
||||
util.GetAppLogger().Error(ctx, err, "error while clearing unresolved claims for heights %d-%d", distribution.FirstPop, distribution.LastPop)
|
||||
}
|
||||
|
||||
err := k.resolveStagedClaims(ctx, distribution.FirstPop, distribution.LastPop)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "%s for provided PoP heights: %d %d", types.ErrResolvingStagedClaims.Error(), distribution.FirstPop, distribution.LastPop)
|
||||
util.GetAppLogger().Error(ctx, err, "%s for provided PoP heights: %d %d", types.ErrResolvingStagedClaims.Error(), distribution.FirstPop, distribution.LastPop)
|
||||
return nil, errorsmod.Wrap(types.ErrConvertClaims, err.Error())
|
||||
}
|
||||
util.GetAppLogger().Info(ctx, "staged claims successfully for provided PoP heights: %d %d", distribution.FirstPop, distribution.LastPop)
|
||||
@ -123,11 +125,12 @@ func (k msgServer) getClaims(ctx sdk.Context, start int64, end int64) (claims Cl
|
||||
for _, challenge := range challenges {
|
||||
initiatorAddr, err := sdk.AccAddressFromBech32(challenge.Initiator)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "error converting initiator address")
|
||||
util.GetAppLogger().Error(ctx, err, "error converting initiator address: %v", challenge.Initiator)
|
||||
} else {
|
||||
validatorPopReward, found := k.getChallengeInitiatorReward(ctx, challenge.GetHeight())
|
||||
if !found {
|
||||
util.GetAppLogger().Error(ctx, "No PoP initiator reward found for height %v", challenge.GetHeight())
|
||||
err = fmt.Errorf("no PoP initiator reward found for height %v", challenge.GetHeight())
|
||||
util.GetAppLogger().Error(ctx, err, "")
|
||||
}
|
||||
claims.initiator[initiatorAddr.String()] += validatorPopReward
|
||||
}
|
||||
|
@ -29,17 +29,17 @@ func (k msgServer) InitPop(goCtx context.Context, msg *types.MsgInitPop) (*types
|
||||
// TODO: expand err value in log
|
||||
initiatorAddr, err := sdk.AccAddressFromBech32(msg.GetInitiator())
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "error converting initiator address: %v", err)
|
||||
util.GetAppLogger().Error(ctx, err, "error converting initiator address")
|
||||
}
|
||||
|
||||
valReward := sdk.NewCoins(sdk.NewCoin(k.GetParams(ctx).StagedDenom, sdk.NewIntFromUint64(amount)))
|
||||
err = k.bankKeeper.MintCoins(ctx, types.ModuleName, valReward)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "error minting initiator rewards: %v", err)
|
||||
util.GetAppLogger().Error(ctx, err, "error minting initiator rewards")
|
||||
}
|
||||
|
||||
if err := k.sendRewards(ctx, initiatorAddr.String(), amount); err != nil {
|
||||
util.GetAppLogger().Error(ctx, "failed to send rewards: %v", err)
|
||||
util.GetAppLogger().Error(ctx, err, "failed to send rewards")
|
||||
}
|
||||
|
||||
return &types.MsgInitPopResponse{}, nil
|
||||
|
@ -31,7 +31,7 @@ func (k msgServer) CreateRedeemClaim(goCtx context.Context, msg *types.MsgCreate
|
||||
|
||||
err := k.burnClaimAmount(ctx, sdk.MustAccAddressFromBech32(msg.Creator), sdk.NewCoins(burnCoins))
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, createRedeemClaimTag+"could not burn claim")
|
||||
util.GetAppLogger().Error(ctx, err, createRedeemClaimTag+"could not burn claim")
|
||||
}
|
||||
|
||||
id := k.CreateNewRedeemClaim(
|
||||
@ -131,7 +131,7 @@ func postClaimToService(goCtx context.Context, beneficiary string, amount uint64
|
||||
util.GetAppLogger().Info(ctx, fmt.Sprintf("Issuing RDDL claim: %s/%d", beneficiary, id))
|
||||
txID, err := clients.PostClaim(goCtx, beneficiary, amount, id)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, createRedeemClaimTag+"could not issue claim to beneficiary: "+beneficiary)
|
||||
util.GetAppLogger().Error(ctx, err, createRedeemClaimTag+"could not issue claim to beneficiary: "+beneficiary)
|
||||
}
|
||||
util.SendUpdateRedeemClaim(goCtx, beneficiary, id, txID)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func (k msgServer) ReissueRDDLProposal(goCtx context.Context, msg *types.MsgReis
|
||||
|
||||
validatorIdentity, err := util.GetValidatorCometBFTIdentity(ctx, k.RootDir)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, reissueTag+errormsg.CouldNotGetValidatorIdentity+": "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, reissueTag+errormsg.CouldNotGetValidatorIdentity)
|
||||
return nil, err
|
||||
}
|
||||
if msg.Proposer != validatorIdentity {
|
||||
@ -46,7 +46,7 @@ func (k msgServer) ReissueRDDLProposal(goCtx context.Context, msg *types.MsgReis
|
||||
cmdArgs := strings.Split(msg.Command, " ")
|
||||
txID, err := clients.ReIssueAsset(goCtx, cmdArgs[1], cmdArgs[2])
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, reissueTag+"asset reissuance failed: "+err.Error())
|
||||
util.GetAppLogger().Error(ctx, err, reissueTag+"asset reissuance failed")
|
||||
}
|
||||
util.SendReissuanceResult(goCtx, msg.GetProposer(), txID, msg.GetBlockHeight())
|
||||
|
||||
|
@ -129,7 +129,7 @@ func (k Keeper) GetLastReissuance(ctx sdk.Context) (val types.Reissuance, found
|
||||
func (k Keeper) ComputeReissuanceValue(ctx sdk.Context, startHeight int64, endHeight int64) (reissuanceValue uint64, firstIncludedPop int64, lastIncludedPop int64, err error) {
|
||||
challenges, err := k.GetChallengeRange(ctx, startHeight, endHeight)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "unable to compute get challenges")
|
||||
util.GetAppLogger().Error(ctx, err, "unable to compute get challenges")
|
||||
return
|
||||
}
|
||||
var overallAmount uint64
|
||||
@ -141,7 +141,7 @@ func (k Keeper) ComputeReissuanceValue(ctx sdk.Context, startHeight int64, endHe
|
||||
popReissuanceString := GetReissuanceAsStringValue(obj.GetHeight(), k.GetParams(ctx).PopEpochs)
|
||||
amount, err := util.RDDLTokenStringToUint(popReissuanceString)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "unable to compute PoP reissuance value: "+popString)
|
||||
util.GetAppLogger().Error(ctx, err, "unable to compute PoP reissuance value: "+popString)
|
||||
continue
|
||||
}
|
||||
util.GetAppLogger().Debug(ctx, "PoP is part of the reissuance: "+popString)
|
||||
|
@ -122,7 +122,7 @@ func (k msgServer) handleNFTIssuance(goCtx context.Context, machine *types.Machi
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(ctx, "Machine NFT issuance failed: "+err.Error())
|
||||
logger.Error(ctx, err, "Machine NFT issuance failed")
|
||||
return err
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ func (k msgServer) sendInitialFundingTokensToMachine(goCtx context.Context, mach
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
machineAddress, err := sdk.AccAddressFromBech32(machineAddressString)
|
||||
if err != nil {
|
||||
util.GetAppLogger().Error(ctx, "error: for provided address "+machineAddress.String())
|
||||
util.GetAppLogger().Error(ctx, err, "for provided address "+machineAddress.String())
|
||||
return
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user