Skip to content

Commit

Permalink
Merge pull request #5741 from multiversx/feat/relayedv3
Browse files Browse the repository at this point in the history
Feat/RelayedV3
  • Loading branch information
sstanculeanu authored Jul 19, 2024
2 parents a6d3565 + 599170a commit 215571b
Show file tree
Hide file tree
Showing 162 changed files with 6,364 additions and 2,393 deletions.
3 changes: 3 additions & 0 deletions api/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,6 @@ var ErrGetWaitingManagedKeys = errors.New("error getting the waiting managed key

// ErrGetWaitingEpochsLeftForPublicKey signals that an error occurred while getting the waiting epochs left for public key
var ErrGetWaitingEpochsLeftForPublicKey = errors.New("error getting the waiting epochs left for public key")

// ErrRecursiveRelayedTxIsNotAllowed signals that recursive relayed tx is not allowed
var ErrRecursiveRelayedTxIsNotAllowed = errors.New("recursive relayed tx is not allowed")
193 changes: 115 additions & 78 deletions api/groups/transactionGroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,27 +182,23 @@ func (tg *transactionGroup) simulateTransaction(c *gin.Context) {
return
}

txArgs := &external.ArgsCreateTransaction{
Nonce: ftx.Nonce,
Value: ftx.Value,
Receiver: ftx.Receiver,
ReceiverUsername: ftx.ReceiverUsername,
Sender: ftx.Sender,
SenderUsername: ftx.SenderUsername,
GasPrice: ftx.GasPrice,
GasLimit: ftx.GasLimit,
DataField: ftx.Data,
SignatureHex: ftx.Signature,
ChainID: ftx.ChainID,
Version: ftx.Version,
Options: ftx.Options,
Guardian: ftx.GuardianAddr,
GuardianSigHex: ftx.GuardianSignature,
innerTxs, err := tg.extractInnerTransactions(ftx.InnerTransactions)
if err != nil {
c.JSON(
http.StatusBadRequest,
shared.GenericAPIResponse{
Data: nil,
Error: fmt.Sprintf("%s: %s", errors.ErrTxGenerationFailed.Error(), err.Error()),
Code: shared.ReturnCodeRequestError,
},
)
return
}
start := time.Now()
tx, txHash, err := tg.getFacade().CreateTransaction(txArgs)
logging.LogAPIActionDurationIfNeeded(start, "API call: CreateTransaction")

if len(innerTxs) == 0 {
innerTxs = nil
}
tx, txHash, err := tg.createTransaction(&ftx, innerTxs)
if err != nil {
c.JSON(
http.StatusBadRequest,
Expand All @@ -215,7 +211,7 @@ func (tg *transactionGroup) simulateTransaction(c *gin.Context) {
return
}

start = time.Now()
start := time.Now()
err = tg.getFacade().ValidateTransactionForSimulation(tx, checkSignature)
logging.LogAPIActionDurationIfNeeded(start, "API call: ValidateTransactionForSimulation")
if err != nil {
Expand Down Expand Up @@ -272,26 +268,23 @@ func (tg *transactionGroup) sendTransaction(c *gin.Context) {
return
}

txArgs := &external.ArgsCreateTransaction{
Nonce: ftx.Nonce,
Value: ftx.Value,
Receiver: ftx.Receiver,
ReceiverUsername: ftx.ReceiverUsername,
Sender: ftx.Sender,
SenderUsername: ftx.SenderUsername,
GasPrice: ftx.GasPrice,
GasLimit: ftx.GasLimit,
DataField: ftx.Data,
SignatureHex: ftx.Signature,
ChainID: ftx.ChainID,
Version: ftx.Version,
Options: ftx.Options,
Guardian: ftx.GuardianAddr,
GuardianSigHex: ftx.GuardianSignature,
innerTxs, err := tg.extractInnerTransactions(ftx.InnerTransactions)
if err != nil {
c.JSON(
http.StatusBadRequest,
shared.GenericAPIResponse{
Data: nil,
Error: fmt.Sprintf("%s: %s", errors.ErrTxGenerationFailed.Error(), err.Error()),
Code: shared.ReturnCodeRequestError,
},
)
return
}
start := time.Now()
tx, txHash, err := tg.getFacade().CreateTransaction(txArgs)
logging.LogAPIActionDurationIfNeeded(start, "API call: CreateTransaction")

if len(innerTxs) == 0 {
innerTxs = nil
}
tx, txHash, err := tg.createTransaction(&ftx, innerTxs)
if err != nil {
c.JSON(
http.StatusBadRequest,
Expand All @@ -304,7 +297,7 @@ func (tg *transactionGroup) sendTransaction(c *gin.Context) {
return
}

start = time.Now()
start := time.Now()
err = tg.getFacade().ValidateTransaction(tx)
logging.LogAPIActionDurationIfNeeded(start, "API call: ValidateTransaction")
if err != nil {
Expand Down Expand Up @@ -370,25 +363,23 @@ func (tg *transactionGroup) sendMultipleTransactions(c *gin.Context) {
var start time.Time
txsHashes := make(map[int]string)
for idx, receivedTx := range ftxs {
txArgs := &external.ArgsCreateTransaction{
Nonce: receivedTx.Nonce,
Value: receivedTx.Value,
Receiver: receivedTx.Receiver,
ReceiverUsername: receivedTx.ReceiverUsername,
Sender: receivedTx.Sender,
SenderUsername: receivedTx.SenderUsername,
GasPrice: receivedTx.GasPrice,
GasLimit: receivedTx.GasLimit,
DataField: receivedTx.Data,
SignatureHex: receivedTx.Signature,
ChainID: receivedTx.ChainID,
Version: receivedTx.Version,
Options: receivedTx.Options,
Guardian: receivedTx.GuardianAddr,
GuardianSigHex: receivedTx.GuardianSignature,
innerTxs, errExtractInnerTransactions := tg.extractInnerTransactions(receivedTx.InnerTransactions)
if errExtractInnerTransactions != nil {
c.JSON(
http.StatusBadRequest,
shared.GenericAPIResponse{
Data: nil,
Error: fmt.Sprintf("%s: %s", errors.ErrTxGenerationFailed.Error(), err.Error()),
Code: shared.ReturnCodeInternalError,
},
)
return
}

if len(innerTxs) == 0 {
innerTxs = nil
}
tx, txHash, err = tg.getFacade().CreateTransaction(txArgs)
logging.LogAPIActionDurationIfNeeded(start, "API call: CreateTransaction")
tx, txHash, err = tg.createTransaction(&receivedTx, innerTxs)
if err != nil {
continue
}
Expand Down Expand Up @@ -499,26 +490,23 @@ func (tg *transactionGroup) computeTransactionGasLimit(c *gin.Context) {
return
}

txArgs := &external.ArgsCreateTransaction{
Nonce: ftx.Nonce,
Value: ftx.Value,
Receiver: ftx.Receiver,
ReceiverUsername: ftx.ReceiverUsername,
Sender: ftx.Sender,
SenderUsername: ftx.SenderUsername,
GasPrice: ftx.GasPrice,
GasLimit: ftx.GasLimit,
DataField: ftx.Data,
SignatureHex: ftx.Signature,
ChainID: ftx.ChainID,
Version: ftx.Version,
Options: ftx.Options,
Guardian: ftx.GuardianAddr,
GuardianSigHex: ftx.GuardianSignature,
innerTxs, errExtractInnerTransactions := tg.extractInnerTransactions(ftx.InnerTransactions)
if errExtractInnerTransactions != nil {
c.JSON(
http.StatusBadRequest,
shared.GenericAPIResponse{
Data: nil,
Error: fmt.Sprintf("%s: %s", errors.ErrTxGenerationFailed.Error(), errExtractInnerTransactions.Error()),
Code: shared.ReturnCodeInternalError,
},
)
return
}
start := time.Now()
tx, _, err := tg.getFacade().CreateTransaction(txArgs)
logging.LogAPIActionDurationIfNeeded(start, "API call: CreateTransaction")

if len(innerTxs) == 0 {
innerTxs = nil
}
tx, _, err := tg.createTransaction(&ftx, innerTxs)
if err != nil {
c.JSON(
http.StatusInternalServerError,
Expand All @@ -531,7 +519,7 @@ func (tg *transactionGroup) computeTransactionGasLimit(c *gin.Context) {
return
}

start = time.Now()
start := time.Now()
cost, err := tg.getFacade().ComputeTransactionGasLimit(tx)
logging.LogAPIActionDurationIfNeeded(start, "API call: ComputeTransactionGasLimit")
if err != nil {
Expand Down Expand Up @@ -728,6 +716,33 @@ func (tg *transactionGroup) getTransactionsPoolNonceGapsForSender(sender string,
)
}

func (tg *transactionGroup) createTransaction(receivedTx *transaction.FrontendTransaction, innerTxs []*transaction.Transaction) (*transaction.Transaction, []byte, error) {
txArgs := &external.ArgsCreateTransaction{
Nonce: receivedTx.Nonce,
Value: receivedTx.Value,
Receiver: receivedTx.Receiver,
ReceiverUsername: receivedTx.ReceiverUsername,
Sender: receivedTx.Sender,
SenderUsername: receivedTx.SenderUsername,
GasPrice: receivedTx.GasPrice,
GasLimit: receivedTx.GasLimit,
DataField: receivedTx.Data,
SignatureHex: receivedTx.Signature,
ChainID: receivedTx.ChainID,
Version: receivedTx.Version,
Options: receivedTx.Options,
Guardian: receivedTx.GuardianAddr,
GuardianSigHex: receivedTx.GuardianSignature,
Relayer: receivedTx.Relayer,
InnerTransactions: innerTxs,
}
start := time.Now()
tx, txHash, err := tg.getFacade().CreateTransaction(txArgs)
logging.LogAPIActionDurationIfNeeded(start, "API call: CreateTransaction")

return tx, txHash, err
}

func validateQuery(sender, fields string, lastNonce, nonceGaps bool) error {
if fields != "" && lastNonce {
return errors.ErrFetchingLatestNonceCannotIncludeFields
Expand Down Expand Up @@ -825,6 +840,28 @@ func (tg *transactionGroup) getFacade() transactionFacadeHandler {
return tg.facade
}

func (tg *transactionGroup) extractInnerTransactions(
innerTransactions []*transaction.FrontendTransaction,
) ([]*transaction.Transaction, error) {
innerTxs := make([]*transaction.Transaction, 0, len(innerTransactions))
if len(innerTransactions) != 0 {
for _, innerTx := range innerTransactions {
if len(innerTx.InnerTransactions) != 0 {
return innerTxs, errors.ErrRecursiveRelayedTxIsNotAllowed
}

newInnerTx, _, err := tg.createTransaction(innerTx, nil)
if err != nil {
return innerTxs, err
}

innerTxs = append(innerTxs, newInnerTx)
}
}

return innerTxs, nil
}

// UpdateFacade will update the facade
func (tg *transactionGroup) UpdateFacade(newFacade interface{}) error {
if newFacade == nil {
Expand Down
62 changes: 62 additions & 0 deletions api/groups/transactionGroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ func TestTransactionGroup_sendTransaction(t *testing.T) {
expectedErr,
)
})
t.Run("recursive relayed v3 should error", testRecursiveRelayedV3("/transaction/send"))
t.Run("should work", func(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -520,6 +521,7 @@ func TestTransactionGroup_computeTransactionGasLimit(t *testing.T) {
expectedErr,
)
})
t.Run("recursive relayed v3 should error", testRecursiveRelayedV3("/transaction/cost"))
t.Run("should work", func(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -640,6 +642,7 @@ func TestTransactionGroup_simulateTransaction(t *testing.T) {
expectedErr,
)
})
t.Run("recursive relayed v3 should error", testRecursiveRelayedV3("/transaction/simulate"))
t.Run("should work", func(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -1127,3 +1130,62 @@ func getTransactionRoutesConfig() config.ApiRoutesConfig {
},
}
}

func testRecursiveRelayedV3(url string) func(t *testing.T) {
return func(t *testing.T) {
t.Parallel()

facade := &mock.FacadeStub{
CreateTransactionHandler: func(txArgs *external.ArgsCreateTransaction) (*dataTx.Transaction, []byte, error) {
txHash, _ := hex.DecodeString(hexTxHash)
return nil, txHash, nil
},
SendBulkTransactionsHandler: func(txs []*dataTx.Transaction) (u uint64, err error) {
return 1, nil
},
ValidateTransactionHandler: func(tx *dataTx.Transaction) error {
return nil
},
}

userTx1 := fmt.Sprintf(`{"nonce": %d, "sender":"%s", "receiver":"%s", "value":"%s", "signature":"%s"}`,
nonce,
sender,
receiver,
value,
signature,
)
userTx2 := fmt.Sprintf(`{"nonce": %d, "sender":"%s", "receiver":"%s", "value":"%s", "signature":"%s", "innerTransactions":[%s]}`,
nonce,
sender,
receiver,
value,
signature,
userTx1,
)
tx := fmt.Sprintf(`{"nonce": %d, "sender":"%s", "receiver":"%s", "value":"%s", "signature":"%s", "innerTransactions":[%s]}`,
nonce,
sender,
receiver,
value,
signature,
userTx2,
)

transactionGroup, err := groups.NewTransactionGroup(facade)
require.NoError(t, err)

ws := startWebServer(transactionGroup, "transaction", getTransactionRoutesConfig())

req, _ := http.NewRequest("POST", url, bytes.NewBuffer([]byte(tx)))
resp := httptest.NewRecorder()
ws.ServeHTTP(resp, req)

txResp := shared.GenericAPIResponse{}
loadResponse(resp.Body, &txResp)

assert.Equal(t, http.StatusBadRequest, resp.Code)
assert.True(t, strings.Contains(txResp.Error, apiErrors.ErrRecursiveRelayedTxIsNotAllowed.Error()))
assert.Empty(t, txResp.Data)
}
}
3 changes: 3 additions & 0 deletions cmd/node/config/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -946,3 +946,6 @@
# MaxRoundsOfInactivityAccepted defines the number of rounds missed by a main or higher level backup machine before
# the current machine will take over and propose/sign blocks. Used in both single-key and multi-key modes.
MaxRoundsOfInactivityAccepted = 3

[RelayedTransactionConfig]
MaxTransactionsAllowed = 50
6 changes: 6 additions & 0 deletions cmd/node/config/enableEpochs.toml
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,12 @@
# UnjailCleanupEnableEpoch represents the epoch when the cleanup of the unjailed nodes is enabled
UnJailCleanupEnableEpoch = 4

# RelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions V3 will be enabled
RelayedTransactionsV3EnableEpoch = 7

# FixRelayedBaseCostEnableEpoch represents the epoch when the fix for relayed base cost will be enabled
FixRelayedBaseCostEnableEpoch = 7

# BLSMultiSignerEnableEpoch represents the activation epoch for different types of BLS multi-signers
BLSMultiSignerEnableEpoch = [
{ EnableEpoch = 0, Type = "no-KOSK" },
Expand Down
8 changes: 8 additions & 0 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,12 @@ const (
// MetricRelayedTransactionsV2EnableEpoch represents the epoch when the relayed transactions v2 is enabled
MetricRelayedTransactionsV2EnableEpoch = "erd_relayed_transactions_v2_enable_epoch"

// MetricRelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions v3 is enabled
MetricRelayedTransactionsV3EnableEpoch = "erd_relayed_transactions_v3_enable_epoch"

// MetricFixRelayedBaseCostEnableEpoch represents the epoch when the fix for relayed base cost is enabled
MetricFixRelayedBaseCostEnableEpoch = "erd_fix_relayed_base_cost_enable_epoch"

// MetricUnbondTokensV2EnableEpoch represents the epoch when the unbond tokens v2 is applied
MetricUnbondTokensV2EnableEpoch = "erd_unbond_tokens_v2_enable_epoch"

Expand Down Expand Up @@ -1221,5 +1227,7 @@ const (
EGLDInESDTMultiTransferFlag core.EnableEpochFlag = "EGLDInESDTMultiTransferFlag"
CryptoOpcodesV2Flag core.EnableEpochFlag = "CryptoOpcodesV2Flag"
UnJailCleanupFlag core.EnableEpochFlag = "UnJailCleanupFlag"
RelayedTransactionsV3Flag core.EnableEpochFlag = "RelayedTransactionsV3Flag"
FixRelayedBaseCostFlag core.EnableEpochFlag = "FixRelayedBaseCostFlag"
// all new flags must be added to createAllFlagsMap method, as part of enableEpochsHandler allFlagsDefined
)
Loading

0 comments on commit 215571b

Please sign in to comment.