Skip to content

Commit

Permalink
Add gas refund on failed tx & fix gas consumed calculation logic
Browse files Browse the repository at this point in the history
  • Loading branch information
p0p3yee committed Sep 8, 2023
1 parent 3463d85 commit 8d3dd82
Showing 1 changed file with 88 additions and 149 deletions.
237 changes: 88 additions & 149 deletions x/pep/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,75 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw
// ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1
func (AppModule) ConsensusVersion() uint64 { return 1 }

func (am AppModule) handleGasConsumption(ctx sdk.Context, recipient sdk.AccAddress, gasUsed cosmosmath.Int, gasCharged *sdk.Coin) {
creatorAccount := am.accountKeeper.GetAccount(ctx, recipient)

if gasUsed.GT(gasCharged.Amount) {
deductFeeErr := ante.DeductFees(
am.bankKeeper,
ctx,
creatorAccount,
sdk.NewCoins(
sdk.NewCoin(
gasCharged.Denom,
gasUsed.Sub(gasCharged.Amount)),
),
)
if deductFeeErr != nil {
am.keeper.Logger(ctx).Error("deduct failed tx fee error")
am.keeper.Logger(ctx).Error(deductFeeErr.Error())
} else {
am.keeper.Logger(ctx).Info("failed tx fee deducted without error")
}
} else {
amount := gasCharged.Amount.Sub(gasUsed)
if amount.IsZero() {
am.keeper.Logger(ctx).Info("refund failed tx fee amount is zero, no need to refund...")
return
}
refundFeeErr := am.bankKeeper.SendCoinsFromModuleToAccount(
ctx,
types.ModuleName,
recipient,
sdk.NewCoins(sdk.NewCoin(gasCharged.Denom, amount)),
)
if refundFeeErr != nil {
am.keeper.Logger(ctx).Error("refund failed tx fee error")
am.keeper.Logger(ctx).Error(refundFeeErr.Error())
} else {
am.keeper.Logger(ctx).Info("failed tx fee refunded without error")
}
}
}

func (am AppModule) processFailedEncryptedTx(ctx sdk.Context, tx types.EncryptedTx, failReason string, startConsumedGas uint64) {
am.keeper.Logger(ctx).Error(fmt.Sprintf("failed to process encrypted tx: %s", failReason))
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, tx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(tx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, failReason),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(tx.Index, 10)),
),
)

creatorAddr, err := sdk.AccAddressFromBech32(tx.Creator)
if err != nil {
am.keeper.Logger(ctx).Error("error while trying to parse tx creator address when processing failed encrypted tx")
am.keeper.Logger(ctx).Error(err.Error())
return
}

var actualGasConsumed uint64 = 0
if ctx.BlockGasMeter().GasConsumed() > startConsumedGas {
actualGasConsumed = ctx.BlockGasMeter().GasConsumed() - startConsumedGas
}

am.handleGasConsumption(ctx, creatorAddr, cosmosmath.NewIntFromUint64(actualGasConsumed), tx.ChargedGas)
}

// BeginBlock contains the logic that is automatically triggered at the beginning of each block
func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {

strLastExecutedHeight := am.keeper.GetLastExecutedHeight(ctx)
lastExecutedHeight, err := strconv.ParseUint(strLastExecutedHeight, 10, 64)

Expand Down Expand Up @@ -270,83 +336,39 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
am.keeper.Logger(ctx).Info(skPoint.String())

for _, eachTx := range arr.EncryptedTx {

startConsumedGas := ctx.GasMeter().GasConsumed()
if currentNonce, found := am.keeper.GetPepNonce(ctx, eachTx.Creator); found && currentNonce.Nonce == math.MaxUint64 {
am.keeper.Logger(ctx).Error("Invalid PEP Nonce")
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, "Invalid pep nonce"),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, "invalid pep nonce", startConsumedGas)
continue
}

newExecutedNonce := am.keeper.IncreasePepNonce(ctx, eachTx.Creator)

creatorAddr, err := sdk.AccAddressFromBech32(eachTx.Creator)
if err != nil {
am.keeper.Logger(ctx).Error("Parse creator address error in BeginBlock")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, err.Error()),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error parsing creator address: %s", err.Error()), startConsumedGas)
continue
}

creatorAccount := am.accountKeeper.GetAccount(ctx, creatorAddr)

txBytes, err := hex.DecodeString(eachTx.Data)
if err != nil {
am.keeper.Logger(ctx).Error("Error decoding tx data to bytes")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, err.Error()),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error decoding tx data to bytes: %s", err.Error()), startConsumedGas)
continue
}

var decryptedTx bytes.Buffer
var txBuffer bytes.Buffer
_, err = txBuffer.Write(txBytes)
if err != nil {
am.keeper.Logger(ctx).Error("Error write byte to tx buffer")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, err.Error()),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error while writing bytes to tx buffer: %s", err.Error()), startConsumedGas)
continue
}

err = enc.Decrypt(publicKeyPoint, skPoint, &decryptedTx, &txBuffer)
if err != nil {
am.keeper.Logger(ctx).Error("Error decrypting tx data")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, err.Error()),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error decrypting tx data: %s", err.Error()), startConsumedGas)
continue
}

Expand All @@ -360,16 +382,7 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {

txDecoderTx, err = am.txConfig.TxJSONDecoder()(decryptedTx.Bytes())
if err != nil {
am.keeper.Logger(ctx).Error("JSON Decoding Tx error in BeginBlock")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, "Unable to decode tx data to Cosmos Tx"),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error trying to json decoding tx: %s", err.Error()), startConsumedGas)
continue
} else {
am.keeper.Logger(ctx).Error("TX Successfully Decode with JSON Decoder")
Expand All @@ -378,87 +391,37 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {

wrappedTx, err := am.txConfig.WrapTxBuilder(txDecoderTx)
if err != nil {
am.keeper.Logger(ctx).Error("Error in wrapping tx to TxBuilder")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, "Unable to wrap tx to TxBuilder"),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error when trying to wrap decoded tx to tx builder: %s", err.Error()), startConsumedGas)
continue
}

sigs, err := wrappedTx.GetTx().GetSignaturesV2()
if err != nil {
am.keeper.Logger(ctx).Error("Error in getting tx signature")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, "Unable to get tx signature"),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error getting decoded tx signatures: %s", err.Error()), startConsumedGas)
continue
}

if len(sigs) != 1 {
am.keeper.Logger(ctx).Error("Number of signatures provided is more than 1")
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, "Number of signatures provided is more than 1"),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, "number of provided signatures is more than one", startConsumedGas)
continue
}

txMsgs := wrappedTx.GetTx().GetMsgs()

if len(sigs) != len(txMsgs) {
am.keeper.Logger(ctx).Error("Number of signature is not equals to number of messages")
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, "Number of signature is not equals to number of messages"),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, "number of provided signatures is not equals to number of tx messages", startConsumedGas)
continue
}

if !sigs[0].PubKey.Equals(creatorAccount.GetPubKey()) {
am.keeper.Logger(ctx).Error("Signer is not sender")
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, "signer public key does not match sender public key"),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, "tx signer is not tx sender", startConsumedGas)
continue
}

expectingNonce := newExecutedNonce - 1

if sigs[0].Sequence < expectingNonce {
am.keeper.Logger(ctx).Error(fmt.Sprintf("Incorrect Nonce sequence, Provided: %d, Expecting: %d", sigs[0].Sequence, expectingNonce))
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, fmt.Sprintf("Incorrect nonce sequence, provided: %d, expecting: %d", sigs[0].Sequence, expectingNonce)),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("Incorrect Nonce sequence, Provided: %d, Expecting: %d", sigs[0].Sequence, expectingNonce), startConsumedGas)
continue
}

Expand Down Expand Up @@ -488,36 +451,19 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
)

if err != nil {
am.keeper.Logger(ctx).Error("Invalid Signature in BeginBlock")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, "Invalid signature"),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error when verifying signature: invalid signature: %s", err.Error()), startConsumedGas)
continue
}

decryptionConsumed := ctx.GasMeter().GasConsumed() - startConsumedGas
simCheckGas, _, err := am.simCheck(am.txConfig.TxEncoder(), txDecoderTx)
// We are using SimCheck() to only estimate gas for the underlying transaction
// Since user is supposed to sign the underlying transaction with Pep Nonce,
// is expected that we gets 'account sequence mismatch' error
// however, the underlying tx is not expected to get other errors
// such as insufficient fee, out of gas etc...
if err != nil && !strings.Contains(err.Error(), "account sequence mismatch") {
am.keeper.Logger(ctx).Error("Check Tx Fails")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, err.Error()),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error while performing check tx: %s", err.Error()), startConsumedGas)
continue
}

Expand All @@ -528,7 +474,9 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
// therefore, we are not charging for the tx execution
if !txFee.Empty() {
gasProvided := cosmosmath.NewIntFromUint64(wrappedTx.GetTx().GetGas())
gasUsedInBig := cosmosmath.NewIntFromUint64(simCheckGas.GasUsed)
// Underlying tx consumed gas + gas consumed on decrypting & decoding tx
am.keeper.Logger(ctx).Info(fmt.Sprintf("Underlying tx consumed: %d, decryption consumed: %d", simCheckGas.GasUsed, decryptionConsumed))
gasUsedInBig := cosmosmath.NewIntFromUint64(simCheckGas.GasUsed).Add(cosmosmath.NewIntFromUint64(decryptionConsumed))
newCoins := make([]sdk.Coin, len(txFee))
refundDenom := txFee[0].Denom
refundAmount := cosmosmath.NewIntFromUint64(0)
Expand Down Expand Up @@ -585,20 +533,11 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
handler := am.msgServiceRouter.Handler(txMsgs[0])
_, err = handler(ctx, txMsgs[0])
if err != nil {
am.keeper.Logger(ctx).Error("Handle Tx Msg Error")
am.keeper.Logger(ctx).Error(err.Error())
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxRevertedEventType,
sdk.NewAttribute(types.EncryptedTxRevertedEventCreator, eachTx.Creator),
sdk.NewAttribute(types.EncryptedTxRevertedEventHeight, strconv.FormatUint(eachTx.TargetHeight, 10)),
sdk.NewAttribute(types.EncryptedTxRevertedEventReason, err.Error()),
sdk.NewAttribute(types.EncryptedTxRevertedEventIndex, strconv.FormatUint(eachTx.Index, 10)),
),
)
am.processFailedEncryptedTx(ctx, eachTx, fmt.Sprintf("error when handling tx message: %s", err.Error()), startConsumedGas)
continue
}

am.keeper.Logger(ctx).Info("!Executed successfully!")
am.keeper.Logger(ctx).Info("! Encrypted Tx Decrypted & Decoded & Executed successfully !")

ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EncryptedTxExecutedEventType,
Expand Down

0 comments on commit 8d3dd82

Please sign in to comment.