Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relayed v3 with multiple transactions #6118

Open
wants to merge 13 commits into
base: feat/relayedv3
Choose a base branch
from

Conversation

sstanculeanu
Copy link
Contributor

@sstanculeanu sstanculeanu commented Apr 15, 2024

Reasoning behind the pull request

  • Relayed v3 should be able to support multiple inner transactions

Proposed changes

  • Added support for multiple inner transactions for relayed v3(senders should be in the same shard with the relayer)
  • Added proper fix for the relayed move balance fee
  • Added integration test with chain simulator for relayed v3

Testing procedure

  • Will be tested with feat branch

Pre-requisites

Based on the Contributing Guidelines the PR author and the reviewers must check the following requirements are met:

  • was the PR targeted to the correct branch?
  • if this is a larger feature that probably needs more than one PR, is there a feat branch created?
  • if this is a feat branch merging, do all satellite projects have a proper tag inside go.mod?

@sstanculeanu sstanculeanu marked this pull request as ready for review April 30, 2024 11:14
sstanculeanu and others added 2 commits April 30, 2024 17:00
…_txs

Extra tests and coverage for relayed v3 with multiple inner transactions
Copy link

codecov bot commented Apr 30, 2024

Codecov Report

Attention: Patch coverage is 80.65934% with 88 lines in your changes are missing coverage. Please review.

Project coverage is 78.78%. Comparing base (d31d68b) to head (e3f3e38).

❗ Current head e3f3e38 differs from pull request most recent head 2bb0754. Consider uploading reports for the commit 2bb0754 to get more accurate results

Files Patch % Lines
api/groups/transactionGroup.go 61.40% 43 Missing and 1 partial ⚠️
process/transaction/shardProcess.go 73.18% 27 Missing and 10 partials ⚠️
...ode/chainSimulator/components/processComponents.go 0.00% 3 Missing ⚠️
factory/processing/processComponents.go 85.71% 1 Missing and 1 partial ⚠️
factory/processing/processComponentsHandler.go 75.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@                Coverage Diff                 @@
##           feat/relayedv3    #6118      +/-   ##
==================================================
+ Coverage           78.77%   78.78%   +0.01%     
==================================================
  Files                 752      753       +1     
  Lines               98137    98341     +204     
==================================================
+ Hits                77308    77481     +173     
- Misses              15604    15626      +22     
- Partials             5225     5234       +9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@sasurobert sasurobert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review still in works.

@@ -14,7 +14,7 @@ import (
)

// CreateGeneralSetupForRelayTxTest will create the general setup for relayed transactions
func CreateGeneralSetupForRelayTxTest() ([]*integrationTests.TestProcessorNode, []int, []*integrationTests.TestWalletAccount, *integrationTests.TestWalletAccount) {
func CreateGeneralSetupForRelayTxTest(relayedV3Test bool) ([]*integrationTests.TestProcessorNode, []int, []*integrationTests.TestWalletAccount, *integrationTests.TestWalletAccount) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a good practice. Make a separate function for CreateGeneralSetupForRelayedV3. which calls the common code.

This can be done without the bool.

txFee = txProc.economicsFee.ComputeTxFee(tx)
moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(tx)
gasToUse := tx.GetGasLimit() - moveBalanceGasLimit
moveBalanceUserFee := txProc.economicsFee.ComputeMoveBalanceFee(tx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will further increase the cost of relayed txs as they are now.

It can stay, but for relayed v1 and v2 the txData is computed twice. Once in the relayer and once when processing the underlying tx.

@@ -298,7 +304,14 @@ func (txProc *txProcessor) executingFailedTransaction(
return nil
}

txFee := txProc.economicsFee.ComputeTxFee(tx)
txFee := txProc.economicsFee.ComputeFeeForProcessing(tx, tx.GasLimit)
if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedMoveBalanceFlag) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move the common code and use a common function. This is the exactly same lines of codes as in baseProcess.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move more code into that new function. activation flags as well.

@@ -391,7 +404,11 @@ func (txProc *txProcessor) processTxFee(
if isUserTxOfRelayed {
totalCost := txProc.economicsFee.ComputeFeeForProcessing(tx, tx.GasLimit)
if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedMoveBalanceFlag) {
totalCost = txProc.economicsFee.ComputeTxFee(tx)
moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(tx)
gasToUse := tx.GetGasLimit() - moveBalanceGasLimit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicated code again.

}

txProc.txFeeHandler.ProcessTransactionFee(relayerFee, big.NewInt(0), txHash)
}

return txHash, nil
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

txHash was returned in order to not calculate that too many times. Actually we should create a task where we calculate txHash only once in the lifetime of a tx.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created new task MX-15423


newInnerTx, _, err := tg.createTransaction(innerTx, nil)
if err != nil {
// if one of the inner txs is invalid, break the loop and move to the next transaction received
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would you send the tx further if the inner tx is wrong? Why not error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, updated

@@ -176,6 +176,16 @@ func (txProc *baseTxProcessor) checkTxValues(
return nil
}

func (txProc *baseTxProcessor) computeTxFeeAfterMoveBalanceFix(tx *transaction.Transaction) *big.Int {
moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(tx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think flag check and old computation can be moved inside this new function as well. It will look better. As in all the cases you have compute old way if flag is not activated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@@ -298,7 +304,14 @@ func (txProc *txProcessor) executingFailedTransaction(
return nil
}

txFee := txProc.economicsFee.ComputeTxFee(tx)
txFee := txProc.economicsFee.ComputeFeeForProcessing(tx, tx.GasLimit)
if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedMoveBalanceFlag) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move more code into that new function. activation flags as well.


allUserTxsSucceeded := len(executedUserTxs) == len(innerTxs) && innerTxErr == nil && innerTxRetCode == vmcommon.Ok
if !allUserTxsSucceeded {
log.Debug("failed to execute all inner transactions", "total", len(innerTxs), "executed transactions", len(executedUserTxs))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do not need information like this in log.Debug - maybe in trace.

) (*smartContractResult.SmartContractResult, error) {
scrValue := tx.Value
if isRevertSCR {
scrValue = big.NewInt(0).Neg(tx.Value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have never created SCR with negative value. Why do you need this ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed not needed, I forgot to remove it after the last discussion

for acnt, prevBalance := range snapshot {
currentBalance := acnt.GetBalance()
diff := big.NewInt(0).Sub(currentBalance, prevBalance)
err := acnt.SubFromBalance(diff)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not good.

user accounts.RevertToSnapshot. it will resolve the issue. No need to manually do this.


uniqueSendersMap := txProc.relayedTxV3Processor.GetUniqueSendersRequiredFeesMap(tx.InnerTransactions)
uniqueSendersSlice := mapToSlice(uniqueSendersMap)
sendersBalancesSnapshot := make(map[state.UserAccountHandler]*big.Int, len(uniqueSendersMap))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you do not need to get the accounts balance like this - no need. revert should be done with accounts.RevertToSnapshot function. after taking the fees out from the relayer, you make a snapshot. accounts.Snapshot() -> will give you an ID. and in case of errors you return to that and all is good.

if userTx.GasLimit != remainingGasLimit {
return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxV3GasLimitMismatch)

if check.IfNil(acntSnd) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have discussed that acntSnd has to be in the same shard. If it is not, you should return error. it is a failed inner tx.

At the interceptor level we can simply stop these kind of txs. there is no reason at all to accept them to the network.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added return error as suggested

this check is already done at interceptor level, so this if should never ever be true

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is true. But it is ok to check here as well and return error.

relayerMoveBalanceFee := proc.economicsFee.ComputeMoveBalanceFee(tx)
uniqueSenders := proc.GetUniqueSendersRequiredFeesMap(tx.InnerTransactions)

relayerFee := big.NewInt(0).Mul(relayerMoveBalanceFee, big.NewInt(int64(len(uniqueSenders))))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think this should be by unique senders. every inner tx is a separate tx with separate move balance fee. independent if the sender is the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

considered this a cost optimization, although it makes more sense to keep each tx independent including the move balance fee.. updated as suggested


const minTransactionsAllowed = 1

type ArgRelayedTxV3Processor struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comment

var innerTxErr error
executedUserTxs := make([]*transaction.Transaction, 0)
for _, innerTx := range innerTxs {
innerTxRetCode, innerTxErr = txProc.finishExecutionOfInnerTx(tx, innerTx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a better name is processInnerTx

}

return txProc.finishExecutionOfRelayedTx(relayerAcnt, acntDst, tx, userTx)
txFee := txProc.computeTxFee(innerTx)
err = txProc.addFeeAndValueToDest(acntSnd, tx, txFee)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is tx.value == big.NewInt(0) ? I think it would be better to have a localTX here, where we have value at 0 clearly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do have an early check for the tx value to be 0, but the suggestion from the next comment with the specific parameter should clarify.. applied it

}

return txProc.finishExecutionOfRelayedTx(relayerAcnt, acntDst, tx, userTx)
txFee := txProc.computeTxFee(innerTx)
err = txProc.addFeeAndValueToDest(acntSnd, tx, txFee)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can change the addFeeAndValueToDest function and have 2 bigInts to be received there. it is much more clear. and here you could have big.newint(0), txFee.

}

return txProc.finishExecutionOfRelayedTx(relayerAcnt, acntDst, tx, userTx)
txFee := txProc.computeTxFee(innerTx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need another check - inveriant check Sum of TXFee == ComputeRelayedFee. you can sum all the txFees you have here, and at the end of processing to check it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added check as discussed 🙏

for _, innerTx := range innerTxs {
innerTxRetCode, innerTxErr = txProc.finishExecutionOfInnerTx(tx, innerTx)
if innerTxErr != nil || innerTxRetCode != vmcommon.Ok {
continue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should create a logEvent for failed innerTX, for those cases which is not caught by processUserTX - I think when the code reaches processUserTX a logEvent is done at least for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's true, when the code reaches processUserTX it generates a logEvent.. will add one in case the processing fails before that point

continue
}

executedUserTxs = append(executedUserTxs, innerTx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have to check whether an additional logEvent would be needed here - I think it is better to have that innerTX was executed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do have a SCR. do we also a logEvent?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants